Custom Operators in Haskell
Unlike many other languages, Haskell supports the use of custom user-defined operators. Which can be used to overload operators and condense code to make it easier to read (when used correctly).
Basic Example
To define a custom operator in Haskell, there are two different ways. The first way is to define it with the infix notation:
a |+| b = abs a + abs b
In the above code, the custom operator |+|
takes the absolute value of both arguments and returns their sum. It can be used just like the + operator:
ghci> 1 |+| 1
2
ghci> (-3) |+| 5
8
ghci> (-2) |+| (-2)
4
Like everything else in Haskell, all operators are functions, including the + operator (See here for how + is implemented for Int
and other Num
types). This also means that custom operators can (and should) have their own type signature. This is almost exactly the same as a regular type signature, but instead the operator is put inside parentheses. So to make a type signature for the operator |+|
defined earlier, it would look like the following:
(|+|) :: Num a => a -> a -> a
a |+| b = abs a + abs b
Legal Operator Symbols
But what are the limits for creating a custom operator? According to The Haskell 98 Report, the following symbols can be used to create a special operator:
! # $ % & * + . / < = > ? @ \ ^ | - ~
(+ Any Unicode Symbol/Punctuation)
That means that if someone really wanted to, they could create a monster like this:
(!#$%&*+./\<=>?@^|-~) :: Num a => a -> a -> a
a !#$%&*+./\<=>?@^|-~ b = a + b
ghci> 1 !#$%&*+./\<=>?@^|-~ 1
2
Operators With Any Number of Arguments
Being functions, operators are not limited to only taking in two arguments. They can take in 3, 4, 50, or even just 1. To do this, the function definition needs to be written in prefix notation as opposed to the infix used earlier. This is the same as the type signature, simply put the symbol in parentheses, and proceed with the definition normally as if it were a normal function. For example, here’s a triple plus operator that takes in three Num
’s and returns their sum:
(+++) :: Num a => a -> a -> a -> a
(+++) a b c = a + b + c
ghci> (+++) 4 1 9
14
Note that when calling the function, it also needs to be written in the prefix form, although technically a combination of both could be used like so:
ghci> (4 +++ 1) 9
14
Recursion
Another fun thing operators can do since they are functions is allow recursive calls to themselves. This is exactly the same way recursive functions are defined in Haskell. For example, here is a custom operator that performs addition via recursion:
(<+>) :: Num a => a -> a -> a
a <+> b = if b /= 0 then (a + 1) <+> (b - 1) else a
And for even more fun, here is a custom operator with a Unicode symbol that takes in one argument and returns an infinite list of that argument by recursively calling itself forever:
(∞) :: a -> [a]
(∞) x = x : (∞) x
ghci> take 5 $ (∞) 1
[1,1,1,1,1]