56

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.

Davorak
  • 7,362
  • 1
  • 38
  • 48
Andriy Drozdyuk
  • 58,435
  • 50
  • 171
  • 272
  • 1
    while people have answered how you can overload (+), you should play with an interactive interpreter. You could try doing (1 + 1) and (1.0 + 1.0) and see it work. It is a powerful way of discovering a language. – Simon Bergot Nov 29 '11 at 09:36
  • 3
    One important difference between Haskell and Scala is overloading. When you do `foo + bar` in Scala, it is equivalent to `foo.+(bar)`. Scala is Object Oriented and allows methods of the same name to appear in different classes, regardless of whether or not they behave similarly. In Haskell, functions on different types can *only* have the same name if they are part of a typeclass. – Dan Burton Nov 29 '11 at 17:50
  • Are you saying that __1+1__ and __Pair(1,2) + Pair(2,3)__ cannot exist in the same file at the same time? – Andriy Drozdyuk Nov 29 '11 at 18:50
  • 5
    No, he's saying that in both cases, the `+` is the *same, generic* function -- not two different functions -- which is part of the `Num` typeclass. (There are lots of different implementations of `+`, though -- one for each instance of `Num`). – Matt Fenwick Nov 29 '11 at 21:07

5 Answers5

61

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.

Edward Kmett
  • 29,632
  • 7
  • 85
  • 107
AndrewC
  • 32,300
  • 7
  • 79
  • 115
  • So... does one have to do the `hiding` trick with this as well? Could you show complete source, with imports? – Andriy Drozdyuk Jul 21 '13 at 09:10
  • 1
    No, you don't need to hide anything, you don't need to import anything. If you copy the code (the two bits data... and instance....), it works as it is. It just adds a straightforward instance of the standard Num typeclass, no cunning tricks. :) – AndrewC Jul 21 '13 at 11:36
  • @drozzy If you like, you can do it with just standard pairs (a,b), without defining the data structure, it would save a bit of typing. I can show you how to do that too/instead if you like. – AndrewC Jul 21 '13 at 11:37
  • How de we define identity elements for a new type we built. For example for a new type of 2X2 matrices, how do I tell ((1, 0), (0, 1)) is the identity element so that for any matrix raised to the power 0, this is returned? – Priyatham Apr 27 '14 at 13:07
  • @Priyatham That's another question, really, but the answer is to make fromInteger return a matrix with that value down the diagonal and zeros elsewhere, so 0 is the zero matrix and 1 is the identity matrix. Mathematically that's because scalar multiplication can be thought of as being multiplication by the identity matrix times the scalar. – AndrewC Apr 27 '14 at 17:14
  • 1
    @Priyatham So for that instance, I'd write `fromInteger i = ((fromInteger i,0),(0,fromInteger i))`. (The second and third `fromInteger` are in case your type isn't `((Integer,Integer),(Integer,Integer))`.) – AndrewC Apr 29 '14 at 16:35
  • @AndrewC Thanks. But don't you agree that this not very natural in a mathematical sense? – Priyatham Apr 30 '14 at 01:45
  • 2
    No, it's completely natural. fromInteger is meant to be an embedding homomorphism, and `{kI|k<-F}` is idosorphic to `F`. – AndrewC Apr 30 '14 at 02:16
  • 1
    *isomophic, not idosorphic. Hehe. Typing not so great at 02:16! – AndrewC Feb 02 '15 at 16:59
  • Thank you for your answer, it is exactly what I was looking for! Could you help with a problem I ran into when using it. If I change the (+) operator to say Pair (a,b) + Pair (c,d) = Pair (a+b,c+d) it keeps complaining about incompatible types. How to overcome this? I only work with float numbers. – Antony Hatchkins Mar 23 '17 at 06:21
  • @AntonyHatchkins In that case you'd need to change the first line to `instance (Num a) => Num (Pair a a)`, since you're mixing the two together, and so they need to have the same type. – AndrewC Apr 01 '17 at 15:03
  • I tried this, but it says: "Illegal instance declaration for `Num (Pair a a)' (All instance types must be of the form (T a1 ... an) where a1 ... an are *distinct type variables*, and each type variable appears at most once in the instance head." – Antony Hatchkins Apr 01 '17 at 16:56
  • 2
    @AntonyHatchkins Sorry, my mistake. Switch that advice to: If you're using the same type in both coordinates, do `newtype Pair a = Pair (a,a)` then use `instance Num a => Num (Pair a)` – AndrewC Apr 03 '17 at 12:55
  • @AntonyHatchkins The language extension `FlexibleInstances` removes this restriction. To use it, put `{-# LANGUAGE FlexibleInstances #-}` on top of the source file. – MauganRa May 08 '18 at 14:12
35

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.

deontologician
  • 2,764
  • 1
  • 21
  • 33
  • 1
    Thank you, that is the most clear explanation of the issue. I like how you explain that all functionalities are in Num for historical reasons, and how it should have been done instead. Perfect. – Andriy Drozdyuk Nov 30 '11 at 19:26
  • Ok, so you now know that the answer is definitely **yes**, and that there are multiple ways to do it in fact. – deontologician Nov 30 '11 at 23:29
  • 2
    Thanks for great explanation, but I would say that full answer from you post is "yes, you can, and no, you shouldn't". This problem doesn't exist for module typeclasses as they can be imported qualified, but it will always exist for Prelude. One have to admit that some functions are locked to some typeclasses and life would be much harder if they weren't. Even if you defined typeclass `Multipliable` instead of `Num` it would not solve problem with vectors (there are two products). So `+` belongs to Num, and if my object doesn't fit Num's profile then I have to create my own functions. – Victor Moroz Dec 01 '11 at 20:59
  • shame that (+) is bundled for numbers only... some more sensible group of operators would have been nice, and yes (+) on an Addable class or so – neu-rah Mar 10 '16 at 22:01
34

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
hammar
  • 138,522
  • 17
  • 304
  • 385
  • 3
    But why don't people use + instead of weird looking things? I mean it' so much better looking! – Andriy Drozdyuk Nov 29 '11 at 13:57
  • @hammar what is the difference between (+) and abs? Both belong to class Num, but I can override abs in my module, but not (+). I can also override (++), so it's not a symbol issue. Does it mean that symbolic members of class have global scope? I feel like I am missing something. – Victor Moroz Nov 29 '11 at 17:22
  • For an interesting example, see [RWH > Data Structures # Numeric Types](http://book.realworldhaskell.org/read/data-structures.html#data.num) – Dan Burton Nov 29 '11 at 17:36
  • 2
    @VictorMoroz not sure what you're talking about. Can you show us some Haskell code where you can override `abs` but not `+`? `+` should receive no more special treatment than `abs`. – Dan Burton Nov 29 '11 at 17:46
  • 5
    @VictorMoroz: That's not overloading, that's just shadowing the name. And yes, you can also shadow `(+)`, there is nothing special about symbols. However, that's likely going to be extremely inconvenient, as you would have to use a qualified name whenever you want to refer to the "real" `(+)` operator, e.g. `1 Prelude.+ 2`. – hammar Nov 29 '11 at 17:48
  • Got it, used unqualified (+) in function body, so this was ambiguous, not function itself. Should have used Prelude.+ (and I agree this should not be done in real life, just curiosity) – Victor Moroz Nov 29 '11 at 18:13
  • So... this is really not an answer to my question, because I didn't ask if you could define a NEW operator. Nor did I ask if you could implement (+) in some "type class". Can you implement JUST the (+) and nothing else? – Andriy Drozdyuk Nov 30 '11 at 14:26
19

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.

Nicolas Wu
  • 4,805
  • 2
  • 25
  • 32
  • 1
    @drozzy You can. Write an instance of `Num` for your custom data type and away you go. – Daniel Wagner Nov 29 '11 at 09:03
  • @drozzy, sorry I accidently hit the enter button a bit too soon: a proper answer is coming! – Nicolas Wu Nov 29 '11 at 09:08
  • So... why can't I write a new class called "Matrix" or something like that, and define only (+), (=) and (-) on it? – Andriy Drozdyuk Nov 29 '11 at 18:50
  • 4
    @drozzy: the compiler won't stop you from giving a partial typeclass definition (but it will generate warnings). The problem is that others might rely on the Num and Eq classes being fully implemented, so their expectations won't match what you're providing. I think the real issue here is that the Num class probably has too many functions bundled together that aren't necessarily related. – Nicolas Wu Nov 29 '11 at 21:54
  • Ok, ok, sorry I meant typeclass. You write new typeclass with (+), and then implement it. – Andriy Drozdyuk Nov 30 '11 at 13:40
  • 4
    Oh, you *could* create a new typeclass with (+) in it, but then you'd have an ambiguous occurrence problem: the `Prelude.+` would clash with your `Matrix.+`. You could always hide the Prelude version, but I'm sure you'd prefer to keep it around, since it's a useful operator. Alternatively you could explicitly qualify your `Matrix.+`, but then it's probably best to make a new symbol like `|+|` as hammar suggested. – Nicolas Wu Nov 30 '11 at 15:21
7

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")
yihuang
  • 329
  • 2
  • 7
  • 2
    The issue is that the (+) operator is bundled in the Num typeclass, along with (+),(-),abs etc, so if you *only* want the semantics for addition, Monoid is the best typeclass to implement. Unfortunately, because of historical reasons, (+) is not the name of the Monoid addition operator, it's called `mappend` (which you can alias, and in this case, yihuang is suggesting aliasing it with (++)). This is probably a more theoretical answer than you were looking for, since you are just trying to overload the (+) operator. Try to be a little less argumentative though,it makes people not want to help – deontologician Nov 30 '11 at 15:35
  • Ok, thanks. But ++ is not a +. Or am I going blind? Regarding argumentative - I am trying not to be. But I keep asking for a hammer, and people keep passing me a screwdriver. – Andriy Drozdyuk Nov 30 '11 at 16:43
  • 4
    The standard alias for `mappend` is now `<>`, which is already widely used. @drozzy: I'd rather say, you keep asking for a [mallet](http://en.wikipedia.org/wiki/Mallet), and people well avoid passing you a [mallet](http://en.wikipedia.org/wiki/Percussion_mallet). The thing is, though it is now common in many programming languages to use `+` for any kind of combination, this is quite a recent development, and, I wager, has in fact a lot to do with the limitation to some predefined set of operators. In maths, `+` is usually much more limited, for instance assumed to be commutative. – leftaroundabout Jul 21 '13 at 19:09