Static local variables

# joe (9 years ago)

I've been hesitant to bring up this idea, because even though I use it extensively as a language extension in my transpiler I'm not sure it is normatively correct.

When I started my first major JS project four or five years ago, I noticed I was doing this a lot:

var intersect_vector_tmp = new Vector();

function intersect_vector(v1, v2) {
    var tmp = intersect_vector_tmp .load(v1).sub(v2);
    //do something
    v1.add(tmp);
}

Basically, I was doing the following transform:

  • For each static local variable...
    1. Build unique name from hash of lexical scope chain
    2. Rename variable (along with references to it in the local hoisted scope).
    3. Move it to module/global namespace.

I ended up implemented this in my compiler as a sort of C-style static local variable. Thus, the example above became:

function intersect_vector(v1, v2) {

    static tmp = new Vector();

    tmp.load(v1).sub(v2);
    //do something
    v1.add(tmp);
}

There are, obviously, a great many problems here, starting with the fact that the variable's initializers are being evaluated outside of the scope they were declared in. Unfortunately this pattern can be hard to avoid for high-performance code, so much so that if the transformation is done by hand you end up with a great deal of clutter. This is especially annoying when dealing with classes, as the following file (with the statics transpiled out) illustrates:

joeedh/fairmotion/blob/master/examples/vectormath_static_illustration.js

This becomes a real pain after a while. At one point I tried using shared object pools, but found that this didn't remove very much clutter because, in practice, each function had to have its own pool (usually one for each type of temporary object it uses). This really does boil down to a lack of support for local stack allocation of temporary objects.

Anyway, I just thought I'd put this out there. Who knows, maybe someone will invent perfect escape analysis for temporary const objects, and we won't need hackish language extensions such as this.

What do people think? Too many normative problems?

Best, Joe

# Fabrício Matté (9 years ago)

What you've described seems very similar to PHP's static variables php.net/manual/en/language.variables.scope.php#language.variables.scope.static.

They are very nice syntactic sugar, in my opinion. I'm just not sure if this is worth adding new syntax to the language, as there is already a pretty standard approach:

var tmp;
function intersect_vector() {
    tmp = tmp || new Vector();
    // ...
}

Or with CoffeeScript:

tmp = null;
intersect_vector = ->
    tmp ?= new Vector();
    # ...

Though, static variables would remove this boilerplate and make it easier to reason about a given binding's scope. I believe they would be a nice addition to the language.

# joe (9 years ago)

I've never liked the |= approach, it's not very readable. I like the ?= operator from CoffeeScript, though. I tend to agree with the conventional wisdom (I think it's the conventional wisdom?) that the existential operator is too immature for JS and needs more research, but an existential assignment operator might be a different matter (I've not thought it through, though).

Best, Joe

# myemailum14 at gmail.com (9 years ago)

Look at it this way

a = a + 1

is same as

a += 1

then it feels natural to say the same thing

a = a || 1

a ||= 1

then you can extend it like this

a ||= var1 || var 2 || var 3

# Allen Wirfs-Brock (9 years ago)
# joe (9 years ago)

Eek, this is even worse than I though. From the previous discussion:

If "static" means "shared per-Function-object" or "shared per

activation of the enclosing scope of the static's scope", we quickly run into weird behavior. Another example, on top of Waldemar's:

     function foo() { function nextID() { static s = 0; return

s++; } ... ... }

Imagine what a programmer who wants nextID to produce unique ids

would think upon discovering that it doesn't. What could the bug possibly be? Moving nextID out to global scope fixes it. Think how crazy this is. The function doesn't even refer to anything in its scope-- yet its scope affects its behavior?

IIRC, my code hoists all the way to module scope, so that example would work as intended. But that's the problem: it's not at all clear to me that it should work that way. Even worse, perhaps it should work, but not this:

    function foo() { var nextID = function() { static s = 0;

return s++; } ... ... }

eek! This is even worse than the existential operator in terms of unintended consequences.

I've been feeling guilty for not sharing my experience with this feature, since I do use it a lot. I recently implemented ES6 modules as a little require.js loader plugin (it only transpiles the module syntax, since Chrome now has most everything else in ES6), perhaps I'll write a little plugin for this, too. Then I can use it in only the half-dozen or so files that really need it.

Joe

Joe

# myemailum14 at gmail.com (9 years ago)