tl;dr
{-# LANGUAGE InstanceSigs #-}
newtype Id t = Id t
instance Monad Id where
return :: t -> Id t
return = Id
(=<<) :: (a -> Id b) -> Id a -> Id b
f =<< (Id x) = f x
Prologue
The application operator $
of functions
forall a b. a -> b
is canonically defined
($) :: (a -> b) -> a -> b
f $ x = f x
infixr 0 $
in terms of Haskell-primitive function application f x
(infixl 10
).
Composition .
is defined in terms of $
as
(.) :: (b -> c) -> (a -> b) -> (a -> c)
f . g = \ x -> f $ g x
infixr 9 .
and satisfies the equivalences forall f g h.
f . id = f :: c -> d Right identity
id . g = g :: b -> c Left identity
(f . g) . h = f . (g . h) :: a -> d Associativity
.
is associative, and id
is its right and left identity.
The Kleisli triple
In programming, a monad is a functor type constructor with an instance of the monad type class. There are several equivalent variants of definition and implementation, each carrying slightly different intuitions about the monad abstraction.
A functor is a type constructor f
of kind * -> *
with an instance of the functor type class.
{-# LANGUAGE KindSignatures #-}
class Functor (f :: * -> *) where
map :: (a -> b) -> (f a -> f b)
In addition to following statically enforced type protocol, instances of the functor type class must obey the algebraic functor laws forall f g.
map id = id :: f t -> f t Identity
map f . map g = map (f . g) :: f a -> f c Composition / short cut fusion
Functor computations have the type
forall f t. Functor f => f t
A computation c r
consists in results r
within context c
.
Unary monadic functions or Kleisli arrows have the type
forall m a b. Functor m => a -> m b
Kleisi arrows are functions that take one argument a
and return a monadic computation m b
.
Monads are canonically defined in terms of the Kleisli triple forall m. Functor m =>
(m, return, (=<<))
implemented as the type class
class Functor m => Monad m where
return :: t -> m t
(=<<) :: (a -> m b) -> m a -> m b
infixr 1 =<<
The Kleisli identity return
is a Kleisli arrow that promotes a value t
into monadic context m
. Extension or Kleisli application =<<
applies a Kleisli arrow a -> m b
to results of a computation m a
.
Kleisli composition <=<
is defined in terms of extension as
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
f <=< g = \ x -> f =<< g x
infixr 1 <=<
<=<
composes two Kleisli arrows, applying the left arrow to results of the right arrow’s application.
Instances of the monad type class must obey the monad laws, most elegantly stated in terms of Kleisli composition: forall f g h.
f <=< return = f :: c -> m d Right identity
return <=< g = g :: b -> m c Left identity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d Associativity
<=<
is associative, and return
is its right and left identity.
Identity
The identity type
type Id t = t
is the identity function on types
Id :: * -> *
Interpreted as a functor,
return :: t -> Id t
= id :: t -> t
(=<<) :: (a -> Id b) -> Id a -> Id b
= ($) :: (a -> b) -> a -> b
(<=<) :: (b -> Id c) -> (a -> Id b) -> (a -> Id c)
= (.) :: (b -> c) -> (a -> b) -> (a -> c)
In canonical Haskell, the identity monad is defined
newtype Id t = Id t
instance Functor Id where
map :: (a -> b) -> Id a -> Id b
map f (Id x) = Id (f x)
instance Monad Id where
return :: t -> Id t
return = Id
(=<<) :: (a -> Id b) -> Id a -> Id b
f =<< (Id x) = f x
Option
An option type
data Maybe t = Nothing | Just t
encodes computation Maybe t
that not necessarily yields a result t
, computation that may “fail”. The option monad is defined
instance Functor Maybe where
map :: (a -> b) -> (Maybe a -> Maybe b)
map f (Just x) = Just (f x)
map _ Nothing = Nothing
instance Monad Maybe where
return :: t -> Maybe t
return = Just
(=<<) :: (a -> Maybe b) -> Maybe a -> Maybe b
f =<< (Just x) = f x
_ =<< Nothing = Nothing
a -> Maybe b
is applied to a result only if Maybe a
yields a result.
newtype Nat = Nat Int
The natural numbers can be encoded as those integers greater than or equal to zero.
toNat :: Int -> Maybe Nat
toNat i | i >= 0 = Just (Nat i)
| otherwise = Nothing
The natural numbers are not closed under subtraction.
(-?) :: Nat -> Nat -> Maybe Nat
(Nat n) -? (Nat m) = toNat (n - m)
infixl 6 -?
The option monad covers a basic form of exception handling.
(-? 20) <=< toNat :: Int -> Maybe Nat
List
The list monad, over the list type
data [] t = [] | t : [t]
infixr 5 :
and its additive monoid operation “append”
(++) :: [t] -> [t] -> [t]
(x : xs) ++ ys = x : xs ++ ys
[] ++ ys = ys
infixr 5 ++
encodes nonlinear computation [t]
yielding a natural amount 0, 1, ...
of results t
.
instance Functor [] where
map :: (a -> b) -> ([a] -> [b])
map f (x : xs) = f x : map f xs
map _ [] = []
instance Monad [] where
return :: t -> [t]
return = (: [])
(=<<) :: (a -> [b]) -> [a] -> [b]
f =<< (x : xs) = f x ++ (f =<< xs)
_ =<< [] = []
Extension =<<
concatenates ++
all lists [b]
resulting from applications f x
of a Kleisli arrow a -> [b]
to elements of [a]
into a single result list [b]
.
Let the proper divisors of a positive integer n
be
divisors :: Integral t => t -> [t]
divisors n = filter (`divides` n) [2 .. n - 1]
divides :: Integral t => t -> t -> Bool
(`divides` n) = (== 0) . (n `rem`)
then
forall n. let { f = f <=< divisors } in f n = []
In defining the monad type class, instead of extension =<<
, the Haskell standard uses its flip, the bind operator >>=
.
class Applicative m => Monad m where
(>>=) :: forall a b. m a -> (a -> m b) -> m b
(>>) :: forall a b. m a -> m b -> m b
m >> k = m >>= \ _ -> k
{-# INLINE (>>) #-}
return :: a -> m a
return = pure
For simplicity's sake, this explanation uses the type class hierarchy
class Functor f
class Functor m => Monad m
In Haskell, the current standard hierarchy is
class Functor f
class Functor p => Applicative p
class Applicative m => Monad m
because not only is every monad a functor, but every applicative is a functor and every monad is an applicative, too.
Using the list monad, the imperative pseudocode
for a in (1, ..., 10)
for b in (1, ..., 10)
p <- a * b
if even(p)
yield p
roughly translates to the do block,
do a <- [1 .. 10]
b <- [1 .. 10]
let p = a * b
guard (even p)
return p
the equivalent monad comprehension,
[ p | a <- [1 .. 10], b <- [1 .. 10], let p = a * b, even p ]
and the expression
[1 .. 10] >>= (\ a ->
[1 .. 10] >>= (\ b ->
let p = a * b in
guard (even p) >> -- [ () | even p ] >>
return p
)
)
Do notation and monad comprehensions are syntactic sugar for nested bind expressions. The bind operator is used for local name binding of monadic results.
let x = v in e = (\ x -> e) $ v = v & (\ x -> e)
do { r <- m; c } = (\ r -> c) =<< m = m >>= (\ r -> c)
where
(&) :: a -> (a -> b) -> b
(&) = flip ($)
infixl 0 &
The guard function is defined
guard :: Additive m => Bool -> m ()
guard True = return ()
guard False = fail
where the unit type or “empty tuple”
data () = ()
Additive monads that support choice and failure can be abstracted over using a type class
class Monad m => Additive m where
fail :: m t
(<|>) :: m t -> m t -> m t
infixl 3 <|>
instance Additive Maybe where
fail = Nothing
Nothing <|> m = m
m <|> _ = m
instance Additive [] where
fail = []
(<|>) = (++)
where fail
and <|>
form a monoid forall k l m.
k <|> fail = k
fail <|> l = l
(k <|> l) <|> m = k <|> (l <|> m)
and fail
is the absorbing/annihilating zero element of additive monads
_ =<< fail = fail
If in
guard (even p) >> return p
even p
is true, then the guard produces [()]
, and, by the definition of >>
, the local constant function
\ _ -> return p
is applied to the result ()
. If false, then the guard produces the list monad’s fail
( []
), which yields no result for a Kleisli arrow to be applied >>
to, so this p
is skipped over.
State
Infamously, monads are used to encode stateful computation.
A state processor is a function
forall st t. st -> (t, st)
that transitions a state st
and yields a result t
. The state st
can be anything. Nothing, flag, count, array, handle, machine, world.
The type of state processors is usually called
type State st t = st -> (t, st)
The state processor monad is the kinded * -> *
functor State st
. Kleisli arrows of the state processor monad are functions
forall st a b. a -> (State st) b
In canonical Haskell, the lazy version of the state processor monad is defined
newtype State st t = State { stateProc :: st -> (t, st) }
instance Functor (State st) where
map :: (a -> b) -> ((State st) a -> (State st) b)
map f (State p) = State $ \ s0 -> let (x, s1) = p s0
in (f x, s1)
instance Monad (State st) where
return :: t -> (State st) t
return x = State $ \ s -> (x, s)
(=<<) :: (a -> (State st) b) -> (State st) a -> (State st) b
f =<< (State p) = State $ \ s0 -> let (x, s1) = p s0
in stateProc (f x) s1
A state processor is run by supplying an initial state:
run :: State st t -> st -> (t, st)
run = stateProc
eval :: State st t -> st -> t
eval = fst . run
exec :: State st t -> st -> st
exec = snd . run
State access is provided by primitives get
and put
, methods of abstraction over stateful monads:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
class Monad m => Stateful m st | m -> st where
get :: m st
put :: st -> m ()
m -> st
declares a functional dependency of the state type st
on the monad m
; that a State t
, for example, will determine the state type to be t
uniquely.
instance Stateful (State st) st where
get :: State st st
get = State $ \ s -> (s, s)
put :: st -> State st ()
put s = State $ \ _ -> ((), s)
with the unit type used analogously to void
in C.
modify :: Stateful m st => (st -> st) -> m ()
modify f = do
s <- get
put (f s)
gets :: Stateful m st => (st -> t) -> m t
gets f = do
s <- get
return (f s)
gets
is often used with record field accessors.
The state monad equivalent of the variable threading
let s0 = 34
s1 = (+ 1) s0
n = (* 12) s1
s2 = (+ 7) s1
in (show n, s2)
where s0 :: Int
, is the equally referentially transparent, but infinitely more elegant and practical
(flip run) 34
(do
modify (+ 1)
n <- gets (* 12)
modify (+ 7)
return (show n)
)
modify (+ 1)
is a computation of type State Int ()
, except for its effect equivalent to return ()
.
(flip run) 34
(modify (+ 1) >>
gets (* 12) >>= (\ n ->
modify (+ 7) >>
return (show n)
)
)
The monad law of associativity can be written in terms of >>=
forall m f g.
(m >>= f) >>= g = m >>= (\ x -> f x >>= g)
or
do { do { do {
r1 <- do { x <- m; r0 <- m;
r0 <- m; = do { = r1 <- f r0;
f r0 r1 <- f x; g r1
}; g r1 }
g r1 }
} }
Like in expression-oriented programming (e.g. Rust), the last statement of a block represents its yield. The bind operator is sometimes called a “programmable semicolon”.
Iteration control structure primitives from structured imperative programming are emulated monadically
for :: Monad m => (a -> m b) -> [a] -> m ()
for f = foldr ((>>) . f) (return ())
while :: Monad m => m Bool -> m t -> m ()
while c m = do
b <- c
if b then m >> while c m
else return ()
forever :: Monad m => m t
forever m = m >> forever m
Input/Output
data World
The I/O world state processor monad is a reconciliation of pure Haskell and the real world, of functional denotative and imperative operational semantics. A close analogue of the actual strict implementation:
type IO t = World -> (t, World)
Interaction is facilitated by impure primitives
getChar :: IO Char
putChar :: Char -> IO ()
readFile :: FilePath -> IO String
writeFile :: FilePath -> String -> IO ()
hSetBuffering :: Handle -> BufferMode -> IO ()
hTell :: Handle -> IO Integer
. . . . . .
The impurity of code that uses IO
primitives is permanently protocolized by the type system. Because purity is awesome, what happens in IO
, stays in IO
.
unsafePerformIO :: IO t -> t
Or, at least, should.
The type signature of a Haskell program
main :: IO ()
main = putStrLn "Hello, World!"
expands to
World -> ((), World)
A function that transforms a world.
Epilogue
The category whiches objects are Haskell types and whiches morphisms are functions between Haskell types is, “fast and loose”, the category Hask
.
A functor T
is a mapping from a category C
to a category D
; for each object in C
an object in D
Tobj : Obj(C) -> Obj(D)
f :: * -> *
and for each morphism in C
a morphism in D
Tmor : HomC(X, Y) -> HomD(Tobj(X), Tobj(Y))
map :: (a -> b) -> (f a -> f b)
where X
, Y
are objects in C
. HomC(X, Y)
is the homomorphism class of all morphisms X -> Y
in C
. The functor must preserve morphism identity and composition, the “structure” of C
, in D
.
Tmor Tobj
T(id) = id : T(X) -> T(X) Identity
T(f) . T(g) = T(f . g) : T(X) -> T(Z) Composition
The Kleisli category of a category C
is given by a Kleisli triple
<T, eta, _*>
of an endofunctor
T : C -> C
(f
), an identity morphism eta
(return
), and an extension operator *
(=<<
).
Each Kleisli morphism in Hask
f : X -> T(Y)
f :: a -> m b
by the extension operator
(_)* : Hom(X, T(Y)) -> Hom(T(X), T(Y))
(=<<) :: (a -> m b) -> (m a -> m b)
is given a morphism in Hask
’s Kleisli category
f* : T(X) -> T(Y)
(f =<<) :: m a -> m b
Composition in the Kleisli category .T
is given in terms of extension
f .T g = f* . g : X -> T(Z)
f <=< g = (f =<<) . g :: a -> m c
and satisfies the category axioms
eta .T g = g : Y -> T(Z) Left identity
return <=< g = g :: b -> m c
f .T eta = f : Z -> T(U) Right identity
f <=< return = f :: c -> m d
(f .T g) .T h = f .T (g .T h) : X -> T(U) Associativity
(f <=< g) <=< h = f <=< (g <=< h) :: a -> m d
which, applying the equivalence transformations
eta .T g = g
eta* . g = g By definition of .T
eta* . g = id . g forall f. id . f = f
eta* = id forall f g h. f . h = g . h ==> f = g
(f .T g) .T h = f .T (g .T h)
(f* . g)* . h = f* . (g* . h) By definition of .T
(f* . g)* . h = f* . g* . h . is associative
(f* . g)* = f* . g* forall f g h. f . h = g . h ==> f = g
in terms of extension are canonically given
eta* = id : T(X) -> T(X) Left identity
(return =<<) = id :: m t -> m t
f* . eta = f : Z -> T(U) Right identity
(f =<<) . return = f :: c -> m d
(f* . g)* = f* . g* : T(X) -> T(Z) Associativity
(((f =<<) . g) =<<) = (f =<<) . (g =<<) :: m a -> m c
Monads can also be defined in terms not of Kleislian extension, but a natural transformation mu
, in programming called join
. A monad is defined in terms of mu
as a triple over a category C
, of an endofunctor
T : C -> C
f :: * -> *
and two natural tranformations
eta : Id -> T
return :: t -> f t
mu : T . T -> T
join :: f (f t) -> f t
satisfying the equivalences
mu . T(mu) = mu . mu : T . T . T -> T . T Associativity
join . map join = join . join :: f (f (f t)) -> f t
mu . T(eta) = mu . eta = id : T -> T Identity
join . map return = join . return = id :: f t -> f t
The monad type class is then defined
class Functor m => Monad m where
return :: t -> m t
join :: m (m t) -> m t
The canonical mu
implementation of the option monad:
instance Monad Maybe where
return = Just
join (Just m) = m
join Nothing = Nothing
The concat
function
concat :: [[a]] -> [a]
concat (x : xs) = x ++ concat xs
concat [] = []
is the join
of the list monad.
instance Monad [] where
return :: t -> [t]
return = (: [])
(=<<) :: (a -> [b]) -> ([a] -> [b])
(f =<<) = concat . map f
Implementations of join
can be translated from extension form using the equivalence
mu = id* : T . T -> T
join = (id =<<) :: m (m t) -> m t
The reverse translation from mu
to extension form is given by
f* = mu . T(f) : T(X) -> T(Y)
(f =<<) = join . map f :: m a -> m b
But why should a theory so abstract be of any use for programming?
The answer is simple: as computer scientists, we value abstraction! When we design the interface to a software component, we want it to reveal as little as possible about the implementation. We want to be able to replace the implementation with many alternatives, many other ‘instances’ of the same ‘concept’. When we design a generic interface to many program libraries, it is even more important that the interface we choose have a variety of implementations. It is the generality of the monad concept which we value so highly, it is because category theory is so abstract that its concepts are so useful for programming.
It is hardly suprising, then, that the generalisation of monads that we present below also has a close connection to category theory. But we stress that our purpose is very practical: it is not to ‘implement category theory’, it is to find a more general way to structure combinator libraries. It is simply our good fortune that mathematicians have already done much of the work for us!
from Generalising Monads to Arrows by John Hughes