[Haskell-beginners] Suspend/resume computation using Cont monad and callCC
Dmitriy Matrosov
sgf.dma at gmail.com
Tue Mar 12 21:16:05 CET 2013
> On Tue, 12 Mar 2013 12:53:37 +0100
> Ertugrul Söylemez <es at ertes.de> wrote:
>
> > Dmitriy Matrosov <sgf.dma at gmail.com> wrote:
> >
> > I have two functions f and g, and i want them to execute in
> > following order: first function f runs, then suspends and passes
> > control to function g. Function g runs, then suspends and
> > "unpauses" function f. Function f finishes and passes control to
> > function g, which also finishes. Here is illustration ('o' means
> > start of function, dot means suspend and pass control to other
> > function, 'x' means end of function):
> >
> > [...]
> >
> > I want to implement this using Cont monad and callCC.
>
> Not directly answering your question, but what you need is called
> coroutines, and there are better monads for that purpose. This is how
> the Cont monads are defined:
>
> newtype Cont r a = Cont ((a -> r) -> r)
>
> But what you really need here is called a Coroutine monad:
>
> newtype Coroutine f a = Coroutine (Either (f (Coroutine f a)) a)
>
> Don't worry about that scary type, because if you look closely you
> will find that this is just Free as defined in the 'free' package:
>
> data Free f a
> = Free (f (Free f a))
> | Pure a
>
> On Tue, 12 Mar 2013 17:54:16 +0000
> Stephen Tetley <stephen.tetley at gmail.com> wrote:
>
> The resumption monad is even simpler, unfortunately I'm not aware of
> any beginner level tutorials.
>
> William Harrison at the University of Missouri has some papers
> introducing resumption monads but the presentations then move very
> quickly.
Thanks for suggestions! I'll try them (though, i suppose, to understand Free i
should read at least something about category theory first).
Anyway, i think, that i understand why my implementation works so and
can explain it.
First, i want to remind the implementation of callCC (omiting Cont
wrapper):
callCC f = \k -> let g x = \_ -> k x
in (f g) k
So, actually, callCC provides to function f continuation to monad
following callCC itself. This will be the key in my explanation.
Let's start with first question and explain how fT/gT pair works. Here
is the code from my previous mail with line-numbers:
type T r = ContT r (Writer String)
1 fT :: T r ()
2 fT = do
3 lift $ tell "I'm in f-1\n"
4 k' <- callCC gT
5 lift $ tell "I'm in f-2\n"
6 k' undefined
7 lift $ tell "I'm in f-3\n"
8
9 gT :: ((() -> T r ()) -> T r ()) -> T r (() -> T r ())
10 gT k = do
11 lift $ tell "I'm in g-1\n"
12 callCC k
13 lift $ tell "I'm in g-2\n"
14 return (\_ -> return ())
And here is illustrations of first part of execution:
|
v
3 'f-1'
4 k' <- callCC gT <--------------------------------
\ |
-------> 11 'g-1' |
12 callCC k |
/ |
5 'f-2' <-------- |
6 k' -------------> 13 'g-2' |
14 return (\_ -> return ()) ---
I.e. after point 'f-1' function gT is called with continuation k to
line 5. Then after point 'g-1' function gT calls this continuation k
and execution jumps back to line 5 in function f. But because
continuation k have been called in callCC, callCC throws continuation
k' to line 13 (in function g) into continuation k. Then after point
'f-2' function f calls continuation k' to line 13. Function g resumes
execution and finishes. But what is the next continuation now? The one
supplied by (callCC gT) ! And this is again continuation to line 5 in
function f. Here i proceed to second illustration:
3 'f-1'
4 k' <- callCC gT <---------------------------------
5 'f-2' |
6 k' 13 'g-2' |
7 'f-3' 14 return (\_ -> return ()) ---
So, function f executes again from point 'f-2'. And meet continuation
k' again, but now k' is continuation returned by function gT at line
14. I.e. it is just (\_ -> return ()). Thus, it does nothing and i
proceed to point 'f-3'.
This explains track produced by Writer monad. But there is one more
question: why track produced using monad result differs?
Here is the code from my previous mail:
type M r = Cont r
fM :: M r [String]
fM = do
let xs' = "I'm in f-1" : []
(ys, k') <- callCC (gM xs')
let ys' = "I'm in f-2" : ys
zs <- k' ys'
let zs' = "I'm in f-3" : zs
return zs'
gM :: [String]
-> (([String], [String] -> M r [String]) -> M r [String])
-> M r ([String], [String] -> M r [String])
gM xs k = do
let xs' = "I'm in g-1" : xs
ys <- callCC (curry k xs')
let ys' = "I'm in g-2" : ys
return (ys', \_ -> return ys')
And if you "trace" its execution in the same manner, you'll notice the
answer: result of monad fM actually determined by call of continuation
k', which occurs during "second" function fM run. And during this
"second" run continuation k' will be (\_ -> return ys'), where ys' is
from function gM. But when ys' had been evaluated in function gM,
second pass through 'f-2' not yet happen! That's why it is missed from
the result as well.
Ugh, well.. that was useless, but still so fascinating :-)
--
Dmitriy Matrosov
More information about the Beginners
mailing list