The obvious way to implement Perl5 "local" bindings in Parrot would
be to:

   1.  Capture the old value in a temporary;

   2.  Use store_global to set the new value;

   3.  Execute the rest of the block; and

   4.  Do another store_global to reinstate the old value.

This has a serious flaw:  It leaves the new binding in effect if
executing the rest of the block causes a nonlocal exit.  Throwing can be
dealt with by establishing an error handler that reinstates the old
value and rethrows, but this doesn't begin to address continuations, not
to mention coroutines, which can be used to jump to an arbitrary call
frame without unwinding the stack.

   Then there are threads to consider.  The naive approach makes each
thread's dynamic bindings visible to all other threads, which may or may
not be desirable (not, IMHO, but this is a language design issue).
Worse, the final state of the variable depends on which thread exits
first, which is surely a bug.

Proposal:

   The only reasonable approach (it seems to me) is to keep dynamic
binding information in the call frame.  One possible implementation is
as follows:

   1.  Add a dynamic_bindings pointer to the call frame.  This points to
a linked list of the frame's current bindings, each entry of which holds
a name/value pair.  Each thread's initial frame gets its
dynamic_bindings list initialized to NULL.  Each new frame's
dynamic_bindings list is initialized from the calling frame's
dynamic_bindings.  Nothing additional need be done for continuation
calling.  The dynamic_bindings list needs to be visited during GC.

   2.  Modify store_global and find_global to search this list for the
desired global.  If found, store_global modifies the entry value, and
find_global fetches the entry value.  If not found, the existing hash is
consulted in the current fashion (modulo namespace implementation).

   3.  Add a bind_global op with the same prototype as store_global that
pushes a new entry on the dynamic_bindings list.

   4.  Add an unbind_global op that takes an integer or integer constant
and pops that many entries off of the dynamic_bindings list.  Bindings
are effectively popped when the sub exists, but this op is still needed
for cases where the end of a dynamic binding lifetime comes before the
end of the sub.

Advantages:

   + The scheme is robust with respect to continuations, threads,
coroutines, and nonlocal exits.  Each sub creates dynamic bindings that
are visible only within its dynamic scope (i.e. subs that it calls,
directly or indirectly), and not to other threads or coroutines.

   + The overhead is low:  a pointer copy on call, none on return, and
zero context switching overhead.  For typical programs with little or no
dynamic binding, these are the only costs.

   + The code that a human or compiler needs to emit is even simpler
than that of the naive scheme described above.

Disadvantages:

   + The time to fetch or store a dynamic binding is proportional to the
depth of the dynamic_bindings stack, which could be considerable for
languages that do a lot of dynamic binding.  Conceivably, this could be
addressed by using a language-dependant PMC class for the binding entry,
and any such dynamic-binding-intensive language could define a per-frame
class that acted as a linked list of hashes.  But this could be
postponed until it was needed, possibly indefinitely.

   If this is acceptable (and there isn't already a better plan), I will
have time to address this over the holiday week.  TIA,

                                        -- Bob Rogers
                                           http://rgrjr.dyndns.org/

Reply via email to