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

Reply via email to