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.