Haskell monads for the category theorist
A monad in Haskell is about composing almost-compatible morphisms. Typically you’ve got some morphism and then you think of a reason that you’d rather have
where
is a functor. In Haskell, though, you don’t define functors right out: you define type constructors, which are like the map of objects in a functor. You have to define the map of morphisms separately.
To define the map of morphisms, we have to define (aka
) and
. We define
to be the category-theorist’s unit
composed with the input. Applying
to
gives
To compose “half-functored” morphisms like we define
like this: given an input
and a morphism like
bind produces
So a “monad” in Haskell is always the monad for categories, except the lists are of bindable half-functored morphisms like rather than lists of composable morphisms.
A side-effect monad has where
is the data type for the environment on which the morphism is to act. The IO monad is a specific instance of a side-effect monad with the environment being the keyboard queue, disk drive, network, etc.
T(A)=AxE isn’t quite the side effect monad. It’s the “constant environment” monad where E contains what you might call “global data”. For side effects you need a mechanism for updating E, so you’d use T(A)=E->AxE.
Stop programming for a few weeks and I forget everything. Scratch that. You’re right, AxE *is* the side effect monad where you just want to have a writable, but not readable, side effect. E->AxE is the state monad. I just usually think of the state monad as readable and writable side effect.
Why do you say it’s not readable? A function AxE->AxE is read-write (because it can both depend on E and affect E), and we get that from bind (or, equivalently, from T and mu).
When dealing with monads we use ‘bind’ to compose Kleisli arrows A -> T(B).
For the monad T(A)=AxE a Kleisli arrow is an ordinary arrow A->BxE. That can’t read side effects, only write them.
For the monad T(A)=E->AxE a Kleisli arrow is A->T(B) = A->(E->BxE) = AxE -> BxE so it can read and write an E.
Ah, OK, I see now. Thanks!
In fact, this monad arises from the adjunction between the functors
FX = X x E and GX = E -> X
which give the currying relation
hom(X x E, Y) =~ hom(X, E -> Y).
So when I made the mistake of currying above, it wasn’t so far off.