[Haskell-cafe] Monads from Functors

Sebastian Fischer sebf at informatik.uni-kiel.de
Thu Apr 9 05:22:54 EDT 2009


Hi all,

thanks for pointing me at the Codensity monad and for mentioning the
lift operation! I'll try to sum up what I learned from this thread.

In short:

What I find interesting is

   1. you can express the results of monadic computations using
      functors that do not themselves support the `>>=` operation. You
      only need an equivalent of `return`.

and

   2. a variety of *non-*monadic effects (e.g., non-determinism) can be
      lifted to this "monad for free", so you get, e.g., a
      non-determinism *monad* even if all you have is a non-determinism
      *functor*.

Here is the long version:

Because `ContT` makes a monad out of anything with kind `* -> *`, we
also get instances for `Functor` and `Applicative` for free. We could
use the `Monad` instance to get them for free by
`Control.Applicative.WrappedMonad` but defining them from scratch
might be insightful, so let's try.

We could define an instance of `Functor` for `ContT t` as follows.

     instance Functor (ContT t)
      where
       fmap = liftM

But let's see what we get if we inline this definition:

     fmap f a
   = liftM f a
   = a >>= return . f
   = ContT (\k -> unContT a (\x -> unContT (return (f x)) k))
   = ContT (\k -> unContT a (\x -> unContT (ContT ($f x)) k))
   = ContT (\k -> unContT a (\x -> k (f x)))

That leads to the `Functor` instance described in the first post on
Kan extensions by Edward Kmett.

 > instance Functor (ContT t)
 >  where
 >   fmap f a = ContT (\k -> unContT a (k.f))

We also get an Applicative instance for free:

     instance Applicative (ContT t)
      where
       pure  = return
       (<*>) = ap

If we inline `ap` we get

     f <*> a
   = f `ap` a
   = f >>= \g -> a >>= \x -> return (g x)
   = ContT (\k1 -> unContT f (\x1 -> unContT (a >>= \x -> return (x1  
x)) k1))
   = ...
   = ContT (\k -> unContT f (\g -> unContT a (\x -> k (g x))))

So, a direct Applicative` instance would be:

 > instance Applicative (ContT t)
 >  where
 >   pure x  = ContT ($x)
 >   f <*> a = ContT (\k -> unContT f (\g -> unContT a (\x -> k (g x))))

The more interesting bits are conversions between the original and the
lifted types. As mentioned earlier, if `f` is a pointed functor, then
we can convert values of type `ContT f a` to values of type `f a`.

     runContT :: Pointed f => ContT f a -> f a
     runContT a = unContT a point

Ryan Ingram pointed in the other direction:

  > You are missing one important piece of the puzzle for ContT:
  >
  > ~~~
  > lift :: Monad m => m a -> ContT m a
  > lift m = ContT $ \k -> m >>= k
  > ~~~
  >
  > This >>= is the bind from the underlying monad.  Without this
  > operation, it's not very useful as a transformer!

That is a fine reason for the *class* declaration of `MonadTrans` to
mention `Monad m` as a constraint for `lift`. But as `ContT t` is a
monad for any `t :: * -> *`, a constraint `Monad t` on the *instance*
declaration for `Monad (ContT t)` is moot. This constraint is not
necessary to define `lift`.

 > instance MonadTrans ContT
 >  where
 >   lift m = ContT (m>>=)

Ryan continues:

  > Without lift, it's quite difficult to get effects from the
  > underlying Applicative *into* ContT.  Similarily, your MonadPlus
  > instance would be just as good replacing with the "free"
  > alternative functor:
  >
  > ~~~
  > data MPlus a = Zero | Pure a | Plus (MPlus a) (MPlus a)
  > ~~~
  >
  > and then transforming MPlus into the desired type after runContT;
  > it's the embedding of effects via lift that makes ContT useful.

Let's see whether I got your point here. If we have a computation

     a :: Monad m => m a

and we have a pointed functor `f`, then we can get the result of the
computation `a` in our functor `f` because `runContT a :: f a`.

If we now have a computation with additional monadic effects (I use
non-determinism as a specific effect, but Edward Kmett shows in his
second post on Kan extensions how to lift other effects like Reader,
State, and IO)

     b :: MonadPlus m => m b

then we have two possibilities to express the result using `f` if `f` is
also an alternative functor.

   1. If `f` is itself a monad (i.e. an instance of MonadPlus), then
      the expression `runContT (lift b)` has type `f b`. (Well, `b`
      itself has type `f b`..)

   2. If `f` is *not* a monad, we can still get the result of `b` in
      our functor `f` (using the `MonadPlus` instance from my previous
      mail), because `runContT b` also has type `f b`.

Clearly, `runContT (lift b)` (or `b` itself) and `runContT b` may
behave differently (even if they both (can) have the type `f b`)
because `ContT` 'overwrites' the definition for `>>=` of `f` if `f`
has one. So it depends on the application which of those behaviours is
desired.

Ryan:

  > The CPS transfrom in ContT also has the nice property that it makes
  > most applications of >>= in the underlying monad be
  > right-associative.

Do you have a specific reason to say *most* (rather than *all*) here?
Because if we have a left-associative application of `>>=` in `ContT`,
then we have:

     (a >>= f) >>= g
   = ContT (\k1 -> unContT (a >>= f) (\x1 -> unContT (g x1) k1))
   = ContT (\k1 ->
       unContT (ContT (\k2 -> unContT a (\x2 -> unContT (f x2) k2)))
               (\x1 -> unContT (g x1) k1)
   = ContT (\k1 -> unContT a (\x2 -> unContT (f x2) (\x1 -> unContT (g  
x1) k1)))
   = ContT (\k1 -> unContT a (\x2 ->
       unContT (ContT (\k2 -> unContT (f x2) (\x1 -> unContT (g x1)  
k2)))
               k1))
   = ContT (\k1 -> unContT a (\x2 -> unContT (f x2 >>= g) k1))
   = a >>= (\x -> f x >>= g)

Cheers,
Sebastian




More information about the Haskell-Cafe mailing list