4

Since I had trouble googling this question I thought I'd post it here.

I'm just interested in the logic behind it or wether it's just the creators' preference to use ++ instead. I mean, using a typeclass for strings that concatenates two strings (or rather lists) with + does not seem too hard to imagine.

Edit: I should add, that in Haskell one has to suspect reasons behind it, because + and ++ are functions defined in typeclasses, whereas in java the usage of + for string concatenation is just part of the language's syntax and therefor subject only to the creators preference/opinion. (The answers so far suggest that I was right about my suspicion.)

Also haskell comes from a mathematical background and is deeply influenced by mathematical syntax, so there might be deeper reasons than just preference/opinion.

John Smith
  • 2,282
  • 1
  • 14
  • 22
  • 2
    Because `(+)` is just a function, and it is part of the `Num` typeclass. A string is *not* a number. – Willem Van Onsem Apr 14 '18 at 09:03
  • 3
    My guess on why `(+)` is defined in the `Num` typeclass and not as part of the `Monoid` typeclass (and have `Num` as a subclass of `Monoid`) is probably historic, since `Monoid` was introduced after `Num`, and also because there's 2 `Monoid` instances for numeric types. – Mor A. Apr 14 '18 at 09:06
  • 2
    Even then, why should `(+)` be the *monoid* operator, as opposed to the operator for some other group-like structure? `Num` is far from being part of a mathematically rigourous hierarchy, and in the end `(+)` is used for the things most likely to be similar to integer addition. List concatenation (being a familiar operation but lacking commutativity or an inverse) is far enough from that definition to warrant its own operator. – chepner Apr 14 '18 at 13:27
  • You might also look at [this chart](https://en.wikipedia.org/wiki/Magma_(algebra)#Classification_by_properties) and ask yourself which object has the best claim on using `(+)` for its operation. (`Num` isn't so much one of those objects as it is "a bunch of stuff that is pretty much the same for integers, rationals, reals, and complex numbers".) – chepner Apr 14 '18 at 13:45
  • 1
    Closely related: [*Why is the \`mappend\` infix alias \`<>\` instead of \`+\`?*](https://stackoverflow.com/q/23794894/2751851) – duplode Apr 14 '18 at 14:39
  • 3
    Haskell predates the ridiculous popularity of `+` for string concatenation. Concatenating strings is nothing like addition, so using different operators is sensible. – augustss Apr 15 '18 at 08:16
  • 2
    I do not have the feeling that this is a primary opinion base question (or at least the answers are really technically justified) , and so I do not thing this question shoud be put on hold – sandwood Apr 18 '18 at 15:11

2 Answers2

16

typeclass for strings that concatenates two strings

Such a typeclass exists, although the operator isn't +, but <>:

Prelude> :m +Data.Monoid
Prelude Data.Monoid> "foo" <> "bar"
"foobar"

While ++ concatenates lists, the <> operator is more general, since it combines any two values of a given Monoid instance.

As other people have pointed out, + is reserved for Num instances. Why isn't the Monoid binary operator called +, then? Because addition is only one of infinitely many monoids; multiplication is another:

Prelude Data.Monoid> Sum 2 <> Sum 3
Sum {getSum = 5}
Prelude Data.Monoid> Product 2 <> Product 3
Product {getProduct = 6}

Choosing something like <> as 'the' monoidal operator is preferred exactly because it carries little semantic baggage.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
3

Long story short, it would cause type troubles.

(+) is part of the Num typeclass:

class  Num a  where
    (+), (-), (*)       :: a -> a -> a
    negate              :: a -> a
    abs                 :: a -> a
    signum              :: a -> a
    fromInteger         :: Integer -> a

    x - y               = x + negate y
    negate x            = 0 - x

And (++) :: [a] -> [a] -> [a].

It's easy to see the first problem: if we wanted (+) to work on list, we would have to implement (*), negate, abs, signum, and fromInteger for lists as well. Which is spurious.

If we decided to seperate (+) from the typeclass, and make a new typeclass, maybe called Plussable for (+), there would be too many typeclasses to keep track of, and simple expressions like 1 + 2*(2-1) would no longer be of type Num a => a, it would be of type (Plussable a, Timesable a, Minusable a) => a, and so on for each operation. It would be far too complicated.

AJF
  • 11,767
  • 2
  • 37
  • 64
  • 2
    That last argument is not per se correct. We could make a `Class (Plussable a, Timesable a) => Num a where ...`, to group typeclasses together. – Willem Van Onsem Apr 14 '18 at 09:05
  • That's true, but we would have to write the type explicitly rather than having it inferred as it currently is. Or compiler magic. – AJF Apr 14 '18 at 09:06
  • @WillemVanOnsem You could declare it with that type, but it wouldn't be inferred anymore – Cubic Apr 14 '18 at 09:06
  • 2
    You can, in fact, express `1 + 2*(2-1)` using a single type class, but it does, indeed, require much verbosity: `Sum 1 <> (Sum $ getProduct $ Product 2 <> (Product $ getSum $ Sum 2 <> Sum (-1)))`. The type class in question is `Monoid`. – Mark Seemann Apr 14 '18 at 09:23
  • 1
    @MarkSeemann A single class, *maybe*... but not a single constraint. – Daniel Wagner Apr 14 '18 at 11:45
  • 2
    @MarkSeemann actually, since `Sum a` and `Product a` are both instances of `Num`, we can reduce your expression to `getSum $ 1 <> (getProduct $ 2 <> (getSum $ 2 <> (-1)))`. This however relies on the fact that integral numerals are polymorphic with type `Num a => a`. – Mor A. Apr 14 '18 at 12:40
  • @M.Aroosi Ooh, I didn't know that! Very cool! Thank you for the tip. – Mark Seemann Apr 14 '18 at 12:51