How and why does the Haskell Cont monad work?

Viewed 11013

This is how the Cont monad is defined:

newtype Cont r a = Cont { runCont :: (a -> r) -> r }

instance Monad (Cont r) where
    return a = Cont ($ a)
    m >>= k  = Cont $ \c -> runCont m $ \a -> runCont (k a) c

Could you explain how and why this works? What is it doing?

5 Answers

Trying to complement the other answers:

Nested lambdas are horrible for readability. This is exactly the reason why let... in... and ... where ... exist, to get rid of nested lambdas by using intermediate variables. Using those, the bind implementation can be refactored into:

newtype Cont r a = Cont { runCont :: (a -> r) -> r }

instance Monad (Cont r) where
    return a = Cont ($ a)
    m >>= k  = k a
            where a = runCont m id

Which hopefully makes what is happening clearer. The return implementation boxes value with a lazy apply. Using runCont id applies id to the boxed value, which returns the original value.

For any monad where any boxed value can simply be unboxed, there is generally a trivial implementation of bind, which is to simply unbox the value and apply a monadic function to it.

To get the obfuscated implementation in the original question, first replace k a with Cont $ runCont (k a) , which in turn can be replaced with Cont $ \c-> runCont (k a) c

Now, we can move the where into a subexpression, so that we are left with

Cont $ \c-> ( runCont (k a) c where a = runCont m id )

The expression within parentheses can be desugared into \a -> runCont (k a) c $ runCont m id.

To finish, we use the property of runCont, f ( runCont m g) = runCont m (f.g), and we are back to the original obfuscated expression.

Related