More simple continuation questions

Hello all, (1) when reading about continuations, there is this thing which needs an "other function" to be passed to produce a final result. And there is this "other function". Which of the two is "the continuation"? http://stackoverflow.com/questions/9050725/call-cc-implementation sais " ... instead, they are passed a function that represents the 'next step' in the computation - the 'continuation' " Which sound like the "other function" is called "continuation". But other articles hint in the opposite direction. (2) When comparing continuations with callbacks, it struck me that the type is newtype Cont r a = Cont { runCont :: (a -> r) -> r } So the type of the final result is r. But why does the function (a->r) need to return an r? With regular callbacks this does not seem to be the case. After returning from the callback the surrounding function is free to do anything it wants with the return value and return a value of a different type. Why is that so?

martin
writes:
(1) when reading about continuations, there is this thing which needs an "other function" to be passed to produce a final result. And there is this "other function". Which of the two is "the continuation"?
Say you have two functions, foo and bar: foo :: a -> b bar :: b -> c Ordinarily we'd just compose these, like so: bar . foo But there are times when this is not desired or possible. In those cases, we can change foo to accept a continuation instead: foo :: a -> (b -> r) -> r 'foo' doesn't know what type the continuation will want to return, nor should it care. So we just leave the result polymorphic, and call it 'r' for result. Now we can call foo and pass it a "continuation": that is, the thing it should do next with the value generated from 'foo': foo bar Note that we can encode 'foo' a little more conveniently now as: foo :: a -> Cont r b This allows us to use the CPS'd (continuation passing style) form of foo as a Functor, Monad, etc. John

Am 07/16/2014 05:06 AM, schrieb John Wiegley:
martin
writes: (1) when reading about continuations, there is this thing which needs an "other function" to be passed to produce a final result. And there is this "other function". Which of the two is "the continuation"?
Say you have two functions, foo and bar:
foo :: a -> b bar :: b -> c
Ordinarily we'd just compose these, like so:
bar . foo
But there are times when this is not desired or possible. In those cases, we can change foo to accept a continuation instead:
foo :: a -> (b -> r) -> r
I see. I just wondered if unix pipes are a way to get intuition about continuations. Is the following about correct? flip ($) turns an ordinary value into a Cont. In bash you can achieve the same by prepending "echo" and appending |. So flip ($) 42 corresponds to echo 42 | That expression "echo 42 |" needs another function behind the pipe symbol to return a value. The equivalent to "id" is "cat". So "flip ($) 42 id" is corresponds to "echo 42 | cat". I am still struggeling with the naming. If "cat" is the continuation of "echo 42", what is "echo 42" called (the thing which needs a continuation)? Anyways The unix pipe can also be modeled by simple function composition. I can write a longer pipe, e.g. echo 42 | wc | cat as cat . wc $ 42 (assuming there were functions cat and id in haskell) I have trouble to understand what continuations give me byound that. I suspect the following: A pipeline is a fixed set of functions, whereas with CPS I can say: if the value received is this, then continue this way, otherwise continue that way. With a simple pipeline I would have to put all these cases into the function which makes this decision and even pass the decision on to the next function in the pipeline so it know how we got there. Is this about right? Finally, how about the call stack? Are these all tail calls, and I don't have to worry?

martin
writes:
A pipeline is a fixed set of functions, whereas with CPS I can say: if the value received is this, then continue this way, otherwise continue that way. With a simple pipeline I would have to put all these cases into the function which makes this decision and even pass the decision on to the next function in the pipeline so it know how we got there. Is this about right?
It's not only about right, I've implemented the concept here: http://hackage.haskell.org/package/simple-conduit This uses a short-circuiting continuation data type underneath: newtype Source m a = Source (forall r. Cont (r -> EitherT r m r) a) And then provides you with the capability to build Unix-style pipelines in order to keep memory use constant, and to ensure that all resources are freed when the pipeline finishes, or if it should short-circuit or fail. I have to look into why the Haddocks are failing to build, but they will build for you locally. John
participants (2)
-
John Wiegley
-
martin