From: "Ben Tilly" <[EMAIL PROTECTED]>
   Date: Mon, 30 Oct 2006 00:00:54 -0800

I'll be brief(er); my hands are beginning to hurt.

   On 10/29/06, Bob Rogers <[EMAIL PROTECTED]> wrote:
   >
   > I don't follow.  That reliable destruction happens when all pointers to
   > $arg are destroyed; tail-calling bar would also destroy them when bar
   > returned, though in the opposite order (bar's reference last instead of
   > foo's reference last).  In other words, it doesn't matter whether foo or
   > bar destroys the value, as long as it's the last reference.
   >
   >    It's true that ReleaseAction can call its actions prematurely in the
   > event of a tailcall, depending on where the object reference is kept,
   > but that's a separate issue.

   That's not just a separate issue, that's the whole issue.  Perl has
   precise semantics for when DESTROY is called . . .

The perlobj page (5.8.1) says the following:

       Destructors

       When the last reference to an object goes away, the object is
       automatically destroyed.  (This may even be after you exit, if
       you've stored references in global variables.) . . .

This is precise in the sense of being well-defined, but not in the sense
of nailing the "when" down as tightly as you seem to expect.  In
particular, the attached use of ReleaseAction confirms that tail-merging
does affect when a destructor is called.  But both behaviors (before bar
is called and after it returns) seem legitimate in light of what the doc
says.  Which implies that tail-merging despite the "my" variable would
be legitimate, if Perl ever did tail-merging.

   > I've done this on occasion, but it's pretty ugly, and the benefit is
   > slim; I usually don't bother.  If you do
   >
   >     my $foo = sub {
   >         ...
   >     };
   >
   > you will find that $foo is not even in its own scope.  And those names
   > don't show up in backtraces, so the sub is still anonymous as far as the
   > rest of Perl is concerned.

   Write that as:

       my $foo;
       $foo = sub {
           ...
       };

Which is only slightly uglier, IMHO, but adding more mutually-recursive
functions makes it worse . . .

   Believe me, I've gone down this path, taken a look at the finished
code, and then wished I hadn't.

   As for giving names to anonymous functions in backtraces,
   http://search.cpan.org/~xmath/Sub-Name-0.02/lib/Sub/Name.pm may assist
   you. :-)

Now *that* is useful; thank you.

   >    . . .  If the rest of the computation depends on, for
   >    instance, global variables, then you haven't really packaged up the
   >    rest of the computation unless you capture those variables as well.
   >    Therefore you need to package up everything.
   >
   > Are you aware of an implementation of continuations that really does it
   > this way?  If calling the continuation really rolls back EVERYTHING,
   > then the called function can only change the state of the computation by
   > returning values.  That would be OK in a pure FP language, of course,
   > where there are no side effects, but if there are no side effects, then
   > it is not necessary to capture the values of globals.  So what is the
   > point?

   My memory said that continuations in Ruby worked this way, but I just
   double-checked and they don't restore global variables.  That seems to
   have been a misunderstanding on my part . . .

   I'm aware that there are multiple ways to implement continuations.
   But to answer your question, I'm not deeply familiar with the runtime
   internals of any of these languages.

So . . . have you *used* explicit (exit) continuations in any of these
languages?  At the start of this thread, you made an assertion about the
difficulty of debugging code that uses (exit) continuations.  Was this
just a theoretical position?

   >    If there is a way to do what is discussed there with closures, then
   >    I'm failing to see it.
   >
   > The key is CP transformation.  Start with the "Examples" section of the
   > Wikipedia "Continuation-passing style" article [2].  A traditional sort
   > of Scheme compiler converts the "Direct style" into "Continuation
   > passing style" during the analysis phase.  All of those extra lambdas
   > are continuations, and all of those continuations are tail-called.
   > Indeed, except for primitives, the transformed code has ONLY tail calls.
   > Because all tail calls are optimized away, it follows that the leaving
   > activation record is not needed after the call; any state that it may
   > contain that is still relevant is captured by the continuation.  Since
   > these continuations do not have to return to an existing AR (as you have
   > already observed, Scheme needs no stack), they can can be implemented as
   > closures.  And since all Scheme programs can be CP-transformed, closures
   > are adequate for the implementation even of what we may think of "exit
   > continuations," though the distinction is unimportant to Scheme.

   I think we're talking past each other again.

   You've just described how to implement continuations using closures.
   However the effect is that within Scheme, you write normal code and
   exit continuations are available.

Yes, I have described (albeit with much hand-waving) how to implement a
Scheme compiler, providing full Scheme continuations with only closures.
Isn't that what you were "failing to see"?

   To use the technique within CL or Perl you'd have to first rewrite
   all of your existing code in an usual style, and then you'd have
   something that works like an exit continuation, but only for your
   rewritten code.

Or write a macro that rewrites it for you.  But, yes, you'd have to
treat all other code as primitives.  It would probably be easier just to
write in Scheme directly.  ;-}

   (Doing it in Perl would waste tons of stack space, but that is
   neither here nor there.)

Only if you (or your macro/preprocessor) didn't "goto $continuation".

   > Sorry; I didn't explain that part because I didn't think it was
   > pertinent.  "(def has-parts-mixin)" in the formal parameter list of a
   > defmethod means that the variable "def" is bound to an object of class
   > "has-parts-mixin".  Since the second formal "continuation" is a symbol,
   > it is not specialized, and may be of any type (recall that CL uses
   > multiple dispatch).

   I've never programmed CL.  I have read a couple of books about it.
   I've never tried to understand CLOS.

   I was badly misreading the code.  I thought that def was a function or
   macro of some sort, and therefore (def has-parts-mixin) was being
   expanded into something that was interpolated into the function
   signature.

That's an unfortunate terminology clash; "def" is short for "definition"
in the application domain, and has nothing to do with Lisp per se.
FWIW, macros are never expanded in lambda lists (unless, of course, they
are in expressions that initialize defaulted optional variables).

   Now that I have that straight, I think that the first
   method is similar (modulo the complexities of CLOS) to:

       sub map_parts {
           my ($def, $continuation) = @_;
           for my $part ($def->def_parts()) {
               $continuation->($part);
           }
       }

Correct.  (And the complexities of CLOS don't apply here, because only
the first arguments are specialized, so they behave like ordinary
single-dispatch methods in Perl 5.)

   And the other to:

       sub map_extent_parts {
           my ($has_parts_mixin, $x1, $y1, $x2, $y2, $continuation) = @_;
           for my $part ($def->def_parts()) {
               if ($part->bbox_overlaps_extent($x1, $y1, $x2, $y2)) {
                   $continuation->($part);
               }
           }
       }

Correct, except that you are missing the $def parameter.

   In that case, the name $continuation seems misleading to me.
   Certainly it would give me the wrong expectations.  (In fact it
   did...)

I'm sorry that I was not as clear as I should have been that I was
illustrating another use of the term.

   >    So I'm guilty of muddying the waters by using "continuation" in two
   > different senses.  My apologies; I will try to be more precise.  I guess
   > I'm a product of my Lisp upbringing, where both senses are used, often
   > imprecisely.  :-}

   And I'm the product of my Perl upbringing, where continuations are
   something that other languages have. :-)

   Still, even if I accept the fact that there is a population somewhere
   that uses continuation in a very different sense, I think I'm being
   reasonable to say that when most people speak about a language having
   continuations, they do not mean something that acts similarly to
   closures.  (Even if they are just closures under the hood.)

But, as mentioned above, acting "similarly to closures" doesn't rule
much out . . .

   Still, I take your point.  I was trying to broaden the discussion to
other uses of "continuation," but wasn't sufficiently explicit about it.
And it didn't help that we were also simultaneously discussing the other
"hard core" meanings of "continuation."

   > I would say "can lead to problems" or "may lead to confusing code."  So
   > can the other powerful language features I've encountered, but I often
   > find uses for them -- in code that (IMO) seems no more confusing than
   > necessary.

   We all claim that features are only misused by others. :-P

   Still I think we're saying the same thing.  Continuations are very
   powerful, and can lead to confusion.  (And not just confusion over
   what the definition of a continuation is!)  We're just in disagreement
   about whether that trade-off is a net win.

Good; perhaps we should leave it at that, then, and give the other
Mongers a rest.

   > Complicated algorithms tend to be hard to debug no matter how they are
   > implemented.  Even so, consider that an algorithm implemented with
   > (exit) continuations may be easier to debug than one without if using
   > continuations makes the implementation appreciably cleaner and/or more
   > compact.

   Hey, you don't have to convince me that exiting early is a good thing.
    Several years ago I read
   
http://www.cis.temple.edu/~ingargio/cis71/software/roberts/documents/loopexit.txt
   and was easily convinced.

Interesting.  But I suspect that I have less need to do this sort of
thing (return & break) when I can use functional abstractions instead of
loops [1].

   >    Indeed, error handling itself makes a good case in point.  The
   > equivalent of try/recover in many languages makes it easier to separate
   > error handling from "normal" code, thereby simplifying the normal
   > control flow, but requires (exit) continuation semantics to make it
   > happen.  (I was going to say that I rarely use exit continuations in my
   > day-to-day programming, but then I remembered error handling.)

   I would agree.  Many others would not.  You can get a sampling of
   opinions at discussions such as
   http://c2.com/cgi/wiki?AvoidExceptionsWheneverPossible.

   Cheers,
   Ben

Astonishing.  Even in the 21st century . . .

                                        -- Bob

[1]  Henry G. Baker, "Iterators: Signs of Weakness in Object-Oriented
     Languages", ACM OOPS Messenger, July 1993.
     http://home.pipeline.com/~hbaker1/Iterator.html (also cited earlier
     in this thread).

#! /usr/bin/perl -w
#
# Illustration of destructor timing.  The tailcall makes the destruction happen
# before bar is entered; the normal call makes it happen after bar exits.
#
# [created.  -- rgr, 29-Oct-06.]

use strict;
use warnings;

use lib '.';

use ReleaseAction;

sub bar {
    my $arg = shift;
    
    print "bar:  got '$arg'.\n";
    return 3*$arg;
}

sub foo {
    my $arg = shift;
    my $action = ReleaseAction->new(sub { print "Cleaning up.\n"; });

    if ($arg) {
        print "Tail-calling bar with one plus $arg.\n";
        @_ = ($arg+1);
        goto \&bar;
    }
    else {
        print "Calling bar with one plus $arg.\n";
        return bar($arg+1);
    }
}

print "foo(0) returned ", foo(0), ".\n";
print "foo(1) returned ", foo(1), ".\n";
 
_______________________________________________
Boston-pm mailing list
[email protected]
http://mail.pm.org/mailman/listinfo/boston-pm

Reply via email to