From Adjunctions to Monads
I thought I would share one of my favorite constructions in
Haskell, namely that adjoint functors give rise to monads.
Although it's a trivial result in category theory how it
manifests in Haskell is quite lovely.
A Functor in Haskell maps objects and morphism (i.e. functions)
in a subcategory of Hask to objects and morphisms of another
category.
class Functor f where
fmap :: (a -> b) -> f a -> f b
And satisfies the functor laws:
fmap id = id
fmap (a . b) = (fmap a) . (fmap b)
In commutative diagrams we draw objects as points and morphisms
as arrows. In a string diagrams we invert this and draw morphisms
as points and objects as lines.
Functor composition is defined for $F : \mathcal{A} \rightarrow
\mathcal{B}$, $G : \mathcal{B} \rightarrow \mathcal{C}$ as $G
\circ F : \mathcal{A} \rightarrow \mathcal{C}$, and is drawn with
parallel lines.
newtype FComp f g a = C { unC :: f (g a) }
instance (Functor f, Functor g) => Functor (FComp f g) where
fmap f (C x) = C (fmap (fmap f) x)
Generally the composition of functors $F \circ G$ is written
simply as $FG$. Composition diagrammatically allows us to
collapse adjacent segments in our string diagram.
The identity functor ( $\text{Id}$ ) is the functor that maps
each morphism and object to itself.
newtype Id a = Identity { unId :: a }
instance Functor Id where
fmap f x = Identity (f (unId x))
Composition with the identity functor forms an identity relation:
$$
F \circ \text{Id}_B = F \
\text{Id}_A \circ F = F
$$
As witnessed by the expressions:
left :: Functor f => FComp f Id a -> f a
left (C a) = fmap unId a
right :: Functor f => f a -> FComp f Id a
right a = C $ fmap Identity a
We'll follow the convention to omit the identity functor, and it is shown as a dotted line in subsequent
string diagrams.
A natural transformation in our context will be a polymorphic function
associated with two Haskell functor instances f
and g
with type
signature (Functor f, Functor g) => forall a. f a -> g a
. Which could be written with
the following type synonym.
type Nat f g = forall a. f a -> g a
The identity natural transform mapping a functor $F$ to itself is
written $1_F$ and in Haskell is just (id
). The composition of
natural transformations follows the associativity laws, shown
below:
The final interchange law states that we can chase the natural
transformations through the functors horizontally or compose
natural transformation between functors vertically and still
arrive at the same result.
$$
(\alpha \beta) \circ (\alpha' \beta') = (\alpha \alpha') \circ (\beta \beta')
$$
type NatComp f f' g g' = forall a. f' (f a) -> g' (g a)
vert :: (Functor f, Functor f', Functor g, Functor g') =>
Nat f' g' -> Nat f g -> NatComp f f' g g'
vert a b x = a (fmap b x)
horiz :: (Functor f, Functor f', Functor g, Functor g') =>
Nat f' g' -> Nat f g -> NatComp f f' g g'
horiz a b x = fmap b (a x)
By the interchange law horiz
and vert
must be
interchangable under composition. For natural transformations
a, b, a', b'
in Haskell we have the equation:
(a . b) `vert` (a' . b') == (a `horiz` a') . (b `horiz` b')
A diagram example for a natural transformation $\eta : 1_\mathcal{C}
\rightarrow {FG}$ between the identity functor and the
composition functor of $FG$ would be drawn as:
An isomorphism $F \cong G$ implies that composition of functors
is invertible in that $F G = \text{Id}_C$ and $G F =
\text{Id}_D$. An adjoint $F ⊣ G$ between a pair of functors $F :
D \rightarrow C$ and $G : C \rightarrow D$ is a weaker statement
that there exists a pair of associated natural transformations
$(F, G, \epsilon, \eta)$ with:
$$
\epsilon : FG \rightarrow 1_\mathcal{C} \
\eta : 1_\mathcal{D} \rightarrow FG
$$
Such that the following triangle identities hold:
$$
(\epsilon F) \circ (F \eta) = 1_F \
(G \epsilon) \circ (\eta G) = 1_G
$$
These are drawn below:
In terms of the categories $C,D$ an adjoint is in some sense a
"half-isomorphism" or "almost inverse" but some structure is lost
in one direction.
$\eta$ and $\epsilon$ are also referred to respectively as the
unit and counit.
In Haskell we have the following typeclass which unfortunately
requires a functional dependency in order for type inferencer to
deduce which fmap
is to be used:
class (Functor f, Functor g) => Adjoint f g | f -> g, g -> f where
eta :: a -> g (f a)
epsilon :: f (g a) -> a
There are also two other natural transformations ($\phi, \psi$)
which together with the adjoint functor pair form an
adjunction. The adjunction can be defined in terms of the
adjoint pair and this is most convenient definition in Haskell
$$
\psi \epsilon = 1_F \
\phi \eta = 1_G
$$
phi :: Adjoint f g => (f a -> b) -> a -> g b
phi f = fmap f . eta
psi :: Adjoint f g => (a -> g b) -> f a -> b
psi f = epsilon . fmap f
Notably $\phi$ and $\psi$ form an isomorphism between the set of
functions (a -> g b)
and (f a -> b)
which is the same
relation as the above triangle identities. Alternatively $\eta$
and $\epsilon$ can be expressed in terms of $\phi$ and $\psi$.
phi eta = id
psi epsilon = id
From the Haskell Prelude we have the canonical adjoint namely
curry
and uncurry
:
$$
\text{curry} \quad ⊣ \quad \text{uncurry}
$$
instance Functor ((,) a) where
fmap f (x,y) = (x, f y)
instance Functor ((->) a) where
fmap f g = \x -> f (g x)
Which we can construction an Adjoint instance from these two
functor instances:
instance Adjoint ((,) a) ((->) a) where
eta x y = (y, x)
epsilon (y, f) = f y
We can check that the triangle equalities hold for this
definition by showing both $(\epsilon F) \circ (F \eta)$ and $(G
\epsilon) \circ (\eta G)$ reduce to the identity natural
transformation ( id
).
a0 :: (a -> (b -> c)) -> a -> (b -> c)
a0 f = \f -> fmap (epsilon . fmap f) . eta
a0 f = fmap (\(y, f) -> g f y) . eta
a0 f = \x y -> f x y
a1 :: ((a, b) -> c) -> (a,b) -> c
a1 f = epsilon . fmap (fmap f . eta)
a1 f = epsilon . fmap (\x y -> f (y, x))
a1 f = \(x, y) -> f (x, y)
We know a Monad is an endofunctor $T : C \rightarrow C$ with two
natural transformations $(T, \mu, \eta)$ with the usual laws:
$$
\mu \circ T \mu = \mu \circ \mu T \
\mu \circ T \eta = \mu \circ \eta T = 1_T \
$$
The geometric intuition is that the monad laws are reflected as
topological properties of the string diagrams. Both $\mu$ and
$\eta$ exhibit reflection symmetry and that we topologically
straighten out $\eta$ to yield the identity functor.
In Haskell we can normally
construct the
Monad type class from an Endofunctor and ($\mu, \eta$) or
join
and return
.
class (Functor t) => Monad t where
eta :: a -> (t a)
mu :: (t (t a)) -> (t a)
(>>=) :: t a -> (a -> t b) -> t b
ma >>= f = mu . fmap f
return = eta
join = mu
What is not immediately apparent though is that every adjoint
pair of functors gives rise to a monad $(T, \mu, \eta)$ over a
category $C$ induced by the composition of the functors to give
rise to a endofunctor and natural transformations in terms of the
unit and counit of the underlying adjunction:
$$
\begin{align}
T &= G \circ F & : &C \rightarrow C \
\mu &= G \epsilon & : &T^2 \rightarrow T \
\end{align}
$$
class Adjoint f g => Monad f g where
muM :: g (f (g (f a))) -> g (f a)
muM = fmap epsilon
etaM :: a -> g (f a)
etaM = eta
(>>=) :: g (f a) -> (a -> g (f b)) -> g (f b)
x >>= f = muM (fmap (fmap f) x)
The geometric intution for this is clear:
From the Monad we can then construct the Kleisli category in the
usual way.
class (Adjoint f g, Category c) => Kleisli c f g where
idK :: c x (g (f x))
(<=<) :: c y (g (f z)) -> c x (g (f y)) -> c x (g (f z))
instance Monad f g => Kleisli (->) f g where
idK = eta
g <=< f = muM . fmap (fmap g) . f
instance Kleisli c f g => Monoid (c a (g (f a))) where
mempty = idK
mappend = (<=<)
In retrospect this is trivial, but importantly leads us to the
more important question: Can we recover an adjunction from a
monad. The answer is Yes...