Inner functions and outer 'this' (Re: That hash symbol)

Claus Reinke claus.reinke at talk21.com
Sat Mar 26 16:49:52 PDT 2011


>> The idea is simply that (lexically scoped) variables usually
>> are bound to the next enclosing binding of the same name,
>> while protected (lexically scoped) variables are bound to
>> the next _outer_ enclosing binding of the same name
>> (each protection key skips one level of binding, lexically).
 
> To clarify, if I have the code
> 
>   foo(#x)
> 
> and suddenly I realize that I need a to guard it with a condition,
> 
>  if (bar) {
>    foo(#x)
>  }
> 
> this would ?not? change the binding of #x since, although I have
> introduced a new let scoped block, x is not declared in that scope so
> it is not counted against the stack of #'s.

This would _not_ change the binding of #x, because it
does not introduce or remove any bindings for x. Both 
bindings and protections are specific to variable names.

> It is not always easy to count scopes though without knowing 
> a lot of details.  E.g. changing
> 
> function f() {}
> 
> (function () {
>   function () {
>     return f;
>   }
> })()
> 
> requires two hashes
> 
> function f() {}
> 
> (function () {
>   function f() {
>     return ##f;
>   }
> })()
> 
> since f was introduced both into the body scope, and into 
> the outer scope via the function declaration.

The counting is (usually) just by enclosing binders, not by how 
many constructs or nested scopes they affect. So, I would count 
one intervening binding here, and one protection # to keep the 
binding structure as before:

function f() {} // target this binding
 
(function () {
   function f() { // skip this binding
     return #f;
   }
 })()

We could have this instead, where there'd be two bindings
for f to skip, and two protections to keep the binding structure
intact:

function f() {} // target this binding
 
(function () {
   var f = function f() { // skip these bindings
     return ##f;
   }
 })()

> It seems brittle, 

Not usually (did your counting scopes cloud the idea, or
am I missing some Javascript-specific problem?) - it has
been the basis of soft- and hardware implementations
of functional (and functional-logic) languages. 

If we can find the binding for an unprotected variable, we
can find the binding to skip for a protected variable; one 
binding and one protection cancel each other; once an 
unprotected variable has a binding, it is bound. 

It is still just the usual lexical scoping in action - the only 
change is that shadowed bindings can now be reached,
by protecting variables from shadowing bindings.

That is all one needs to know about the system at the
programmer-level, at least for a language like Javascript,
whose operational semantics isn't defined by rewrite
rules (*).

Hope this helps,
Claus

(*) As a student, I found programming with it very intuitive,
even though the language we were given did execute by
rewriting (so the protection keys adjusted to dynamically
changing program structure, e.g, when a function definition
was inlined at a call site);

implementers find it similar to deBruijn-Indices (if an 
implementation renames all variables to be the same, 
the resulting protection keys correspond directly to 
indices for stack access), but those are not usually
palatable to programmers; 
http://en.wikipedia.org/wiki/De_Bruijn_index

when reasoning about programs, or rewriting programs, 
it means that names are less of a problem, because we 
can always preserve the binding structure (no more 
exceptions like "you can wrap code in a function,
provided the code does not mention the function
name or parameters").
 


More information about the es-discuss mailing list