One aspect of Haskell that many new users find difficult to get a handle on is operators. Unlike many other languages, Haskell gives a lot of flexibility to developers to define custom operators. This can lead to shorter, more elegant code in many cases. For example, compare these three equivalent pieces of code:
v1 = mappend (mappend "hello " "there ") "world" v2 = "hello " `mappend` "there " `mappend` "world" v3 = "hello " ++ "there " ++ "world"
Unfortunately, not all operators are as selfexplanatory as the ++
operator (which, in case you're wondering, is "list append"). This
page will attempt to cover the most common "surprising" operators. In
other words: we won't bother covering common mathematical operators
like +
or *
, nor will we cover operators defined in less common
libraries.
Hoogle is your friend
It's worth pointing out as well that for many operators, using the
Hoogle search engine can be a
great way to find out about an operator, or for that matter any
function. It's
pretty easy to find ++
that way. Go ahead, try it out now!
Function application $
($) :: (a > b) > a > b
One of the most common operators, and source of initial confusion, is
the $
operator. All this does is apply a function. So, f $ x
is
exactly equivalent to f x
. If so, why would you ever use $
? The
primary reason is  for those who prefer the style  to avoid
parentheses. For example, you can replace:
foo (bar (baz bin))
with
foo $ bar $ baz bin
A less common but arguably more compelling use case is to capture the act of applying a function to an argument. To clarify that rather vague statement with an example:
#!/usr/bin/env stack  stack resolver lts12.21 script double :: Int > Int double x = x + x square :: Int > Int square x = x * x main :: IO () main = print (map ($ 5) [double, square])
The ($ 5)
bit means "apply the function to 5", and then we can use
map
to use it with both the double
and square
functions.
Function composition .
(.) :: (b > c) > (a > b) > (a > c)
Not much more to it than that: take two functions and compose them together.
#!/usr/bin/env stack  stack resolver lts12.21 script double :: Int > Int double x = x + x square :: Int > Int square x = x * x main :: IO () main = (print . double . square) 5
Or you can combine this together with the $
operator to avoid those
parentheses if you're so inclined:
main = print . double . square $ 5
In addition to its usage for function composition, the period is also used for hierarchical modules, e.g.:
#!/usr/bin/env stack  stack resolver lts12.21 script import qualified Data.Monoid main :: IO () main = putStrLn $ Data.Monoid.mappend "hello " "world"
Finally, in the Control.Category
module, the Category
typeclass
also uses the .
operator to define categorical composition. This
generalizes standard function composition, but is not as commonly
used.
Reverse function application &
(&) :: a > (a > b) > b
&
is just like $
only backwards. Take our example for $
:
foo $ bar $ baz bin
This is semantically equivalent to:
bin & baz & bar & foo
&
is useful because the order in which functions are applied to their
arguments read left to right instead of the reverse (which is the case
for $
). This is closer to how English is read so it can improve code clarity.
In our function composition example we composed the functions
square
, double
, and print
and applied the resulting function to the number 5
.
Rewriting it using &
gives us
#!/usr/bin/env stack  stack resolver lts12.21 script import Data.Function double :: Int > Int double x = x + x square :: Int > Int square x = x * x main :: IO () main = 5 & square & double & print
Monoidal append <>
(<>) :: Monoid m => m > m > m
The <>
operator is just a synonym for the
mappend
function. This
comes from the Monoid
typeclass, which represents types which have
an identity and an associative binary operation. Some examples:
 For lists,
<>
is the same as++
(append two lists)  For vectors, this logic holds as well
 For
Set
s, this is a union operation (all values present in eitherSet
)  For
Map
s, we have a "left biased union", meaning we combine the key/value pairs from both inputs, and if both inputs share a key, the value in the left input is selected  For numbers, both addition and multiplication form a
Monoid
, where 0 is the additive identity (since0 + x = x
) and 1 is the multiplicative identity (since1 * x = x
). Therefore, to avoid confusion,Data.Monoid
defines helper newtype wrappersSum
andProduct
#!/usr/bin/env stack  stack resolver lts12.21 script import Data.Monoid ((<>)) main :: IO () main = putStrLn $ "hello " <> "there " <> "world!"
Functor map <$>
(<$>) :: Functor f => (a > b) > f a > f b (<$) :: Functor f => a > f b > f a ($>) :: Functor f => f a > b > f b
The <$>
operator is just a synonym for the
fmap
function
from the Functor
typeclass. This function generalizes the map
function for lists to many other data types, such as Maybe
, IO
,
and Map
.
#!/usr/bin/env stack  stack resolver lts12.21 script import Data.Monoid ((<>)) main :: IO () main = do putStrLn "Enter your year of birth" year < read <$> getLine let age :: Int age = 2020  year putStrLn $ "Age in 2020: " <> show age
In addition, there are two additional operators provided which replace
a value inside a Functor
instead of applying a function. This can be
both more convenient in some cases, as well as for some Functor
s be
more efficient. In terms of definition:
value <$ functor = const value <$> functor functor $> value = const value <$> functor x <$ y = y $> x x $> y = y <$ x
Applicative function application <*>
(<*>) :: Applicative f => f (a > b) > f a > f b (*>) :: Applicative f => f a > f b > f b (<*) :: Applicative f => f a > f b > f a
Commonly seen with <$>
, <*>
is an operator that applies a wrapped
function to a wrapped value. It is part of the Applicative
typeclass, and is very often seen in code like the following:
foo <$> bar <*> baz
For cases when you're dealing with a Monad
, this is equivalent to:
do x < bar y < baz return (foo x y)
Other common examples including parsers and serialization libraries. Here's an example you might see using the aeson package:
data Person = Person { name :: Text, age :: Int } deriving Show  We expect a JSON object, so we fail at any nonObject value. instance FromJSON Person where parseJSON (Object v) = Person <$> v .: "name" <*> v .: "age" parseJSON _ = empty
To go along with this, we have two helper operators that are less frequently used:

*>
ignores the value from the first argument. It can be defined as:a1 *> a2 = (id <$ a1) <*> a2
Or in
do
notation:a1 *> a2 = do _ < a1 a2
For
Monad
s, this is completely equivalent to>>
. 
<*
is the same thing in reverse: perform the first action then the second, but only take the value from the first action. Again, definitions in terms of<*>
anddo
notation:(<*) = liftA2 const a1 <* a2 = do res < a1 _ < a2 return res
Various monadic binding/composition operators
(>>=) :: Monad m => m a > (a > m b) > m b (=<<) :: Monad m => (a > m b) > m a > m b (>>) :: Monad m => m a > m b > m b (>=>) :: Monad m => (a > m b) > (b > m c) > (a > m c) (<=<) :: Monad m => (b > m c) > (a > m b) > (a > m c)
There are a few different monadic binding operators. The two most
basic are >>=
and >>
, as they can be trivially expressed in
do
notation. And as previously mentioned, >>
is just a synonym for
*>
from the Applicative
class, so it's even easier. =<<
is
just >>=
with the arguments reversed.
m1 >>= f = do x < m1 f x m1 >> m2 = do _ < m1 m2 f =<< m1 = do x < m1 f x
In addition to these two operators, there are also composition
operators for when you have two monadic functions. >=>
pipes the
result from the left side to the right side, while <=<
pipes the
result the other way. In other words:
f >=> g = \x > do y < f x g y g <=< f = \x > do y < f x g y f >=> g = g <=< f g >=> f = f <=< g
Alternative <>
(<>) :: Alternative f => f a > f a > f a
The Alternative
typeclass provides a binary operation on applicative
functors (<>
), as well as some identity value (empty
). This is
used in the ecosystem for a number of different activities, e.g.:
 In parser libraries for defining different alternative parsing options
 In the async library to run two different
Concurrently
actions at once and take the first result to succeed
#!/usr/bin/env stack  stack resolver lts12.21 script import Control.Applicative ((<>)) import Control.Concurrent (threadDelay) import Control.Concurrent.Async (Concurrently (..)) main :: IO () main = do res < runConcurrently $ (Concurrently (threadDelay 1000000 >> return (Left "Hello"))) <> (Concurrently (threadDelay 2000000 >> return (Right 42))) print res
More operators!
If you're aware of other common operators that cause confusion, please open an issue or a PR to extend this document!