m (→A practical Cont tutorial)
Revision as of 00:04, 20 January 2012
I found a bug with the <hask> tag. I put it on its own page so it doesn't ruin my user page.
1 A practical Cont tutorialIt seems to me like
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.
x <- magic $ \rest -> -- ... thingInvolving x
Magic a = Cont r a magic = Cont
Tada!The thing with
instance Functor (Cont r) where fmap f (Cont g) = -- ...
fmap f (Cont g) = Cont $ \rest -> -- ...
fmap f (Cont x) = Cont $ \rest -> x (\val -> rest (f val))
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 -> -- ...
Cont f <*> Cont x = Cont $ \rest -> f (\fn -> x (\val -> rest (fn val)))
1.1 So what's callCC?"Call with current continuation". I don't really get the name. Basically, you use
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!"
1.2 What about ContT?The thing with
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
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
I think it was sigfpe who made this click for me, after thinking about how this works: Quick and dirty reinversion of control and there's also this: The Mother of all Monads which is more-or-less the above trick but in a bit more detail.