User:Benmachine/Cont
From HaskellWiki
(→A practical Cont tutorial) |
|||
| Line 4: | Line 4: | ||
<haskell> | <haskell> | ||
| + | contstuff :: Magic | ||
contstuff = do | contstuff = do | ||
thing1 | thing1 | ||
| Line 28: | Line 29: | ||
</haskell> | </haskell> | ||
| - | so the rest of the do-block has an <hask>x</hask> in it that we need to supply (as well as other variables, but <hask>magic</hask> already has access to those). So the rest of the do-block can be thought of as a bit like <hask>a -> r</hask>. Given access to the rest of that do-block, we need to produce something of type <hask>r</hask>. So our lambda has type <hask>(a -> r) -> r</hask> and hence <hask>magic :: (a -> r) -> r -> Magic | + | so the rest of the do-block has an <hask>x</hask> in it that we need to supply (as well as other variables, but <hask>magic</hask> already has access to those). So the rest of the do-block can be thought of as a bit like <hask>a -> r</hask>. Given access to the rest of that do-block, we need to produce something of type <hask>r</hask>. So our lambda has type <hask>(a -> r) -> r</hask> and hence <hask>magic :: (a -> r) -> r -> Magic</hask>... oh but ''this'' looks familiar... |
| - | + | ||
<haskell> | <haskell> | ||
newtype Cont r a = Cont { runCont :: (a -> r) -> r } | newtype Cont r a = Cont { runCont :: (a -> r) -> r } | ||
| + | -- Magic = Cont r a | ||
magic = Cont | magic = Cont | ||
</haskell> | </haskell> | ||
| + | Tada! The moral of the story is, if you got up one morning and said to yourself "I want to stop in the middle of a do-block and play about with the last half of it", then <tt>Cont</tt> is the type you would have come up with. | ||
| - | + | ---- | |
| - | Now you know what the < | + | Now you know what the <tt>Cont</tt> type is, you can implement pretty much all of its type class instances just from there, since the types force you to apply this to that and compose that with this. But that doesn't really help you to understand what's going on: here's a way of using the intuition introduced above to implement `Functor` without thinking about the types too much: |
<haskell> | <haskell> | ||
Revision as of 00:43, 20 January 2012
Contents |
1 A practical Cont tutorial
It seems to me likecontstuff :: Magic 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
newtype Cont r a = Cont { runCont :: (a -> r) -> r } -- Magic = Cont r a magic = Cont
Tada! The moral of the story is, if you got up one morning and said to yourself "I want to stop in the middle of a do-block and play about with the last half of it", then Cont is the type you would have come up with.
Now you know what the Cont type is, you can implement pretty much all of its type class instances just from there, since the types force you to apply this to that and compose that with this. But that doesn't really help you to understand what's going on: here's a way of using the intuition introduced above to implement `Functor` without thinking about the types too much:
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 useret <- 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 to understand withnewtype 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
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.
