Can you overload + in haskell?

Haskell

Haskell Problem Overview


While I've seen all kinds of weird things in Haskell sample code - I've never seen an operator plus being overloaded. Is there something special about it?

Let's say I have a type like Pair, and I want to have something like

 Pair(2,4) + Pair(1,2) = Pair(3,6)

Can one do it in haskell?

I am just curious, as I know it's possible in Scala in a rather elegant way.

Haskell Solutions


Solution 1 - Haskell

Yes

(+) is part of the Num typeclass, and everyone seems to feel you can't define (*) etc for your type, but I strongly disagree.

newtype Pair a b = Pair (a,b)  deriving (Eq,Show) 

I think Pair a b would be nicer, or we could even just use the type (a,b) directly, but...

This is very much like the cartesian product of two Monoids, groups, rings or whatever in maths, and there's a standard way of defining a numeric structure on it, which would be sensible to use.

instance (Num a,Num b) => Num (Pair a b) where
   Pair (a,b) + Pair (c,d) = Pair (a+c,b+d)
   Pair (a,b) * Pair (c,d) = Pair (a*c,b*d)
   Pair (a,b) - Pair (c,d) = Pair (a-c,b-d)
   abs    (Pair (a,b)) = Pair (abs a,    abs b) 
   signum (Pair (a,b)) = Pair (signum a, signum b) 
   fromInteger i = Pair (fromInteger i, fromInteger i)

Now we've overloaded (+) in an obvious way, but also gone the whole hog and overloaded (*) and all the other Num functions in the same, obvious, familiar way mathematics does it for a pair. I just don't see the problem with this. In fact I think it's good practice.

*Main> Pair (3,4.0) + Pair (7, 10.5)
Pair (10,14.5)
*Main> Pair (3,4.0) + 1    -- *
Pair (4,5.0)

* - Notice that fromInteger is applied to numeric literals like 1, so this was interpreted in that context as Pair (1,1.0) :: Pair Integer Double. This is also quite nice and handy.

Solution 2 - Haskell

Overloading in Haskell is only available using type classes. In this case, (+) belongs to the Num type class, so you would have to provide a Num instance for your type.

However, Num also contains other functions, and a well-behaved instance should implement all of them in a consistent way, which in general will not make sense unless your type represents some kind of number.

So unless that is the case, I would recommend defining a new operator instead. For example,

data Pair a b = Pair a b
    deriving Show

infixl 6 |+| -- optional; set same precedence and associativity as +
Pair a b |+| Pair c d = Pair (a+c) (b+d)

You can then use it like any other operator:

> Pair 2 4 |+| Pair 1 2
Pair 3 6

Solution 3 - Haskell

I'll try to come at this question very directly, since you are keen on getting a straight "yes or no" on overloading (+). The answer is yes, you can overload it. There are two ways to overload it directly, without any other changes, and one way to overload it "correctly" which requires creating an instance of Num for your datatype. The correct way is elaborated on in the other answers, so I won't go over it.

Edit: Note that I'm not recommending the way discussed below, just documenting it. You should implement the Num typeclass and not anything I write here.

The first (and most "wrong") way to overload (+) is to simply hide the Prelude.+ function, and define your own function named (+) that operates on your datatype.

import Prelude hiding ((+)) -- hide the autoimport of +
import qualified Prelude as P -- allow us to refer to Prelude functions with a P prefix

data Pair a = Pair (a,a)

(+) :: Num a => Pair a -> Pair a -> Pair a -- redefinition of (+)
(Pair (a,b)) + (Pair (c,d)) = Pair ((P.+) a c,(P.+) b d ) -- using qualified (+) from Prelude

You can see here, we have to go through some contortions to hide the regular definition of (+) from being imported, but we still need a way to refer to it, since it's the only way to do fast machine addition (it's a primitive operation).

The second (slightly less wrong) way to do it is to define your own typeclass that only includes a new operator you name (+). You'll still have to hide the old (+) so haskell doesn't get confused.

import Prelude hiding ((+))
import qualified Prelude as P

data Pair a = Pair (a,a)

class Addable a where
   (+) :: a -> a -> a

instance Num a => Addable (Pair a) where
    (Pair (a,b)) + (Pair (c,d)) = Pair ((P.+) a c,(P.+) b d )

This is a bit better than the first option because it allows you to use your new (+) for lots of different data types in your code.

But neither of these are recommended, because as you can see, it is very inconvenient to access the regular (+) operator that is defined in the Num typeclass. Even though haskell allows you to redefine (+), all of the Prelude and the libraries are expecting the original (+) definition. Lucky for you, (+) is defined in a typeclass, so you can just make Pair an instance of Num. This is probably the best option, and it is what the other answerers have recommended.

The issue you are running into is that there are possibly too many functions defined in the Num typeclass (+ is one of them). This is just a historical accident, and now the use of Num is so widespread, it would be hard to change it now. Instead of splitting those functionalities out into separate typeclasses for each function (so they can be overridden separately) they are all glommed together. Ideally the Prelude would have an Addable typeclass, and a Subtractable typeclass etc. that allow you to define an instance for one operator at a time without having to implement everything that Num has in it.

Be that as it may, the fact is that you will be fighting an uphill battle if you want to write a new (+) just for your Pair data type. Too much of the other Haskell code depends on the Num typeclass and its current definition.

You might look into the Numeric Prelude if you are looking for a blue-sky reimplementation of the Prelude that tries to avoid some of the mistakes of the current one. You'll notice they've reimplemented the Prelude just as a library, no compiler hacking was necessary, though it's a huge undertaking.

Solution 4 - Haskell

Overloading in Haskell is made possible through type classes. For a good overview, you might want to look at this section in Learn You a Haskell.

The (+) operator is part of the Num type class from the Prelude:

class (Eq a, Show a) => Num a where
  (+), (*), (-) :: a -> a -> a
  negate :: a -> a
  ...

So if you'd like a definition for + to work for pairs, you would have to provide an instance.

If you have a type:

data Pair a = Pair (a, a) deriving (Show, Eq)

Then you might have a definition like:

instance Num a => Num (Pair a) where
  Pair (x, y) + Pair (u, v) = Pair (x+u, y+v)
  ...

Punching this into ghci gives us:

*Main> Pair (1, 2) + Pair (3, 4)
Pair (4,6)

However, if you're going to give an instance for +, you should also be providing an instance for all of the other functions in that type class too, which might not always make sense.

Solution 5 - Haskell

If you only want (+) operator rather than all the Num operators, probably you have a Monoid instance, for example Monoid instance of pair is like this:

class (Monoid a, Monoid b) => Monoid (a, b) where
    mempty = (mempty, mempty)
    (a1, b1) `mappend` (a2, b2) = (a1 `mappend` a2, b1 `mappend` b2)

You can make (++) a alias of mappend, then you can write code like this:

(1,2) ++ (3,4) == (4,6)
("hel", "wor") ++ ("lo", "ld") == ("hello", "world")

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndriy DrozdyukView Question on Stackoverflow
Solution 1 - HaskellAndrewCView Answer on Stackoverflow
Solution 2 - HaskellhammarView Answer on Stackoverflow
Solution 3 - HaskelldeontologicianView Answer on Stackoverflow
Solution 4 - HaskellNicolas WuView Answer on Stackoverflow
Solution 5 - HaskellyihuangView Answer on Stackoverflow