70

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.)

Petr
  • 62,528
  • 13
  • 153
  • 317
  • 5
    `ST` is the other obvious example, but it also violates your "pure" monad restriction. – Ganesh Sittampalam Jul 01 '14 at 17:23
  • So you're looking for a type T such that there is an `instance Monad (T Identity)` that satisfies the monad laws, but such that T doesn't satisfy the monad transformer laws? – bennofs Jul 01 '14 at 17:27
  • @GaneshSittampalam Good point, I added `ST` to the "exclusion list". – Petr Jul 01 '14 at 17:28
  • 2
    @bennofs Yes, but not just the monad transformer laws, it could be that `T` satisfies them, but for some monad `n` the type `T n` fails to be a monad. For example [`ListT`](http://hackage.haskell.org/package/mtl-2.2.1/docs/Control-Monad-List.html#v:ListT) fails to satisfy monad laws for just some monads (non-commutative ones), however, [there is another, correct transformer for `[]`](http://www.haskell.org/haskellwiki/ListT_done_right). – Petr Jul 01 '14 at 17:32
  • 4
    The list of "this is obviously IO" exceptions is basically infinite. There's `STM`, for instance. But there's also every single custom monad that works over IO intrinsically. Lots of libraries provide such things. – Carl Jul 01 '14 at 18:24
  • It's funny how it's very hard to come up with a trivial monad transformer. –  Jul 01 '14 at 20:58
  • The "double-dual reader" `(a -> b) -> b` might be an IO-free candidate (has that any standard name / implementation BTW)? – leftaroundabout Jul 01 '14 at 23:50
  • @leftaroundabout `Cont`? – J. Abrahamson Jul 02 '14 at 00:29
  • 1
    @Rhymoid `newtype Id1 m a = Id1 (m a)`? – J. Abrahamson Jul 02 '14 at 00:30
  • 2
    @leftaroundabout `Cont` is almost the opposite: It's so easy to turn into a transformer that the methods of the `Monad` instance for `ContT m` don't even need the `Monad` instance for `m`. – Ørjan Johansen Jul 02 '14 at 01:51
  • 2
    I feel like a precise definition of what it means for a type to "have a monad" is needed. Why does list "have" both ListT and ListTDoneRight? Can this be answered by appealing to free monad transformers? Something like `T` with `Monad T` such that `T' Identity ~ T` but there exists an `n` with `Monad n` such that `T' n` no longer instantiates `Monad` or such that `T'` isn't `MonadTrans`. There might be many such `T'` though (as ListT points out). All of them are under consideration for any given `T`, though. – J. Abrahamson Jul 02 '14 at 03:35
  • That doesn't seem right offhand, though, as it's easy to solve. At least the transformer part: `data I a = I () a` is a monad, is isomorphic to `I' Identity` for `data I' m a = I' (m ()) a`. But `I'` definitely isn't a monad transformer. – J. Abrahamson Jul 02 '14 at 03:37
  • 9
    Any Monad that can be expressed as a Free monad for some functor (which is almost all of them: http://stackoverflow.com/questions/14641864/what-monads-can-be-expressed-as-free-over-some-functor) should also get the free monad transformer. – Cirdec Jul 02 '14 at 04:43
  • 1
    @Cirdec This is a very good point. If a monad can be constructed as a free monad of some functor, then the same functor can be used to construct its corresponding monad transformer. So it seems the only candidates are monads that can't be constructed this way. – Petr Jul 02 '14 at 05:36
  • @PetrPudlák: non-constructive, but since all monads can be written in terms of `Cont`, and we know how to make `ContT`, all monads have a corresponding transformer. – John L Jul 02 '14 at 05:51
  • @JohnL I think that the simple way of embedding a monad in `Cont` is isomorphic to using `ContT` as a transformer on it, and there isn't then any room left for making the *result* of that transform yet another monad. – Ørjan Johansen Jul 02 '14 at 08:29
  • @J.Abrahamson Not what I meant. `Id1 Id` is isomorphic to `Id` only. I was thinking about `TT m`, a transformer corresponding to *any* `m` (for all monads `m`: `TT m Id ~ m`, etc.). –  Jul 02 '14 at 09:25
  • @Rhymoid I'm not seeing how that differs: `Id1 m ~ m` for all monads. If you want to slot an `Id` in there you could look at `newtype Compose f g a = Compose (f (g a))`? – J. Abrahamson Jul 02 '14 at 13:52
  • @J.Abrahamson: `Id1 m ~ m` is not enough to make `Id1` or `Id1 m` a transformer corresponding to `m`, according to OP's definition. –  Jul 02 '14 at 14:09
  • @JohnL Sounds promising, could you elaborate on that? For example how could one express `[]` using `Cont` (not `ContT`)? – Petr Jul 02 '14 at 18:01
  • @PetrPudlák Something like `\xs -> ContT (\f -> Identity (xs >>= runIdentity . f)` and `runIdentity . ($ Identity . return) . runContT`? –  Jul 19 '14 at 16:18
  • @Cirdec's point (which I came to independently in [a duplicate](https://stackoverflow.com/questions/50260591/what-is-an-explicit-example-of-a-monad-without-a-monad-transformer?noredirect=1&lq=1)) means that there actually *are* monad transformers corresponding to `IO` and `ST s`, although the correspondence cannot be expressed fully in Haskell. I don't know if these constructions have any practical value. – dfeuer May 11 '18 at 16:41
  • @PetrPudlák I think I have an example of a monad with two inequivalent transformers. My example is the "search monad" `S p` defined by `type S p a = (a -> p) -> a` has a transformer `t1 n a = (n a -> p) -> n a` and a transformer `t2 n a = (a -> n p) -> n a`. Both transformers have `lift`. Also, `t1 Identity` is isomorphic to `t2 Identity` and to `S p`. However, `t2` is not functorial in the foreign monad `n`, while `t1` is. See also the recently added item 5 in my answer below. – winitzki Jun 27 '19 at 21:40

4 Answers4

21

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 functors F_k and G_k such that F_k is left adjoint to G_k and that m is isomorphic to G_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 that t_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. Defining return for this functor is not difficult since F_k is a "pointed" functor, and defining join should be possible since extract from the comonad F_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 type G_k * n * n * F_k, which is then further reduces via join from n.

We do have to be a bit careful since F_k and G_k are not endofunctors on Hask. So, they are not instances of the standard Functor typeclass, and also are not directly composable with n as shown above. Instead we have to "project" n into the Kleisli category before composition, but I believe return from m 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 for lift, return, and join with a similar dependency on extract from the comonad F_em * G_em.

duplode
  • 33,731
  • 7
  • 79
  • 150
  • 1
    A very nice idea. Could you perhaps add an example, deriving some well-known monad transformer this way? – Petr Jul 20 '14 at 06:35
  • 1
    http://stackoverflow.com/a/4702513/2008899 builds the State monad from an adjunction. If instead you were trying to build StateT from that adjunction, there are at least 3 possible ways to compose the functors with different corresponding definitions of StateT s IO: `StateT s IO a ~ s -> (IO a, s)` (r * w * m), `StateT s IO a ~ IO (s -> (a, s))` (m * r * w), and `StateT s IO a ~ s -> IO (a, s)` (r * m * w). The last, correct, construction is how I propose to build a transformer from an adjunction. – Boyd Stephen Smith Jr. Jul 20 '14 at 21:08
  • 2
    Granted bounty to this answer, since it's the only *rigorous* answer that hasn't been disproven. Still, more elaboration would be awesome :) –  Jul 21 '14 at 21:22
  • @Rhymoid I'd gladly transfer the bounty to someone else if they can prove (or disprove) my intuition, and I will continue to try and work through a complete proof myself. – Boyd Stephen Smith Jr. Jul 21 '14 at 21:28
  • 1
    I got some time to try to elaborate on your answer. All looks good, but interestingly so far I haven't been able to implement `lift` from `MonadTrans`. Any ideas? – Petr Mar 31 '15 at 18:37
  • @PetrPudlák lift {t_mk m} = fmap {G_k} . fmap {m} (return/pure/point {F_k}) ? Just guessing. – Boyd Stephen Smith Jr. Apr 02 '15 at 06:41
  • Can you elaborate on your idea about "projecting" into the Kleisli category? I don't see how it could work. – Turion Jun 14 '16 at 10:03
  • @Turion basically instead of operating on values of type `n a` we would need to operate on values of type `a -> F_k (n a)`. When we talk about monads as monoids in the category of endofunctors the monoidal values are really these "decorated" endofunctions. – Boyd Stephen Smith Jr. Jun 15 '16 at 15:12
  • I can't follow that at all. (What are "monoidal values" of a monad? The multiplication of the monad is `join`.) Basically, my question is, given an endofunctor, how do you make an endofunctor in the Kleisli category out of it? – Turion Jun 16 '16 at 09:08
  • `return` stays in `Hask`, so how would construct your "projection"? – Turion Jun 16 '16 at 09:11
  • I'll try to be a bit more elaborate in my concern. Let's say the endofunctor is called `n`. We want to have a functor `n_k` on the Kleisli category. Since the Kleisli category has the same objects, we say `n_k` has the same function on objects like `n`. Now what do we do with a Kleisli arrow? Given a value of type `a -> n b`, what do we do with it? One could think that we use the comonad to extract a `b`, then `fmap`, then compose with `return`. But the comonad is only defined on the Kleisli category, it's not in `Hask`! – Turion Jun 16 '16 at 09:17
  • @Turion The monoidal operation of a monad is NOT join. It doesn't have the right arity. The monodial operation of a monad is the Kleisli fish: (>=>) :: (a -> m b) -> (b -> m c) -> a -> m c restricted to the operating on the Kleisli decorated endofuntions a -> m a. – Boyd Stephen Smith Jr. Jun 16 '16 at 14:59
  • 1
    Ok, I hadn't ever heard the term "monoidal operation" before. (I thought you meant the product of the monad as a monoid in the category of endofunctors.) So nevermind. Still, I don't see how your "projection" would work. Why would any functor need to work on endofunctions of the Kleisli category? – Turion Jun 17 '16 at 10:34
  • @BoydStephenSmithJr. As far as I know, *only* the StateT and ContT can be built in this way (via adjunctions). The question remains - how to obtain the functors F_k, F_em etc. in practice, given a monad's the source code? For example, `type P a = Either (a, a) (a, a, Maybe a)` is a monad. How to find F_k, G_k, F_em, G_em for it, what is the procedure? Same question for `type Q a = ((u -> a) -> v) -> u -> a`. I also heard that Eilenberg-Moore decomposition is not unique. So, we could have multiple F_em, G_em for a given monad. What to do? Are all these monad transformers are valid? – winitzki May 11 '18 at 15:55
  • @winitzki Only `StateT` and `ContT` can be build from an adjunction _of endofunctors on Hask_. All monads are built from an underlying adjunction, but in general that adjunction is of functors between different categories and so not straightforwardly expressible in Haskell. Re non-uniqueness of the decomposition: I might guess (but have not tried to prove) that all of those decompositions produce the same monad transformer (up to monad isomorphism). I believe the EM-decomposition per se _is_ unique, so it's just that there are other decompositions besides the EM one. – Benjamin Hodgson May 11 '18 at 16:43
  • @BenjaminHodgson Yes, that's what I meant to say. Only something that's expressible in code has relevance to practical programming. It is fine for academic papers if we show that some monad "exists" in some imaginary category we "defined", but it's not useful for programming if we can't write code for that in Haskell or another programming language. Suppose that my business logic requires me to have `Either (a, a) (a, a, Maybe a)` as the data type of a monad, and I need to construct a monad transformer for this monad. What do I do? Knowing about the "existence" of EM construction doesn't help. – winitzki May 11 '18 at 16:57
  • I feel like someone should work out the full details and turn this answer into a paper. – dfeuer May 11 '18 at 17:02
  • @winitzki, a non-constructive existence proof tells you that you're probably not wasting your time when you're pulling your hair out trying to find the corresponding transformer. The question shifts from "Is there a transformer corresponding to this?" to "What is the transformer corresponding to this?" – dfeuer May 11 '18 at 17:05
  • 1
    My impression, looking at the EM and the K constructions, is that we have no proof of existence of monad transformers. The EM and the K constructions give the initial and the final objects in the category of adjunctions that generate a given monad - okay. When translated into Haskell code, they correspond to monad transformers that are "inside" or "outside". E.g., the ReaderT is "outside" because `ReaderT z m a = Reader z (m a)` while the EitherT is "inside" because `EitherT z m a = m (Either z a)`. Only one of the "inside" and the "outside" transformers is valid. Theory doesn't say which one! – winitzki May 22 '18 at 20:39
  • @BoydStephenSmithJr. I'm not really sure I'm understanding what you're saying, but the append operation of a monad (as defined as a monoid object in a monoidal category) is definitely join. It only has the wrong arity if we mistakenly seek a binary operation with respect to tuple (which is the wrong tensor), instead of with respect to functor composition. – Asad Saeeduddin Jan 13 '20 at 18:14
  • Regarding the answer itself: is it really always possible to find two adjoint *endo*functors `F` and `G` such that their composition induces a given monad `GF`? If we use the Kleisli construction, the functors are instead to and from the Kleisli category, and so what you'd be transforming is a monad in the Kleisli category to a monad in Hask. If we are fine with this loosening of the idea of a `Monad` transformer, we can look at Ed Kmett's construction where he shows a "comonad to monad" transformer: http://comonad.com/reader/2011/monads-from-comonads/ – Asad Saeeduddin Jan 13 '20 at 18:30
  • "is it really always possible to find two adjoint endofunctors F and G" No, it is not, and the answer mentions this: "We do have to be a bit careful since F_k and G_k are not endofunctors on Hask." @AsadSaeeduddin – Boyd Stephen Smith Jr. Jan 15 '20 at 17:57
3

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 Monad m, then we could say that the language m is being extended with additional operations available via t. The Identity monad is the language with no effects/operations, so applying t to Identity will just get you a language with only the operations provided by t.

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.

Dan Burton
  • 53,238
  • 27
  • 117
  • 198
2

Previously, I thought I found examples of explicitly defined monads without a transformer, but those examples were incorrect.

The transformer for Either a (z -> a) is m (Either a (z -> m a), where m is an arbitrary foreign monad. The transformer for (a -> n p) -> n a is (a -> t m p) -> t m a where t m is the transformer for the monad n.

  1. The free pointed monad.

The monad type constructor L for this example is defined by

  type L z a  = Either a (z -> a)

The intent of this monad is to embellish the ordinary reader monad z -> a with an explicit pure value (Left x). The ordinary reader monad's pure value is a constant function pure x = _ -> x. However, if we are given a value of type z -> a, we will not be able to determine whether this value is a constant function. With L z a, the pure value is represented explicitly as Left x. Users can now pattern-match on L z a and determine whether a given monadic value is pure or has an effect. Other than that, the monad L z does exactly the same thing as the reader monad.

The monad instance:

  instance Monad (L z) where
     return x = Left x
     (Left x) >>= f = f x
     (Right q) >>= f = Right(join merged) where
        join :: (z -> z -> r) -> z -> r
        join f x = f x x -- the standard `join` for Reader monad
        merged :: z -> z -> r
        merged = merge . f . q -- `f . q` is the `fmap` of the Reader monad
        merge :: Either a (z -> a) -> z -> a 
        merge (Left x) _ = x
        merge (Right p) z = p z

This monad L z is a specific case of a more general construction, (Monad m) => Monad (L m) where L m a = Either a (m a). This construction embellishes a given monad m by adding an explicit pure value (Left x), so that users can now pattern-match on L m to decide whether the value is pure. In all other ways, L m represents the same computational effect as the monad m.

The monad instance for L m is almost the same as for the example above, except the join and fmap of the monad m need to be used, and the helper function merge is defined by

    merge :: Either a (m a) -> m a
    merge (Left x) = return @m x
    merge (Right p) = p

I checked that the laws of the monad hold for L m with an arbitrary monad m.

This construction gives the free pointed functor on the given monad m. This construction guarantees that the free pointed functor on a monad is also a monad.

The transformer for the free pointed monad is defined like this:

  type LT m n a = n (Either a (mT n a))

where mT is the monad transformer of the monad m (which needs to be known).

  1. Another example:

type S a = (a -> Bool) -> Maybe a

This monad appeared in the context of "search monads" here. The paper by Jules Hedges also mentions the search monad, and more generally, "selection" monads of the form

 type Sq n q a = (a -> n q) -> n a

for a given monad n and a fixed type q. The search monad above is a particular case of the selection monad with n a = Maybe a and q = (). The paper by Hedges claims (without proof, but he proved it later using Coq) that Sq is a monad transformer for the monad (a -> q) -> a.

However, the monad (a -> q) -> a has another monad transformer (m a -> q) -> m a of the "composed outside" type. This is related to the property of "rigidity" explored in the question Is this property of a functor stronger than a monad? Namely, (a -> q) -> a is a rigid monad, and all rigid monads have monad transformers of the "composed-outside" type.

  1. Generally, transformed monads don't themselves automatically possess a monad transformer. That is, once we take some foreign monad m and apply some monad transformer t to it, we obtain a new monad t m, and this monad doesn't have a transformer: given a new foreign monad n, we don't know how to transform n with the monad t m. If we know the transformer mT for the monad m, we can first transform n with mT and then transform the result with t. But if we don't have a transformer for the monad m, we are stuck: there is no construction that creates a transformer for the monad t m out of the knowledge of t alone and works for arbitrary foreign monads m.

However, in practice all explicitly defined monads have explicitly defined transformers, so this problem does not arise.

  1. @JamesCandy's answer suggests that for any monad (including IO?!), one can write a (general but complicated) type expression that represents the corresponding monad transformer. Namely, you first need to Church-encode your monad type, which makes the type look like a continuation monad, and then define its monad transformer as if for the continuation monad. But I think this is incorrect - it does not give a recipe for producing a monad transformer in general.

Taking the Church encoding of a type a means writing down the type

 type ca = forall r. (a -> r) -> r

This type ca is completely isomorphic to a by Yoneda's lemma. So far we have achieved nothing other than made the type a lot more complicated by introducing a quantified type parameter forall r.

Now let's Church-encode a base monad L:

 type CL a = forall r. (L a -> r) -> r

Again, we have achieved nothing so far, since CL a is fully equivalent to L a.

Now pretend for a second that CL a a continuation monad (which it isn't!), and write the monad transformer as if it were a continuation monad transformer, by replacing the result type r through m r:

 type TCL m a = forall r. (L a -> m r) -> m r

This is claimed to be the "Church-encoded monad transformer" for L. But this seems to be incorrect. We need to check the properties:

  • TCL m is a lawful monad for any foreign monad m and for any base monad L
  • m a -> TCL m a is a lawful monadic morphism

The second property holds, but I believe the first property fails, - in other words, TCL m is not a monad for an arbitrary monad m. Perhaps some monads m admit this but others do not. I was not able to find a general monad instance for TCL m corresponding to an arbitrary base monad L.

Another way to argue that TCL m is not in general a monad is to note that forall r. (a -> m r) -> m r is indeed a monad for any type constructor m. Denote this monad by CM. Now, TCL m a = CM (L a). If TCL m were a monad, it would imply that CM can be composed with any monad L and yields a lawful monad CM (L a). However, it is highly unlikely that a nontrivial monad CM (in particular, one that is not equivalent to Reader) will compose with all monads L. Monads usually do not compose without stringent further constraints.

A specific example where this does not work is for reader monads. Consider L a = r -> a and m a = s -> a where r and s are some fixed types. Now, we would like to consider the "Church-encoded monad transformer" forall t. (L a -> m t) -> m t. We can simplify this type expression using the Yoneda lemma,

 forall t. (x -> t) -> Q t  = Q x

(for any functor Q) and obtain

 forall t. (L a -> s -> t) -> s -> t
 = forall t. ((L a, s) -> t) -> s -> t
 = s -> (L a, s)
 = s -> (r -> a, s)

So this is the type expression for TCL m a in this case. If TCL were a monad transformer then P a = s -> (r -> a, s) would be a monad. But one can check explicitly that this P is actually not a monad (one cannot implement return and bind that satisfy the laws).

Even if this worked (i.e. assuming that I made a mistake in claiming that TCL m is in general not a monad), this construction has certain disadvantages:

  • It is not functorial (i.e. not covariant) with respect to the foreign monad m, so we cannot do things like interpret a transformed free monad into another monad, or merge two monad transformers as explained here Is there a principled way to compose two monad transformers if they are of different type, but their underlying monad is of the same type?
  • The presence of a forall r makes the type quite complicated to reason about and may lead to performance degradation (see the "Church encoding considered harmful" paper) and stack overflows (since Church encoding is usually not stack-safe)
  • The Church-encoded monad transformer for an identity base monad (L = Id) does not yield the unmodified foreign monad: T m a = forall r. (a -> m r) -> m r and this is not the same as m a. In fact it's quite difficult to figure out what that monad is, given a monad m.

As an example showing why forall r makes reasoning complicated, consider the foreign monad m a = Maybe a and try to understand what the type forall r. (a -> Maybe r) -> Maybe r actually means. I was not able to simplify this type or to find a good explanation about what this type does, i.e. what kind of "effect" it represents (since it's a monad, it must represent some kind of "effect") and how one would use such a type.

  • The Church-encoded monad transformer is not equivalent to the standard well-known monad transformers such as ReaderT, WriterT, EitherT, StateT and so on.

It is not clear how many other monad transformers exist and in what cases one would use one or another transformer.

  1. One of the questions in the post is to find an explicit example of a monad m that has two transformers t1 and t2 such that for some foreign monad n, the monads t1 n and t2 n are not equivalent.

I believe that the Search monad provides such an example.

 type Search a = (a -> p) -> a

where p is a fixed type.

The transformers are

 type SearchT1 n a = (a -> n p) -> n a
 type SearchT2 n a = (n a -> p) -> n a

I checked that both SearchT1 n and SearchT2 n are lawful monads for any monad n. We have liftings n a -> SearchT1 n a and n a -> SearchT2 n a that work by returning constant functions (just return n a as given, ignoring the argument). We have SearchT1 Identity and SearchT2 Identity obviously equivalent to Search.

The big difference between SearchT1 and SearchT2 is that SearchT1 is not functorial in n, while SearchT2 is. This may have implications for "running" ("interpreting") the transformed monad, since normally we would like to be able to lift an interpreter n a -> n' a into a "runner" SearchT n a -> SearchT n' a. This is possibly only with SearchT2.

A similar deficiency is present in the standard monad transformers for the continuation monad and the codensity monad: they are not functorial in the foreign monad.

winitzki
  • 3,179
  • 24
  • 32
  • Whether what I wrote was right or not is beside the issue. I feel sure that where I got my answer was from this question which predates it https://math.stackexchange.com/questions/1560793/can-every-monad-give-rise-to-a-monad-transformer and the point is I don’t have the qualification to be able to understand it. Perhaps I should not have inserted myself into this discussion. – James Candy Dec 18 '19 at 12:24
1

My solution exploits the logical structure of Haskell terms etc.

I looked at right Kan extensions as possible representations of the monad transformer. As everyone knows, right Kan extensions are limits, so it makes sense that they should serve as universal encoding of any object of interest. For monadic functors F and M, I looked at the right Kan extension of MF along F.

First I proved a lemma, "rolling lemma:" a procomposed functor to the Right kan extension can be rolled inside it, giving the map F(Ran G H) -> Ran G(FH) for any functors F, G, and H.

Using this lemma, I computed a monadic join for the right Kan extension Ran F (MF), requiring the distributive law FM -> MF. It is as follows:

Ran F(MF) . Ran F(MF) [rolling lemma] =>
  Ran F(Ran F(MF)MF) [insert eta] =>
  Ran F(Ran F(MF)FMF) [gran] =>
  Ran F(MFMF) [apply distributive law] =>
  Ran F(MMFF) [join Ms and Fs] =>
  Ran F(MF).

What seems to be interesting about this construction is that it admits of lifts of both functors F and M as follows:

(1) F [lift into codensity monad] =>
  Ran F F [procompose with eta] =>
  Ran F(MF).

(2) M [Yoneda lemma specialized upon F-] =>
  Ran F(MF).

I also investigated the right Kan extension Ran F(FM). It seems to be a little better behaved achieving monadicity without appeal to the distributive law, but much pickier in what functors it lifts. I determined that it will lift monadic functors under the following conditions:

1) F is monadic.

2) F |- U, in which case it admits the lift F ~> Ran U(UM). This can be used in the context of a state monad to "set" the state.

3) M under certain conditions, for instance when M admits a distributive law.

James Candy
  • 439
  • 5
  • 10
  • What is the exact definition of "monadified"? What is the purpose of having a strict list monad? Can you give an example, like how having the instance `Monad []` leads to the corresponding definition and instance of the corresponding monad transformer? How does cut elimination help to get there? – Petr Aug 03 '15 at 18:47
  • @PetrPudlák, @user2817408 If "monadification" is understood as "converting the source code of `p :: a` into `p' :: m a`" then I have a sort of a counterexample where this cannot be done. I am quoting from https://stackoverflow.com/questions/39649497/is-this-property-of-a-functor-stronger-than-a-monad: Suppose we have a program `p :: a` that internally uses a function `f :: b -> c`. Now, suppose we want to replace `f` by `f' :: b -> m c` for some monad `m`, so that the program `p` will become monadic as well: `p' :: m a`. Our task is to refactor `p` into `p'`. This can't work for arbitrary `m`. – winitzki May 11 '18 at 16:06
  • @user2817408 Is there an algorithm that would find a monad transformer from an arbitrary given type constructor and a given implementation of a monad instance via pure functions? Let's limit ourselves to type constructors made out of any combinations of `->`, tuple, and `Either`. E.g. given the code `type F a = r -> Either (a, a) (a, a, Maybe a)` and an implementation of a monad instance for `F`, to find the code for the monad transformer `FT`. – winitzki May 11 '18 at 16:14
  • I have one more thing to add. While the restriction to rigidity explained by winitzki does prevent the monad transformer from being constructed in general, there's a trick using Kan extensions that allows you to get monad transformers for other monads, at the price of accepting a Codensity-type structure along with it. Code is here https://pastebin.com/3A3Yd252 – James Candy Jun 03 '18 at 20:10
  • @JamesCandy Your pastebin code is interesting, but I am a bit confused because I am not familiar with stuff like `O` and `unO`. Is your construction, `Lift g`, a general monad transformer for any `Monad g` that satisfies the laws? – winitzki Aug 09 '18 at 23:25
  • @JamesCandy After staring at your pastebin code for a while, I'm still confused about what exactly it does. `Lift g f` seems to take a `Functor f`, not a `Monad f` as I would expect. So, in effect, you are constructing a new monad, `Lift g f`, out of an existing monad `g` and an arbitrary functor `f`? Also, could you please explain what `f ((:.) f g)` means. I can't figure out this syntax. – winitzki Aug 12 '18 at 04:54
  • This construction was based on the construction of a monad for Codensity, which works unconditionally on any type constructor f. Further investigation has shown that monadicity for this construction requires the extra condition h . runRan h2 = runRan h2 . (h . ), which is a pretty natural condition to put. – James Candy Aug 14 '18 at 18:45
  • @JamesCandy I looked again at your formula for Church-encoded monad transformer. It seems that what you claim is that for any monad `L a`, the type constructor `(forall b. L a -> m b) -> m b)` represents the L-transformed monad `m`. Indeed it is a monad and both `L a` and `m a` can be lifted to it. This is very interesting because this gives a general type formula for a monad transformer for an arbitrary monad L. However, when I set `L = Maybe` and `m a = r -> a` and simplify, I don't get the standard monad transformer `MaybeT` applied to `Reader` monad. Is this a different monad transformer? – winitzki Jan 15 '19 at 03:26
  • With the form I presented, x:: Ran f(f:.g), it is possible to calculate a term in the composition f:.g by taking runRan x return. The same is true for the form you presented in your most recent comment, because it is possible to make the variable instantiation b = L a, and again apply return to get a term in m(L a), which in this case equals r->Maybe a. After doing some more digging, I found out that the form (forall b. (L a -> f b) -> f b) is very closely related to the Church-encoded free monad transformer; if so this could explain why it is possible to get a transformer. – James Candy Jan 17 '19 at 19:27
  • I replaced the body of my answer with a new answer reflecting this discussion. – James Candy Sep 16 '19 at 18:11
  • @JamesCandy Your construction of monadic `join` for Ran F (M F) seems to need a crucial step where you replace `M F M F` by `M F` using the distributive law `F M -> M F`. However, you did not check that the laws of the monad hold. I believe that once you require the associativity law, you will find that you need `M F` to be itself a monad. The distributive law `F M -> M F` does not automatically guarantee the associativity law for `M F`. I think if we assume enough laws to get a lawful `join` out of `M F M F -> M F`, we will find that `M F` is already a monad transformer; no need for `Ran`. – winitzki Dec 24 '19 at 16:19