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