From: Allison Randal <[EMAIL PROTECTED]>
   Date: Wed, 06 Dec 2006 20:09:09 -0800

   Bob Rogers wrote:
    > Allison Randal wrote:
   > 
   >    The proposal starts off talking about Perl 5's 'local' and Perl 6's 
   >    'temp'. They are lexical in nature (though they're more feature-rich 
   >    than simple lexicals). They only exist within a specific lexical scope, 
   >    and "roll back" on exiting that scope.
   > 
   > They do "roll back" on block exit, but that is a statement about
   > lifetime, not scope.  The whole point of dynamic binding is that such
   > bindings are visible outside of the sub where they are bound, which by
   > definition makes them "not lexical" in scope.  Do you disagree?

   The whole point of 'temp' and 'local' is that the bindings aren't 
   visible outside the scope where they're defined (whether that scope is a 
   sub, block, etc).

Please run the following Perl 5 example:

        #! /usr/bin/perl -w
        #
        # Illustration of "local" binding scope.  This is an
        # "uncompiled" version of the "binding at two call levels" test
        # case from t/op/globals.t in the patch.  The order of the three
        # subs is immaterial.  -- rgr, 7-Dec-06.

        package Foo::Bar;

        use strict;

        use vars qw($foo);

        sub main {
            $foo = "Outer value\n";
            show_value();
            test1();
            show_value();
        }

        sub test1 {
            local $foo = "Inner value\n";
            show_value();
        }

        sub show_value {
            print $foo;
        }

        main();

The variable $foo has global scope, but "local $foo" introduces a new
binding of $foo that is most definitely visible outside of the lexical
environment where it is created.  Running this code prints

        Outer value
        Inner value
        Outer value

illustrating that show_value has access to this binding.  According to
perlsub (but with underlining changed to caps):

       A "local" modifies its listed variables to be "local" to the enclosing
       block, "eval", or "do FILE"--and to ANY SUBROUTINE CALLED FROM WITHIN
       THAT BLOCK.  A "local" just gives temporary values to global (meaning
       package) variables.  It does NOT create a local variable.  This is
       known as dynamic scoping.  Lexical scoping is done with "my", which
       works more like C's auto declarations.

This is also my definition of "dynamic scoping".  Were you not aware of
this behavior of "local", or are we talking about different things
entirely?

   > . . .
   > 
   > But I'm not at all sure what "assignment" vs "binding" means here, since
   > it appears to refer to syntax that is not shown.  I'm also unsure that I
   > understand the difference between temporizing/hypotheticalizing values
   > vs. variables; is this a question of morphing a PMC vs. replacing it in
   > its container?

   To put it in Parrot terms: An assignment passes the value of one PMC to 
   an existing PMC and lets the receiving PMC decide what to do with the 
   value. A binding replaces the PMC stored in the namespace or lexical pad 
   with another PMC.

OK, I think I've got it.  In that case, we probably need C<temp_global>
and C<temp_location> ops in that case.  And certainly some new
C<BoundLocation> classes . . .

   But I'm still unclear how this distinction is made in a Perl 6
program.  If you write

        temp $foo = 37;

is this assignment or binding?  And in either case, how do you specify
the other?  Larry?  (Not that understanding this is likely to be
necessary for getting Parrot to do the right thing.)

   > . . .
   >
   > And in
   > any case, I still don't understand in what sense such a variable could
   > be "shared across threads" if the binding is not?  It seems to me that
   > sharing only the variable name is not sharing the variable.

   The variable is still shared if you can "unbind" it at some point and 
   then have access to the shared value. The shared value is stored 
   somewhere in thread.

OK, I think I see; the shadowed binding is still "shared" in that sense.
(Though I'm still not fond of this terminology.)

   > Could you be more specific about the proposed implementation?  What
   > issues do you have with it?  Is it worth fixing, or should I give up?

   Mainly, I don't see the proposal as it stands as a solution for 'temp' 
   and 'local'. Dynamic binding as you define it (what I'm calling 
   thread-locals), is a completely different problem. The proposal needs a 
   description of the problem it is solving, instead of describing one 
   problem and solving a different one.

Does the Perl 5 example above convince you that it is the same problem?
Or at least a subset, given that I didn't follow the "assignment
vs. binding" thing?

   Also, the particular implementation suggested -- the Location class 
   hierarchy of abstract and instantiable classes, the stack of stored 
   values -- is more heavy weight than the underlying concept merits.

If you can think of a lighter-weight approach, I'm all ears.  I proposed
something fairly lightweight nearly a year ago [1], with nearly the same
functionality, though it only addressed globals.  Leo quite reasonably
objected that it didn't fully implement Perl 5 "local", and I agreed
that it was too particular to globals to be generalized, so I withdrew
it.  The new approach attempts to address that limitation by introducing
PMC classes to support structure and variable binding distinctly in an
extensible way [2].  Now it appears that I need to be both (a) more
complete, to handle assignment as opposed to just binding, and (b)
lighter in weight.  Please pick just one. ;-}

   I see thread-locals as a feature to integrate into the sharing
   architecture of the core concurrency model.

   So yeah, the proposal is worth working on, if you're interested in 
   pushing the idea to the next level. If you're not interested in going 
   there, you have raised an interesting idea, so many thanks!

I would like to see this implemented, so I'm willing to go back to the
drawing board for a third go.  Before I do, though, I would just like to
be reasonably sure that I'm aimed in the right direction.

   > As a compiler
   > writer, the only thing I find "unhelpful" about Parrot in terms of
   > scoping is its lack of dynamic binding.

   That would be useful information. What are the practical problems
   you're working on that would be made easier with thread-locals? How
   will the feature be used by HLLs? How will it be used in Parrot's
   internal code (compiler tools, etc)?

   Allison

Oh, I have lots of ideas:

   1.  First and foremost, I need for my compiler to support this basic
CL feature.  (Right now, it kludges around this with C<pushaction>, but
the implementation is really gross.)  Here is a CL version of the Perl
program above; it is almost identical, modulo syntax changes.

        ;;; Illustration of Common Lisp "special" bindings.  This is an
        ;;; "uncompiled" version of the "binding at two call levels"
        ;;; test case from t/op/globals.t in the patch.  The order of
        ;;; the three subs is immaterial.  -- rgr, 7-Dec-06.

        (defpackage foobar)

        (in-package :foobar)

        (defvar foo)

        (defun main ()
          (setq foo "Outer value")
          (show-value)
          (test1)
          (show-value))

        (defun test1 ()
          (let ((foo "Inner value"))
            (show-value)))

        (defun show-value ()
          (format t "~A~%" foo))

        (main)

Besides being an important feature in its own right (CL I/O depends
heavily upon dynamic binding), it is traditionally used to implement
nonlexical features such as CATCH and THROW [3], which typically
communicate via a dynamically-bound alist of CATCH tags.  Not having
dynamic binding has effectively stalled my compiler project for about
six months now.

   2.  I have an implementation strategy for bringing C<throw> up to
PDD23 that relies on dynamic binding -- and here the per-thread aspect
becomes important.  How does one keep track of bound handlers as they
are tried in such a way that they are not in scope when invoked, yet are
still restored if and when a continuation exits?  The following Perl 5
pseudocode should give the flavor of it:

        sub throw {
            my $exception = shift;

            local @BOUND_HANDLERS = @BOUND_HANDLERS;
            while (@BOUND_HANDLERS) {
                pop(@BOUND_HANDLERS)->($exception);
            }
            # still unhandled . . .
        }

This cannot be implemented in C as written since invoking the handler
via an inferior runloop would make it impossible for the handler to call
an exit continuation (or BE an exit continuation).  Fortunately, the
rebinding of @BOUND_HANDLERS makes it easy to rewrite this as a tail
recursion, given suitable C-calling magic in the continuation.  It also
serves the purpose of keeping each handler out of its own scope, while
restoring the handler state appropriately if a handler exits.

   3.  When the PDD23 dust settles, it will then be possible to finish
STM with respect to error handling.  (Well, I suppose it's possible now,
but I assume it will be easier later.)

   4.  As currently implemented, the Coroutine class requires all
C<yield> instructions to be in the initial coroutine sub (indeed, this
IS the coroutine), which is limiting.  To fix this, Coroutine should be
redone as a variant of a Continuation, which probably requires minor API
changes to the way in which coroutines are created.  However, the
C<yield> interface can remain the same as long as there is a "current
coroutine" concept which is dynamically scoped -- an ideal application
for a dynamic variable binding (again, thread-local).

   And I'm sure I'll think of more as soon as I send this . . .

                Your faithful Dynamic Environmentalist,

                                        -- Bob

[1]  
http://groups-beta.google.com/group/perl.perl6.internals/browse_thread/thread/3080716c382777b/11877a16369508c8?lnk=gst&q=dynamic+binding+patch&rnum=1#11877a16369508c8

[2]  This extensibility is important to me because CL requires that I be
     able to bind a variable (named by a symbol) that is not in any
     namespace (package).  That should be easy enough to handle with a
     new LispBinding class that takes a symbol.  The details are still
     fuzzy . . .

[3]  http://www.lispworks.com/documentation/HyperSpec/Body/s_catch.htm#catch

Reply via email to