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

Claus Reinke claus.reinke at talk21.com
Mon Mar 28 02:03:18 PDT 2011


I am really astonished to hear protection keys being thought
of as "brittle" under transformation: that is just the opposite 
of what they are about!

Executive summary:

    - de Bruijn indices are a good assembly language of
        binding constructs, suitable for automatic transformation,
        but not suitable for human use

    - the classical name-based scheme is suitable for human
        use, in principle, but is brittle under transformation 
        (many transformations can not be applied without 
        renaming); the more important these transformations
        become, the less suitable this scheme is, for either
        humans or machines (because of excessive renaming)

    - Berkling's protection keys add flexibility to name-based 
        schemes, *removing* their brittleness under transformation;
        it combines suitability for automated transformation
        with readability for humans (in fact, both de Bruijn's 
        and the classical name-based scheme are special cases 
        of Berkling's)

> The bigger issue is that scoping mechanisms in the de
> Bruijn tradition are brittle for programming. They make
> sense as intermediate representations or notations for
> proof frameworks, because they can (sometimes) be
> easier to reason about formally, but they're fragile in
> the face of refactoring.

Sorry, but you have two directly conflicting statements
in one sentence there (unless I misunderstand what you
mean be "fragile"?): the point of de Bruijn choosing that
representation was to make automatic transformation
of scoped terms possible/easy, without breaking proofs.
That is the opposite of "fragile" to me.

> To be fair, your suggestion is more moderate than de
> Bruijn, although it's not clear whether you're proposing
> the ability to refer to shadowed bindings of *all* variables
> or just |this|.

Indeed, I wouldn't want to use de Bruijn indices at the
programmer level, while Berkling's protection keys enter
the frame only when naming alone isn't sufficient.

The problem is wider than 'this', 'this' just seems to be 
the main use case where the problem surfaces in Javascript
practice at the moment. 

With scopes and prototypes, Javascript has two separate 
binding chains, but with prototypes, we can use different 
prefixes, "(prototype.)*.name", to skip entries in that chain.

Btw, have you ever wondered whether 'var'-bindings are
recursive?

function Outer() {
  var x = "outer", outer_x = x;
  function Inner() {
    var x = [x]; // var bindings are not recursive, are they?
    log(x + ' - ' + [outer_x]);
  }
  Inner();
}
Outer();

Will this log '["outer"] - ["outer"]', or '[[[[[[[[...', or something
else entirely?

I'm not making proposals yet, I'm just bringing some 
useful ideas to the attention of those who have to come 
up with proposals, hoping to make their work easier!-)

> Seriously, the problem you're trying to solve is that |this|
> is too fragile in the face of refactoring, but you're solving
> it with a mechanism that's just as sensitive to refactoring.

It seems I am still not communicating Berkling's ideas
successfully (sorry about that):

his reduction systems were *based* on refactoring problem
descriptions (functional programs) to solution descriptions
(values of the functional language). His protection keys and
their handling were at the core of every scope-related
reduction/refactoring rule in those systems. If protection
keys were at all "brittle" in the face of refactoring, none of
his systems would ever have worked.

Quite contrary to being "brittle", protection keys introduce
the necessary flexibility that allows these systems to *avoid
breaking* the binding structure while automatically rewriting
the code.

> It does make it syntactically simpler to fix than
> |var self = this|, but the fix is just as brittle to the
> next refactoring.

Perhaps it helps to clarify the problem: when we want to
transform a program in a way that introduces new bindings
where they get in between an existing binding and some of
its bound variables (shadowing them), we have to make
additional changes to compensate (to keep the original
binding structure and program meaning intact). If that is
what you mean by "brittle", there is just no way around it.

The only question is how to do the compensation. The
'var self = this' route is in the traditional line, renaming
bound variables. For non-'this' use cases, its main drawback
is that is changes the names chosen by the programmer.
If one does that during automated refactorings, the final
program is sometimes not recognizable to the programmer
who wrote the initial code (the alternative is for the
refactoring tool to balk and ask the programmer to resolve
the naming conflict before invoking the refactoring again).

For 'this', the issue is the other way round: the name is
not the programmer's choice, the binders for 'this' are
implicit (so we cannot rename at the binder), and one
cannot rename the variable without losing its special
functionality (so we end up with two names instead of
one: 'self' is lexically scoped, redirects to 'this', which
is more dynamically scoped).

The protection route is non-traditional, yes, but it neatly
solves the main issues: instead of renaming bound 
variables, it just protects variables from getting bound 
erroneously; instead of having to invent fresh variable 
names, automatic transformation tools just have to do 
a little arithmetic.

Claus
 


More information about the es-discuss mailing list