haskellmonadslifting

Use of a monad superclass in a monad instance declaration?


I'm implementing a very simple poor mans concurrency structure with the following data type:

data C m a = Atomic (m (C m a)) | Done a

I'm creating an instance of monad for this:

instance Monad m => Monad (C m) where
  (>>=) (Atomic m) f      = Atomic $ (liftM (>>= f) m)
  (>>=) (Done v) f        = f v
  return                  = Done

Q1. Am I right in saying Atomic $ (liftM (>>= f) m) is creating a new Atomic monad which contains the result of f (* -> *) applied to the value inside of m?

Q2. Am I right in saying the superclass Monad m is used here to enable the use of liftM? If so, as this is an instance of the Monad class, why can't this access liftM directly?


Solution

  • Q1. It is creating Atomic value. Monad is a mapping at type level. Since f is the second argument of >>= of C m a we know its type

    f :: Monad m => a -> C m b
    

    hence

    (>>= f) :: Monad m => C m a -> C m b
    

    is f extended to unwrap its argument, and

    liftM (>>= f) :: (Monad m1, Monad m) => m1 (C m a) -> m1 (C m b)
    

    simply lifts the transformation into m1 which in your setting is unified with m. When you extract the value m from Atomic and pass it to liftM you use the monad m's bind (via liftM) to extract the inner C m a to be passed to f. The second part of liftM's definition re-wraps the result as a m (C m b) which you wrap in Atomic. So yes.

    Q2. Yes. But it is a liftM of the underlying monad m. The liftM for C m a is defined in terms of the instance (its >>= and return). By using C m a's liftM (if you managed to define >>= in terms of liftM) you would get a cyclic definition. You need the constraint Monad m to create the plumbing for m (C m a) to travel through the >>= and f. In fact as pointed by Benjamin Hodgson, Monad m is an unnecessarily strong constaint, Functor m would suffice. The fact that C is in fact Free together with the relevant implementations give the most insight in the matter.