tag:blogger.com,1999:blog-1777990983847811806.post3887548369332504731..comments2014-11-07T09:06:07.021-08:00Comments on Haskell for all: The Continuation MonadGabriel Gonzaleznoreply@blogger.comBlogger15125tag:blogger.com,1999:blog-1777990983847811806.post-50953357834364808812014-01-20T17:32:35.355-08:002014-01-20T17:32:35.355-08:00So, the answer to part 1 is that you would probabl...So, the answer to part 1 is that you would probably need to invoke two separate monad transformer layers (one for each type of continuation) then run each one separately (i.e. provide a separate continuation for each one). I'm not aware of a good way to combine two distinct return types into a single monad.<br /><br />I don't know how to generalize `ContT` into a type class, but I do know how to generalize an equivalent monad into a type class. The idea is that you can actually implement `ContT` using the `Server` type from `pipes`, where you replace every continuation `k` with a call to `respond`. The argument to `respond` is the argument of the continuation and the return value of `respond` is the return value of the continuation. Then you feed the continuation using the `(//>)` operation from `pipes`, which is analogous to `(>>=)` for an indexed continuation monad.<br /><br />In fact, you can actually show that this behaves exactly like an indexed `ListT` monad (where `(>>=)` = `for`/(//>)` and `return` = `yield`/`respond`). `pipes` provides a non-indexed `ListT` monad that you can study to see what I mean.<br /><br />Now, `pipes` currently does not provide a type class for this, but it used to. If you study the `3.*` series of `pipes` you will see that there is a `Proxy` type class which is sort of like the `mtl`-ization of `pipes`. I ended up dropping it in version 4.0, but it's there for you to study.<br /><br />So that's a long-winded way of saying that it is possible to type class this behavior if you implement it in terms of an indexed `ListT` monad and then type class that.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-88946607554030700542014-01-20T14:09:02.138-08:002014-01-20T14:09:02.138-08:001/ You explained how to deal with variable argumen...1/ You explained how to deal with variable arguments, but what about variable return types ?<br /><br />For example, the following function has 2 holes with distinct return types :<br /><br />unitAttack :: Target -> IO ()<br />unitAttack target = do<br /> valid1 <- ???_1 60<br /> valid2 <- isTargetValid target<br /> if (valid1 && valid2)<br /> then ???_2 target<br /> else sayUhOh<br /><br />How would you use the continuation monad in this case ?<br /><br />2/ For many monads (Reader, Writer, Error...), one can generalize any function "f1 :: xxxT m a" into "f2 (Monadxxx m) => m a" ; is it possible to do the same with MonadCont ?<br />I guess it's not since MonadCont is a *single*-parameter typeclass, while ContT is a *multi*-parameter type constructor...<br />Any insight about how it could or why it cannot be possible, would be much appreciated :) .chahinehttp://www.blogger.com/profile/09692382145518936635noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-37563020542167106642013-04-02T12:46:06.838-07:002013-04-02T12:46:06.838-07:00Using the constructor is the right approach for th...Using the constructor is the right approach for that specific example that I gave.<br /><br />To make an analogy, let's draw a parallel with StateT. Let's say I have a function of type:<br /><br /> f :: s -> m (r, s)<br /><br />... and I want to embed that in StateT. The obvious way would be to just wrap it in the StateT constructor, but let's say that I didn't allow myself to use the constructor (or anything equivalent) and restricted myself to using get, lift, and put. Then I would have to write:<br /><br /> do s <- get<br /> (r, s) <- lift (f s)<br /> put s<br /> return r<br /><br />Weird, right? That's basically how awkward it would be to use callCC for something where the ContT constructor is more appropriate.<br /><br />Now, ContT differs from StateT in one important way: in StateT our mental model of state most closely matches the 'get' and 'put' commands, which is why we like to use those more frequently than wrapping things in the StateT constructors. However, with ContT the situation is the opposite. The ContT constructor most closely resembles our mental model for how continuations work, which is why we use it more often than we use callCC.<br /><br />So I hope that explains why ContT is a bit unusual in that idiomatic usage tends to use the constructor more heavily than other monad transformers. It's just that internal type already closely matches our mental model so we don't need to deviate that far from the original constructor and abstract over it with higher-level primitives.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-3740133309215754732013-04-02T12:34:37.425-07:002013-04-02T12:34:37.425-07:00Something is bothering me here at about the point ...Something is bothering me here at about the point where the second hole appears. It's fine if you use the ContT constructor directly:<br /><br />newtype ContT r m a = ContT { runContT :: (a -> m r) -> m r }<br /><br />because here the continuation is represented as an ordinary function *that returns*, so it can be called multiple times, eg once with (Swing 60) and again with (Attack Target).<br /><br />However, is using the ContT constructor like this really what you're supposed to do? If the constructor is hidden, then the standard way to muck about with the continuation is to use callCC, which leads me to write something like this:<br /><br />unitAttack target = callCC $ \k -> do<br /> k (Swing 60)<br /> valid <- isTargetValid target<br /> if valid<br /> then k (Attack target)<br /> else sayUhOh<br /><br />BUT, when you do this, the first call to k aborts the rest of the computation--k never returns and the target is never attacked. Surely, to get the effect you are after without breaking the continuation monad abstraction is to use composable or delimited continuations? I'm only just learning about delimcc and all that so I'm not sure if I've got the right end of the stick yet...oddsockhttp://www.blogger.com/profile/18078892855231328882noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-59867460576936568172013-01-29T17:52:06.296-08:002013-01-29T17:52:06.296-08:00Sorry for the late reply. You're right, and I...Sorry for the late reply. You're right, and I corrected it.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-67314711922680612712013-01-16T12:14:48.164-08:002013-01-16T12:14:48.164-08:00I think there's another typo here:
damageTarg...I think there's another typo here:<br /><br />damageTarget :: Target -> IO ()<br />... and then supply it to our continuation to complete it:<br />runContT (unitAttack target) damage :: IO ()<br /><br />That should be<br /><br />runConT (unitAttack target) damageTarget :: IO ()<br />Arthurhttp://www.blogger.com/profile/15430037818965623797noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-36535112193906080572013-01-11T17:14:10.660-08:002013-01-11T17:14:10.660-08:00There's an Oleg classic written about it, but ...There's an Oleg classic written about it, but it's unsurprisingly dense. Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-91537606746346600082013-01-10T18:53:00.134-08:002013-01-10T18:53:00.134-08:00I actually don't know much about it, but some ...I actually don't know much about it, but some people have told me it's a more powerful version of what I've just described. I haven't had the chance to study it, yet.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-60567360805986268772013-01-10T18:50:01.657-08:002013-01-10T18:50:01.657-08:00What are your opinions about delimcc?What are your opinions about delimcc?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-11061158863938428672013-01-02T11:58:14.870-08:002013-01-02T11:58:14.870-08:00I don't know if that's a good or bad thing...I don't know if that's a good or bad thing. :)<br /><br />I left it out because the article is about continuations as monads, and call/cc doesn't have anything to do with their role as monads.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-20678491065946238572013-01-02T11:55:27.671-08:002013-01-02T11:55:27.671-08:00I'm impressed that someone wrote an entire art...I'm impressed that someone wrote an entire article on continuations and didn't once mention call/cc.ben whttp://waste.typepad.comnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-7467192050497033152013-01-01T08:43:21.011-08:002013-01-01T08:43:21.011-08:00I originally wrote this this for the /r/haskell au...I originally wrote this this for the /r/haskell audience, so it assumes some familiarity with Haskell already.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-62994927208475104372013-01-01T08:40:09.758-08:002013-01-01T08:40:09.758-08:00I am. Thanks for catching that! I fixed it.I am. Thanks for catching that! I fixed it.Gabriel Gonzalezhttp://www.blogger.com/profile/01917800488530923694noreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-31756613282282331732012-12-31T15:10:06.345-08:002012-12-31T15:10:06.345-08:00do you speak english too?do you speak english too?Anonymousnoreply@blogger.comtag:blogger.com,1999:blog-1777990983847811806.post-3822326215566106842012-12-30T22:16:11.131-08:002012-12-30T22:16:11.131-08:00Are you missing the '60' in your first inv...Are you missing the '60' in your first invocation of swingAxeBack?Anonymousnoreply@blogger.com