Monads by example

Running :i Monad in ghci yields the following:

class Applicative m => Monad (m :: * -> *) where
  (>>=) :: m a -> (a -> m b) -> m b
  (>>) :: m a -> m b -> m b
  return :: a -> m a
  fail :: String -> m a

It also lists available instances of the Monad type class:

instance Monad m => Monad (WrappedMonad m)
  -- Defined in ‘Control.Applicative’
instance Monad (Either e) -- Defined in ‘Data.Either’
instance Monad [] -- Defined in ‘GHC.Base’
instance Monad Maybe -- Defined in ‘GHC.Base’
instance Monad IO -- Defined in ‘GHC.Base’
instance Monad ((->) r) -- Defined in ‘GHC.Base’
instance Monoid a => Monad ((,) a) -- Defined in ‘GHC.Base’

Concrete type signatures

To understand similarities and differences between various instances of the Monad type class we will take a look at the type signatures of the return, >>= and >> functions for List, Maybe and Either types:

-- Generic
return :: a -> m a

-- List
return :: a -> [a]

-- Maybe
return :: a -> Maybe a

-- Either
return :: a -> Either l a
-- Generic
(>>=) :: m a -> (a -> m b) -> m b

-- List
(>>=) :: [a] -> (a -> [b]) -> [b]

-- Maybe
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b

-- Either
(>>=) :: Either a b -> (b -> Either a c) -> Either a c
-- Generic
(>>) :: m a -> m b -> m b

-- List
(>>) :: [a] -> [b] -> [b]

-- Maybe
(>>) :: Maybe a -> Maybe b -> Maybe b

-- Either
(>>) :: Either a b -> Either a c -> Either a c

Using return with different types

Do you remember how we said "pure lifts a value to a given type" in the previous note? return is a function doing exactly the same thing for the instances of the Monad type class:

-- List
(return 2 :: [Integer]) == [2]
-- Maybe
(return 2 :: Maybe Integer) == Just 2
-- Either
(return 2 :: Either a Integer) == Right 2

Interactive Examples:


Using >>= with different types

>>= also known as bind helps us run monadic actions (values) in sequence by composing them. A value produced by the first one will be passed as an argument to the second.

After actions are defined we will use >>= to sequentially execute our program:

addOne :: (Monad m, Num a) => a -> m a
addOne a = return (a + 1)
-- List
[ ]    >>= addOne == [ ]
[1]    >>= addOne == [2]
[1, 2] >>= addOne == [2, 3]

[ ]    >>= addOne >>= addOne == [ ]
[1]    >>= addOne >>= addOne == [3]
[1, 2] >>= addOne >>= addOne == [3, 4]
-- Maybe
Nothing >>= addOne == Nothing
Just 1  >>= addOne == Just 2

Nothing >>= addOne >>= addOne == Nothing
Just 1  >>= addOne >>= addOne == Just 3
-- Either
Left  1 >>= addOne == Left  1
Right 1 >>= addOne == Right 2

Left  1 >>= addOne >>= addOne == Left  1
Right 1 >>= addOne >>= addOne == Right 3

Iteractive Examples:


Using >> with different types

Similar to the bind operator >> allows us to sequentially compose two actions by ignoring the value produced by the first.

-- List
[ ] >> [ ] == [ ]
[ ] >> [2] == [ ]
[1] >> [ ] == [ ]
[1] >> [2] == [2]
-- Maybe
Nothing >> Nothing == Nothing
Nothing >> Just 2  == Nothing
Just 1  >> Nothing == Nothing
Just 1  >> Just 2  == Just 2
-- Either
Left  1 >> Left  2 == Left  1
Left  1 >> Right 2 == Left  1
Right 1 >> Left  2 == Left  2
Right 1 >> Right 2 == Right 2

Interactive Examples:


I was pretty intrigued when I saw how these methods work. After reading so much about Monads on the internet I was pretty scared of the concept but then — after I started using them — it turned out they aren't as scary as I originally thought.

Let me know on twitter what was your experience - @maciejsmolinski.