So far, every monad (that can be represented as a data type) that I have encountered had a corresponding monad transformer, or could have one. Is there such a monad that can't have one? Or do all monads have a corresponding transformer?
By a transformer t
corresponding to monad m
I mean that t Identity
is isomorphic to m
. And of course that it satisfies the monad transformer laws and that t n
is a monad for any monad n
.
I'd like to see either a proof (ideally a constructive one) that every monad has one, or an example of a particular monad that doesn't have one (with a proof). I'm interested in both more Haskell-oriented answers, as well as (category) theoretical ones.
As a follow-up question, is there a monad m
that has two distinct transformers t1
and t2
? That is, t1 Identity
is isomorphic to t2 Identity
and to m
, but there is a monad n
such that t1 n
is not isomorphic to t2 n
.
(IO
and ST
have a special semantics so I don't take them into account here and let's disregard them completely. Let's focus only on "pure" monads that can be constructed using data types.)
I'm with @Rhymoid on this one, I believe all Monads have two (!!) transformers. My construction is a bit different, and far less complete. I'd like to be able to take this sketch into a proof, but I think I'm either missing the skills/intuition and/or it may be quite involved.
Due to Kleisli, every monad (
m
) can be decomposed into two functorsF_k
andG_k
such thatF_k
is left adjoint toG_k
and thatm
is isomorphic toG_k * F_k
(here*
is functor composition). Also, because of the adjunction,F_k * G_k
forms a comonad.I claim that
t_mk
defined such thatt_mk n = G_k * n * F_k
is a monad transformer. Clearly,t_mk Id = G_k * Id * F_k = G_k * F_k = m
. Definingreturn
for this functor is not difficult sinceF_k
is a "pointed" functor, and definingjoin
should be possible sinceextract
from the comonadF_k * G_k
can be used to reduce values of type(t_mk n * t_mk n) a = (G_k * n * F_k * G_k * n * F_k) a
to values of typeG_k * n * n * F_k
, which is then further reduces viajoin
fromn
.We do have to be a bit careful since
F_k
andG_k
are not endofunctors on Hask. So, they are not instances of the standardFunctor
typeclass, and also are not directly composable withn
as shown above. Instead we have to "project"n
into the Kleisli category before composition, but I believereturn
fromm
provides that "projection".I believe you can also do this with the Eilenberg-Moore monad decomposition, giving
m = G_em * F_em
,tm_em n = G_em * n * F_em
, and similar constructions forlift
,return
, andjoin
with a similar dependency onextract
from the comonadF_em * G_em
.My solution exploits the logical structure of Haskell terms. Everyone knows that a function in Haskell with return type t can be turned into a monadic function with return type (Monad m) => m t. Therefore if the "bind" function could have its program text "monadified" appropriately, the result would be a monad transformer.
The sticking point is that there is no reason why the "monadification" of the "bind" operator should satisfy the laws, particularly associativity. This is where cut-elimination comes in. The cut elimination theorem has the effect on a program text of inlining all let-bindings, case-analyses, and applications. Also, all computations on a particular term end up being performed in one place.
Since the type parameters of "bind" are rigid, the uses of the right-hand side of "bind" are indexed by their return values. The terms end up in positions in the returned structure that make "bind" associate, therefore the uses of the right-hand side must be associative in the "monadified" "bind," and the resulting structure is a monad.
This is really wooly, so here is an example. Consider the following strict list monad:
This order of evaluation leads to the standard ListT (not really a monad). However, by cut elimination:
This provides the exact evaluation order to be "monadified."
In response to Petr Pudlak:
If the type of the monad in question is some function type (it is convenient to Church-encode all data types), then the function type is converted by decorating all return values of the type with the transformed monad. This is the type portion of monadification. The value portion of monadification lifts pure functions using "return," and combines them with uses of inhabitants of the monad type using "bind," preserving the evaluation order of the original program text.
The strict list monad was given as an example of an evaluation order that does not compose associatively, as witnessed by the fact that ListT uses the same evaluation order as the strict list monad.
To complete the example, the Church encoding of the list monad is:
Monadified, it becomes:
To elaborate on the above, the cut elimination step forces all values to be consumed using a stack discipline, ensuring that the order of results in the structure matches the evaluation order -- this comes at the price of potentially running the same action many times.
[Technically, what you need is a cut elimination of approximants: A is a cut elimination (of approximants) of B, iff for every finite approximant of B, there is a finite approximant of A such that A is a cut elimination of B.]
I hope that helps.
Here's a hand-wavy I'm-not-quite-sure answer.
Monads can be thought of as the interface of imperative languages.
return
is how you inject a pure value into the language, and>>=
is how you splice pieces of the language together. The Monad laws ensure that "refactoring" pieces of the language works the way you would expect. Any additional actions provided by a monad can be thought of as its "operations."Monad Transformers are one way to approach the "extensible effects" problem. If we have a Monad Transformer
t
which transforms a Monadm
, then we could say that the languagem
is being extended with additional operations available viat
. TheIdentity
monad is the language with no effects/operations, so applyingt
toIdentity
will just get you a language with only the operations provided byt
.So if we think of Monads in terms of the "inject, splice, and other operations" model, then we can just reformulate them using the Free Monad Transformer. Even the IO monad could be turned into a transformer this way. The only catch is that you probably want some way to peel that layer off the transformer stack at some point, and the only sensible way to do it is if you have
IO
at the bottom of the stack so that you can just perform the operations there.