User:Benmachine/Cont
From HaskellWiki
Contents |
1 A practical Cont tutorial
It seems to me likeCont
ContT
contstuff :: Magic a contstuff = do thing1 thing2 -- Now I want to manipulate the rest of the computation. -- So I want a magic function that will give me the future to -- play with. magic $ \rest -> -- 'rest' is the rest of the computation. Now I can just do it, -- or do it twice and combine the results, or discard it entirely, -- or do it and then use the result to do it again... it's easy to -- imagine why this might be useful. messAboutWith rest thing3 -- these might get done once, several times, thing4 -- or not at all.
magic
r
magic
r
r
magic
x <- magic $ \rest -> -- ... thingInvolving x
x
magic
a -> r
r
(a -> r) -> r
magic :: (a -> r) -> r -> Magic a
Magic a = Cont r a magic = Cont
Tada!
The thing withCont
Functor
instance Functor (Cont r) where fmap f (Cont g) = -- ...
Cont
fmap f (Cont g) = Cont $ \rest -> -- ...
g
\rest -> stuffWith (rest val)
val
<-
rest
f
fmap f (Cont x) = Cont $ \rest -> x (\val -> rest (f val))
Applicative
instance Applicative (Cont r) where pure x = Cont $ \rest -> -- ...
We don't want to do anything special here. The rest of the computation wants a value, let's just give it one:
pure x = Cont $ \rest -> rest x
<*>
Cont f <*> Cont x = Cont $ \rest -> -- ...
fmap
Cont f <*> Cont x = Cont $ \rest -> f (\fn -> x (\val -> rest (fn val)))
Monad
runCont
case
let
1.1 So what's callCC?
"Call with current continuation". I don't really get the name. Basically, you usecallCC
ret <- callCC $ \exit -> do -- A mini Cont block. -- You can bind things to ret in one of two ways: either return -- something at the end as usual, or call exit with something of -- the appropriate type, and the rest of the block will be ignored. when (n < 10) $ exit "small!" when (n > 100) $ exit "big!" return "somewhere in between!"
exit
runCont
1.2 What about ContT?
The thing to understand withContT
newtype ContT r m a = ContT (Cont (m r) a) deriving (Functor, Applicative, Monad) runContT :: ContT r m a -> (a -> m r) -> m r runContT (ContT m) = runCont m
MonadTrans
1.3 Some real examples
The examples in the mtl doc are unconvincing. They don't do anything genuinely daring. Some of them work in any monad! Here's a more complex example:
-- This tends to be useful. runC :: Cont a a -> a runC c = runCont c id faff :: Integer -> Maybe Integer faff n = runC $ do test <- Cont $ \try -> case try n of Nothing -> try (2*n) res -> fmap (subtract 10) res return $ if test < 10 then Nothing else Just test
return
test = n
(2*n)
Nothing
Just 12
Just 0
1.4 Acknowledgements
I think it was the legendary sigfpe who made this click for me, after thinking about how this works:
and there's also this:
which is more-or-less the above trick but in a bit more detail.
1.5 Disclaimer
I'm currently unsure if I've fallen victim to Brent's (in)famous monad tutorial fallacy. I know that there was more in my learning process than I've been able to reproduce above, but I do think I'm doing this in a genuinely new style –Cont
1.6 A moderately heretical conclusion
Sometimes looking at types isn't the best way to understand things! I've implemented theCont
Cont
