On 10/26/06, Bob Rogers <[EMAIL PROTECTED]> wrote: > From: "Ben Tilly" <[EMAIL PROTECTED]> > Date: Thu, 26 Oct 2006 17:09:35 -0700 > > On 10/26/06, Bob Rogers <[EMAIL PROTECTED]> wrote: > > From: Tom Metro <[EMAIL PROTECTED]> > [...] > > Guido made comparisons to Perl only in two areas - saying he likes > > generators and iterators better than continuations . . . > > > > made me think of a paper [1] I only stumbled on recently (despite it > > being 13 years old!) on the semantic weaknesses of iterators. I found > > it while researching coroutines; I can think of no more compelling > > demonstration of the power of continuations than the fact that they make > > coroutines trivial to implement [2]. > [...] > > While I agree that continuations are insanely powerful, I likewise > prefer generators and iterators to continuations. Why? Well suppose > that you write some complex code using continuations. Suppose that > something goes wrong. Now you want to debug it. So you'd want some > useful debugging information. Something like, say, a stack backtrace. > > You're outta luck. > > That is not true -- or, at least, not necessarily true. In a full CPS > language like Scheme, tail-merging (or the equivalent CP transformation) > is obligatory, so you may in fact be of luck. (I've never used Scheme, > so I don't know how a real Schemer goes about debugging.) However, a > Scheme continuation looks just like a closure at the source level. It > is possible to write nearly identical code in Common Lisp that uses > closures, and since CL does not tail-merge by default, you get the same > functionality plus useful backtraces in the debugger.
Ummm...you've mixed three unrelated things together. 1. Continuations. 2. Closures. 3. Tail call elimination. Taking them in reverse order, here is what they are: 3. Tail call elimination is the process of eliminating call frames that have nothing left to do except return a later call. So in other words if foo ends with return bar(), then one can wipe out foo's call frame and replace it with bar. Tail call elimination can make some recursive algorithms as efficient as looping. At the expense of losing potentially useful debugging information. 2. Closures are anonymous functions that carry their own environment (ie variables that were in lexical scope when the function was created). I love closures. I'm very glad that Perl 5 has them. 1. Continuations are a representation of the execution state of a program at a given time. You can think of executing a continuation as a goto that also changes the executing context (various variables, etc) as you're performing the goto. Note that, like with tail call elimination and unlike with closures, after you call a continuation there is no record left of where you were or what your state was. If there is no record left, then there is no useful debugging information left. However it is worse than that. With tail call elimination there is a limited amount of useful debugging information that one might want to have. You don't have the information, but it is easy to know what the information would be. And one could in principle have 2 run modes, one in which that is tracked for debugging purposes, and one in which you can't. With continuations debugging is not so easily done for a more fundamental reason. Which is that the flow of control need not correspond to any directly comprehensible structure. A traditional program, even one that uses closures, has an execution shape that looks like you're traversing a tree. (Go down one level, another level, back up, down again, etc.) And at any point in time one may think about the most direct path back to the root. (This is your stack backtrace.) But the execution shape of continuation based code need not look remotely like this. For instance suppose that you used continuations to implemented a cooperative multi-tasking system with multiple threads running in parallel, and inside each thread you're following a familiar execution paradigm of function calls that return. (This is a realistic use, and it all can be implemented with continuations.) Your execution path can now be visualized as a series of rapid switches between simultaneous traversals of several different trees. This is *massively* more complex than any execution path that a traditional program can follow. Furthermore one can only reduce it to this simple picture by knowing the intent of the code, something that I would not expect any automatic debugging facility to decode a trace of and present in an understandable fashion. And finally, suppose that this multi-threading code was messing up, you're debugging a situation where sometimes it is calling the wrong continuation so that a thread was randomly "forgetting" that it had made or returned from some function calls. Now you've got a series of executions that has no sane visualization. If that didn't make any sense, let me make it simpler. A language like Common Lisp can be implemented with all of the function calls represented on a stack. And therefore when you ask for a stack backtrace, there is something to give you. However continuations introduce too much complexity to be implemented with a stack, and therefore it is impossible to show what is going on with something as simple as a stack backtrace. While the Scheme continuations may look similar to closures to you, they're NOT similar to closures, and the implications are VERY different from closures. > Of course, you may also blow out the stack if you try to write > Scheme-style loops without tail-merging. But Common Lisp allows you to > enable tail-merging selectively, as a compiler optimization. I believe that "tail-merging" is usually called "tail-call elimination." If you mean something else, then please explain. > I also believe (from having written complex CL applications) that the > use of functional arguments (closures, continuations, whatever) reduces > complexity, sometimes considerably. This is a direct consequence of > Graham's "succinctness = power" thesis in [1], which Tom cited > previously. I agree on the power and utility of closures. I do not agree on continuations. Please note that your CL experience is of no utility on the question of whether continuations are good because CL does not have continuations. Furthermore this is not an accidental oversight, the CL committee looked at continuations, decided that continuations are Bad Things, and explicitly chose to leave them out. > Abstraction. Speed. Debuggability. Pick any three. ;-} If you were arguing for closures, I'd agree with you. But you're arguing for continuations. Cheers, Ben _______________________________________________ Boston-pm mailing list [email protected] http://mail.pm.org/mailman/listinfo/boston-pm

