A Monad is a triple where:
- is an endofunctor
- is a natural transformation (the unit)
- is another natural transformation (the multiplication)
These must satisfy the following coherence conditions, known as the Monad laws:
- (associativity)
- (left and right identity)
This means that for any object in , we have:
- (associativity)
- (left and right unit laws)
We can rephrase these conditions using the subsequent commutative diagrams:
We can also write down the natural transformations in terms of their components. For each object of , the unit is a morphism , and the multiplication is a morphism , such that the following diagrams commute:
An application of this concept is that monads provide a way to express computations (in terms of morphisms) that include additional structure or side-effects (captured by the endofunctor ) in such a way that these computations can be chained together (via the natural transformation) and lifted over the monadic structure (via the natural transformation), and they do so in a way that is consistent (respecting the associativity and unit laws).
Example
The Monad, by definition, requires us to implement two functions: the unit, which is called return in Haskell, where we just have to lift a value into the Monad (e.g., put a value into a list), and the multiplication join
.
Haskell Definition of Monad (Interface)
class Monad m where
-- ηx : A -> T A (unit)
return :: a -> m a
-- μx : T (T A) -> T A (multiplication)
join :: m (m a) -> m a
These have to obey the Monad laws:
join . join == join . fmap join -- associativity
join . return == id == join . fmap return -- left and right identiy
We can now draw the commutative diagram for the Haskell definition of Monad:
The definition of a monad given here is equivalent to the one we typically use in Haskell.
class Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
We can easily define >>=
with join
and fmap
.
a >>= f = join (fmap f a)
This operation is called bind (or is sometimes refered to as flatMap). The bind function can be used if you need to operate on the lifted value before collapsing. We can also translate the other way around and define join
in terms of >>=
and id
:
join a = a >>= id
Hence join
is bind applied to the identity function. These two constructions are reverse to each other and they translate the monad laws correctly. Now we lets have a look at some concrete examples (instances of Monad).
An Instance of Monad, the List Monad
instance Monad [] where
return :: a -> [a]
return x = [x]
(>>=) :: [a] -> (a -> [b]) -> [b]
xs >>= f = concat (map f xs)
Another Instance, the Maybe Monad
instance Monad Maybe where
return :: a -> Maybe a
return x = Just x
(>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
(>>=) m g = case m of
Nothing -> Nothing
Just x -> g x
All of the above is already implemented in the standard Haskell library, so you can simply open an interactive Haskell interpreter (ghci) and test the following examples.
ghci> [1,2,3] >>= \x -> [x, x*2]
[1,2,2,4,3,6]
ghci> Just 3 >>= \x -> Just (x*2)
Just 6
-- And heres how to use IO
ghci> getLine >>= \line -> putStrLn ("You said: " ++ line ++ "!")
Hi
You said: Hi!
References
- 0.The diagram displayed at the top of this post is a modified version of Brent Yorgey's Typeclassopedia diagram ↩
- 1.Monad in ncatlab ↩
- 2.Notes on Category Theory by Paolo Perrone ↩
- 3.Category theory/Monads ↩