function hoisting like var -- Was: Re: Surprising semantics

Ingvar von Schoultz ingvar-v-s at
Thu Jul 24 16:57:43 PDT 2008

waldemar at wrote:
> I'm trying to keep the language relatively simple. 

You can't get away from supporting this:

         function a(){}
         var b = a;

What you see above is function b() hoisting like var. This
is the /exact/ functionality, apart from using two names.
You can refuse the clearer syntax, but you can't refuse
the above code and functionality.

b is bound to the global scope and assigned |undefined|
before you enter the global scope.

Assigning |undefined| is correct for any function whose
assignment depends on sequential code. The above is such a
sequential dependency, even though it may not look that way.

> What you're proposing
> is far too complicated.

On the contrary, the functionality already exists, as shown

> Keep in mind that function assignments hoist to the beginning of the scope
> in which the function is defined, so your proposal won't work. 

When the programmer explicitly says that the assignment depends
on sequential code, then do what the programmer says. Anything
else is an error. Do it by assigning |undefined| before scope
entry. This is the only correct thing to do.

> You're
> trying to do a complex split-scope approach where each function definition
> has *two* scopes, one in which it is declared and one in which it is
> defined, but even that won't work with const, typed functions and
> variables, etc. 

Are you saying that the function /body/ gets into trouble,
or the function /name/?

The function /body/ stays where it is. The hoisting doesn't
affect it in any way. Moving the body would change its context
and meaning catastrophically. Don't touch it.

The /name/ becomes a var. Treat it like any other var. Hoist
it and assign |undefined|, exactly like you do with other vars.

Are you saying that because this var is related to a function
it can't be treated like other vars? Is this var fundamentally
different from other vars? At least above it isn't.

Or is the problem in the type declarations? Are datatypes of
vars and functions fundamentally different?

Or is it because functions have parameters? You're not saying
anything about parameters, so if that's the problem you're
being very vague indeed.

> See my previous email as to why. 

That email is about some wildly unworkable dynamic scoping.
It has nothing to do with anything I ever said. You jumped
to that conclusion.

Please stop insisting that I'm proposing that nonsense. I'm
not. I never did.

> You'd then have to
> introduce extra rules about some definitions only being possible within
> {{}} blocks, which would then affect the behavior of existing definitions
> like var if one of the other definitions within the same block was a const
> or function, which would snowball into a complex mess.

Unrelated, I believe.


> Ingvar von Schoultz wrote:
>> I'm astonished that you interpreted my text in such a weird
>> way! That's completely foreign to JavaScript!
>> var is always unconditional in JavaScript. An if() before a
>> declaration doesn't make the declaration conditional.
>> The var takes effect long before you reach that if(). It takes
>> effect before you enter the scope in which the variable resides.
>> You can consider the declaration glued to the opening brace of
>> that scope. Or better, glued to both braces and stretched between
>> them.
>> The assignment, on the other hand, stays in place and is
>> conditional. But only the assignment.
>> With your surprising interpretation things would be much worse
>> than what your examples suggest. Much worse. Consider assigning
>> to the unpredictable variable:
>>     function Outer()
>>     {
>>         var Test = 1;
>>         function Inner()
>>         {
>>             if (Unknown)
>>                 var Test = 2; // Weird conditional declaration
>>             // Many
>>             // lines
>>             // of
>>             // code
>>             Test = 3;  // Semantics totally unpredictable
>>         }
>>     }
>> Is the outer Test set to three, or has an inner Test been
>> created that can take the value? Every program would become
>> impossible to understand, an unintelligible mess.
>> With my subject line "surprising semantics" I didn't mean to
>> advocate more surprises, I wanted less!
>> I assumed that ES3.1 and ES4 declarations would work like
>> ES3 declarations do: The declaration spans the entire scope
>> from before you enter the scope and throughout. The value
>> is unassigned (undefined) until you reach an assignment.
>> I took it for granted that making it constant and/or giving it
>> a type would follow the same pattern. The name gets associated
>> with the constantness and/or type before you enter the scope,
>> and it's in effect throughout.
>> In your examples with dual scopes, the result depends on whether
>> you mean that the inner c is bound to the outer scope or the
>> inner. If it's the outer you have conflicting, irreconcilable
>> bindings that collide before you enter that outer scope, a clear
>> declaration error. If it's the inner, then the inner declaration
>> shadows the outer, so the value can be unassigned or 37.
>> Regarding the plethora of ways to define things, by including
>> or excluding "let" you simply change which one of two possible
>> scope opening braces the declaration is glued to for the purpose
>> of name visibility. You get the same effect if you just move the
>> declaration to that spot yourself, without moving the assignment.
>> By including or excluding "let" you're simply telling the compiler
>> where you want this move to go.
>> Ingvar
>> Waldemar Horwat wrote:
>>> We've been down this road before, and the arguments you present have
>>> been hashed out over years.  This approach doesn't work.  Read the
>>> archives of the ES4 group.
>>> The problem is that you then get a plethora of ways to define things:
>>> var
>>> const
>>> function
>>> type
>>> namespace
>>> let
>>> let const
>>> let function
>>> let type
>>> let namespace
>>> Furthermore, some of them don't make sense (such as "function" without
>>> "let") because they can conditionally capture variables that may not
>>> even exist.
>>> The example you give of conditional definitions:
>>> if (foo) {
>>>   const c = 37;
>>> } else {
>>>   const c = "abc";
>>> }
>>> ... do something with c ...
>>> is particularly disruptive.  You must then support conditional holes:
>>> // outer scope
>>> function c() ...;
>>> // inner scope
>>> {
>>>   if (foo) {
>>>     const c = 37;
>>>   }
>>>   ... c can be either 37 or the outer scope function here ...
>>> }
>>> It gets worse:
>>> // outer scope
>>> function c() ...;
>>> // inner scope
>>> {
>>>   function f() {
>>>     return c;
>>>   }
>>>   a = f();
>>>   if (foo) {
>>>     const c = 37;
>>>   }
>>>   b = f();
>>>   ... just what do a and b hold here?  Was f's captured variable
>>> rebound by the if statement? ...
>>> }
>>> Also consider:
>>> for (i = 0; i < foo.length; i++) {
>>>   const v = foo[i];
>>> }
>>> You'll catch everyone off-guard if you make folks do a let const
>>> instead of a const here.
>>> In E4 it gets worse still because c can have a type:
>>> type c = ...
>>> {
>>>   if (foo) {
>>>     const c:Number = 37;
>>>   } else if (bar) {
>>>     var c:String = "abc";
>>>   }
>>> }
>>> ... do something with c, which is either a type, a constant, or a
>>> variable, and can be statically typed as either a Number or a String ...
>>> const d:c = ... // Conditional definition requires variable types to
>>> be evaluated at run-time, which is not somewhere we want to go in the
>>> first version
>>> I don't know of anyone here who wants to support something like that.
>>>     Waldemar
>>> Ingvar von Schoultz wrote:
>>>> These are some impressions looking at what I expect from the
>>>> language, and how some things in the specification can cause
>>>> confusion.
>>>> I would have contributed here during the discussions, but I
>>>> discovered the mailing lists just a couple of days ago.
>>>> I expect the compiler's interpretation of program-code text
>>>> to be close to my intuitive understanding of what the text
>>>> says. It's very unfortunate if keywords have unexpected
>>>> meanings that cause mysterious side effects.
>>>> If I learn that ECMAScript will let me change my var(iables)
>>>> into const(ants) I expect this to turn them into constants, in
>>>> the sense that trying to change their value will be considered
>>>> an error. It's very disappointing that by default they are
>>>> instead defined to have the baffling and mysterious behavior
>>>> of silently ignoring an attempt to change them, acting as if
>>>> no error had occurred.
>>>> You'll have to keep this oddity in mind at all times, and even
>>>> then errors related to this will sometimes cause symptoms to
>>>> appear far from where the error is, costing quite some time
>>>> to explore. Why doesn't my program change its behavior even
>>>> though I'm provoking changes? Where in this big program's
>>>> complicated sequence of events is the change silently, secretly
>>>> lost?
>>>> If instead you use var, at least the problems that can come
>>>> from this will tend to give symptoms closely connected to the
>>>> incorrect change in the value.
>>>> So this is a disappointing red flag: Don't use const, it is
>>>> likely to cause baffling problems and unlikely to help.
>>>> Unfortunately there's another problem with const that is much
>>>> more important. I often use constants for conditional settings:
>>>>      if (Debugging)
>>>>      {   var DatabaseName = "TestDatabase";
>>>>          var DisplayCount = 5;
>>>>      }
>>>>      else
>>>>      {   var DatabaseName = "RealDatabase";
>>>>          var DisplayCount = 15;
>>>>      }
>>>> The redundant "var"s are a defensive habit, omitting them would
>>>> be a warning about accesses outside the current scope.
>>>> If I haven't been warned, and hear that ECMAScript understands
>>>> "const", I expect that replacing "var" with "const" will change
>>>> the above from variables into constants. The keyword in no way
>>>> suggests that it will hide them from view. If they disappear
>>>> I'll inevitably consider such a completely unrelated side effect
>>>> a compiler bug.
>>>> Because of this I'm unhappy about the conclusions of ES3.1 that
>>>> the visibility scope of "const" should be the enclosing brace-
>>>> delimited block. Such intricate semantics hidden in words that
>>>> express something completely unrelated will make the language
>>>> seem difficult and fraught with hidden surprises.
>>>> I much prefer what ES4 says in various places on the website:
>>>> that you express this localness with "let const" and "let function".
>>>> One block-scope keyword for all block-scope visibility. Consistency
>>>> and clarity.
>>>> However this brings me to the unfortunate word "let". Although
>>>> this word has a precise and clear technical meaning for the
>>>> initiate, for us in the unwashed masses I can't see that the
>>>> English word "let" gives even the remotest suggestion of local
>>>> containment. In fact it suggests very clearly that it's related
>>>> to the "=" that so often follows:
>>>>      if (x == 5)
>>>>      {   let y = 3;
>>>>      }
>>>> "If x is 5, then let y equal 5." There's an almost inescapably
>>>> strong suggestion that "let" is a phrasing of the assignment
>>>> expression, and therefore can't have anything to do with the
>>>> braces.
>>>> I think ECMAScript should be easily accessible to us in the
>>>> unwashed masses. It becomes much more intuitively accessible
>>>> if it uses a word that strongly implies localness:
>>>>      {   if (x == 5)
>>>>          {   local y = 3;
>>>>          }
>>>>          local const Debugging = false;
>>>>          for (local Key in List)
>>>>              ++List [Key];
>>>>      }
>>>> You get plain English sentences that express quite accurately
>>>> what they're supposed to mean. The programmer won't be the
>>>> least surprised if a value gets hidden by "local".
>>>> When people want to write let expressions, if they have to
>>>> write "local" instead of "let" I don't think this will cause
>>>> problems. I'm sure the initiate are sophisticated enough that
>>>> they can adapt to this.
>>>> Apart from this, I think the scoping arrangements would
>>>> become significantly simpler and clearer if the language
>>>> made a very clear, really visible, intuitively accessible
>>>> distinction between two different types of block, and allowed
>>>> you to choose either type of block wherever this made sense.
>>>> My suggestion is to introduce a clearly distinct new and
>>>> better block. This block should be delimited by {{ and }}
>>>> if it's at all possible, and I think it is. No keyword,
>>>> just {{ and }}. This better block would bind vars, consts
>>>> and functions, just like function scopes do. In fact function
>>>> scopes and {{ }} would be the same thing, as seen by the
>>>> programmer.
>>>> An important advantage with {{ }} is that you can keep
>>>> everything contained without tedious and error-prone
>>>> repetition of local (or let) everywhere. And the scoping
>>>> is prominently visible and clearly structured.
>>>> It may seem odd that I say that adding yet another scoping
>>>> construct would make it simpler and easier to learn, but
>>>> if it's built this way it becomes conceptually clear, free
>>>> of hidden intricacies, easy to explain. The delimiters
>>>> {{ and }} suggest that you are walling things in with thicker
>>>> walls, so that hoisting can't get past. Nicely intuitive.
>>>> Especially for programmers who put braces at left it becomes
>>>> very clear indeed. And I'm sure syntax-coloring editors will
>>>> help making the scoping clear at a glance.
>>>> The terminology might distinguish between the two types of
>>>> block by talking about strong and weak blocks, where strong
>>>> means thick walls that you can't hoist out of, and weak means
>>>> that the block can only capture things that are marked local
>>>> (or let), and everything else gets hoisted out.
>>>> In fact I think a terminology with strong versus weak blocks
>>>> is clearer than the current terminology, where one type of
>>>> block is called block and the other is called variable object.
>>>> I'm sorry if this comes across as a series of complaints. All
>>>> in all I'm delighted with the many enticing improvements! But
>>>> listing all the nice things here wouldn't make for interesting
>>>> reading. And so it may sound much more negative than my overall
>>>> delighted and enthusiastic feelings.
>>> _______________________________________________
>>> Es3.x-discuss mailing list
>>> Es3.x-discuss at
> _______________________________________________
> Es3.x-discuss mailing list
> Es3.x-discuss at

Ingvar von Schoultz

------- (My quirky use of capitals in code comes from my opinion that
reserved and predefined words should all start with lowercase, and
user-defined should all start with uppercase, because this will easily
and elegantly prevent a host of name-collision problems when things
like programming languages are upgraded with new labels.)

More information about the Es4-discuss mailing list