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