MonadCont under the hood
From HaskellWiki
m |
m |
||
| Line 1: | Line 1: | ||
| - | This tutorial is a response to the following [http://stackoverflow.com/questions/3322540/tutorial-to-disassemble-the-haskell-cont-monad/3323283#3323283 Stack Overflow question]. There's a short but useful description of <tt>Cont</tt> and <tt>MonadCont</tt> in the [http://hackage.haskell.org/packages/archive/mtl/1.1.0.2/doc/html/Control-Monad-Cont.html Control.Monad.Cont documentation], but it doesn't really describe how the continuation monad | + | This tutorial is a response to the following [http://stackoverflow.com/questions/3322540/tutorial-to-disassemble-the-haskell-cont-monad/3323283#3323283 Stack Overflow question]. There's a short but useful description of <tt>Cont</tt> and <tt>MonadCont</tt> operations in the [http://hackage.haskell.org/packages/archive/mtl/1.1.0.2/doc/html/Control-Monad-Cont.html Control.Monad.Cont documentation], but it doesn't really describe how the continuation monad does its thing. This is an attempt at much more detailed explanation of what Cont and MonadCont are doing under the hood. |
This tutorial assumes a working knowledge of Haskell, though of course it doesn't assume that you understood the implementation of <hask>Control.Monad.Cont</hask> the first time you read it! | This tutorial assumes a working knowledge of Haskell, though of course it doesn't assume that you understood the implementation of <hask>Control.Monad.Cont</hask> the first time you read it! | ||
| Line 68: | Line 68: | ||
Do you see what's happening? (k a) has become part of the continuation that m is given, and m passes its value to k simply by passing its value to its continuation. The <hask>Cont</hask> objects are being created "just in time" to be used, based on the computation so far. | Do you see what's happening? (k a) has become part of the continuation that m is given, and m passes its value to k simply by passing its value to its continuation. The <hask>Cont</hask> objects are being created "just in time" to be used, based on the computation so far. | ||
| - | = | + | =Exploring the Monad= |
Here's a simple example I've cooked up that should help illustrate the monad in action: | Here's a simple example I've cooked up that should help illustrate the monad in action: | ||
| Line 145: | Line 145: | ||
=> "Done: 15" -- apply *; apply finalC to final value! | => "Done: 15" -- apply *; apply finalC to final value! | ||
| - | = | + | =MonadCont and callCC= |
One final extension to this which is frequently used is the <hask>MonadCont</hask> class, which provides a <hask>callCC</hask> operation. <hask>callCC</hask> creates a <hask>Cont</hask> object that invokes the function it's given, but provides a ''second'' continuation to that function that can be invoked to "break out" of the computation and simply pass a value to the continuation that was active when <hask>callCC</hask> was invoked. This function's operation is definitely easier to understand by seeing it in action. Evaluate the following code, replacing the corresponding functions above: | One final extension to this which is frequently used is the <hask>MonadCont</hask> class, which provides a <hask>callCC</hask> operation. <hask>callCC</hask> creates a <hask>Cont</hask> object that invokes the function it's given, but provides a ''second'' continuation to that function that can be invoked to "break out" of the computation and simply pass a value to the continuation that was active when <hask>callCC</hask> was invoked. This function's operation is definitely easier to understand by seeing it in action. Evaluate the following code, replacing the corresponding functions above: | ||
Revision as of 02:51, 24 July 2010
This tutorial is a response to the following Stack Overflow question. There's a short but useful description of Cont and MonadCont operations in the Control.Monad.Cont documentation, but it doesn't really describe how the continuation monad does its thing. This is an attempt at much more detailed explanation of what Cont and MonadCont are doing under the hood.
This tutorial assumes a working knowledge of Haskell, though of course it doesn't assume that you understood the implementation ofContents |
1 Introducing Continuations and the Cont type
Continuations are functions that represent "the remaining computation to do." Their representation here is- takes a continuation as an argument
- does whatever it needs to do
- produces a value of type at the end, presumably by invoking the continuation.r
2 Sequencing Continuation-Style Computations
2.1 Applicative Sequencing
- needs to invokeF3when it's doneC3
- needs to invoke a continuationF2that will invokeC2, which will invokeF3.C3
- needs to invoke a continuationF1which will invokeC1, which will invokeF2, which will invokeF3.C3
What I've described so far is the applicative operation of continuations.
2.2 Extending to Monad
With the- takes a value and produces areturnobject that just passes that value to its continuation.Cont
- takes abindobject, and a function that produces anotherContobject given a value from the first, and chains them together into oneContobject. That object, when invoked, is going to:Cont
- take a single continuation object ,C
- produce an intermediate value,
- use that intermediate value to select/create the next object to invoke,Cont
- invoke that object withContC
- take a single continuation object
3 Understanding the Monad
3.1 Return
The code:
return a = Cont ($ a)
is equivalent to the following code:
return a = Cont $ \c -> c a
3.2 Bind
The code:
m >>= k = Cont $ \c -> runCont m $ \a -> runCont (k a) c
is a terse way of saying the following:
m >>= k = let s c = runCont m c t c = \a -> runCont (k a) c in Cont $ \c -> s (t c)
4 Exploring the Monad
Here's a simple example I've cooked up that should help illustrate the monad in action:
f :: Int -> Cont r Int f x = Cont $ \c -> c (x * 3) g :: Int -> Cont r Int g x = Cont $ \c -> c (x - 2)
BTW, they can be equivalently written as:
f x = return (x * 3) g x = return (x - 2)
where they look very similar to normal functions. I'm writing them longhand to show you explicitly what the functions are doing.
h :: Int -> Cont r Int h x | x == 5 = f x | otherwise = g x
doC :: Cont r Int doC = return 5 >>= h
And we'll invoke it like this:
finalC :: Show a => a -> String finalC x = "Done: " ++ show(x) runCont doC finalC
Let's see if you're right:
let s c = c 5 t c = \a -> runCont (h a) c in Cont $ \c -> s (t c)
runCont doC finalC => runCont (Cont $ \c -> s (t c)) finalC -- unfold doC => s (t finalC) -- simplify with lemma and apply to finalC => (t finalC) 5 -- unfold s => (\a -> runCont (h a) finalC) 5 -- unfold t => runCont (h 5) finalC -- apply \a... to 5 => runCont (f 5) finalC -- unfold h => runCont (Cont $ \c -> c (5*3)) finalC -- unfold f => (\c -> c (5*3)) finalC -- simplify with lemma => finalC (5*3) -- apply \c... to finalC => "Done: 15" -- apply *; apply finalC to final value!
5 MonadCont and callCC
One final extension to this which is frequently used is theh :: Int -> (Int -> Cont r Int) -> Cont r Int h x abort | x == 5 = f x | otherwise = abort (-1) doC n = return n >>= \x -> callCC (\abort -> h x abort) >>= \y -> g y
doC n = return n >>= \x -> callCC (\abort -> h x abort >>= \y -> g y)
callCC f = Cont $ \c -> runCont (f (\a -> Cont $ \_ -> c a )) c
can be written as
callCC f = let backtrack a = Cont $ \_ -> c a in Cont $ \c -> runCont (f backtrack) c
