function hoisting like var

Ingvar von Schoultz ingvar-v-s at comhem.se
Fri Jul 25 19:03:51 PDT 2008


waldemar at google.com wrote:
> Ingvar von Schoultz wrote:
>> waldemar at google.com wrote:
>>> I'm trying to keep the language relatively simple.
>> You can't get away from supporting this:
>>
>>     {
>>         function a(){}
>>         var b = a;
>>     }
> 
> What do you mean?  This is a syntax error in both ES3 and ES3.1.

It works fine in Firefox 2, Konqueror 3, Opera 9, Internet
Explorer 6, and server-side Rhino with JavaScript 1.6.

Five platforms out of five. Can you throw a syntax error here
and claim to be compatible?

You can test it in browsers at http://test-a-1.appjet.net/

Below is a more detailed example that also includes let. It
uses JavaScript 1.7 and works in Firefox 2. You can test it
at http://test-a-2.appjet.net/

     alert (b);
     {
         function a(){}
         var b = a;
         let c = 3;
         alert (c);
     }
     alert (b);
     alert (typeof c);

>> On the contrary, the functionality already exists, as shown
>> above.
> 
> It does not already exist in ES3 or ES3.1.

It exists on platforms as described above. I assumed that ES4
would be compatible.

>>> 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.
> 
> I don't understand.

In all normal cases the function is assigned at the beginning
of the scope. However, there are some special cases where this
would be an incorrect interpretation of the source code.

If the programmer clearly says that the assignment is /unknown/
at the beginning of the scope, then assigning prematurely would
be wrong.

For example, the programmer might say:

     if (Math.random() < 0.5)
         function f() {return 1}
     else
         function f() {return 2}

Implementations vary here, but let's assume that it is defined
to mean what it says.

Then f will be assigned either the first or the second body,
depending on the random number. This decision comes /after/
you enter the scope. The assignment depends on something that
happens /inside/ the scope. Therefore, here the programmer
is saying quite clearly that the assignment is /unknown/ at
the beginning of the scope.

It would be wrong for the compiler to guess. The solution is
to assign |undefined| at the beginning of the scope.

This situation is unusual. It happens only when the programmer
says that the assignment is unknown at the beginning of the
scope, as above.

>> 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.
> 
> That doesn't answer the question.  The problem is that the body can
> capture variables that haven't been declared yet.

With suitable arrangements this becomes impossible.

Of course if you /insist/ on setting everything up and assigning
to the function name before the body's context exists, then you
can't expect it to work. Then you get an incorrect program that
will act as you say. That's to be expected if you allow calling
a function whose context doesn't exist.

But if you accept that when the context doesn't exist you should
assign |undefined| at the beginning of the scope, and only assign
the body later, when its context does exist, then you get a well-
defined and well-behaved program.

Then any premature call to the function will throw an error as
a call to undefined(). This will work in ways that the programmer
can understand well.

As far as I can tell, this is correct by definition. Can you find
an example where it breaks?

>> The /name/ becomes a var. Treat it like any other var. Hoist
>> it and assign |undefined|, exactly like you do with other vars.
> 
> That's incompatible with how functions are defined in ES3.

Incompatible? Your comment on my tiny two-line program says
that you want to throw a syntax error in these cases. Breaking
five platforms out of five is less compatible than what I
describe.

It certainly doesn't break JavaScript 1.7 in Firefox. It's
almost exactly what Firefox does in this special situation.

According to at least one discussion in the ES3.1 archives,
ES3 does not specify how you should treat a function in this
special situation. Here's one of these comments:
https://mail.mozilla.org/pipermail/es4-discuss/2008-July/003142.html

>> 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.
> 
> Huh?  Do you understand how ES3 works?

Yes, I know it quite well. I've been using JavaScript near
full-time on the client since Netscape 3, and on both server
and client since Microsoft ASP with JScript first appeared.

But why do you refer to ES3? ES3 doesn't have any scoping
blocks with var hoisting out of them.

Could you please try to be more specific? What specifically
is the problem with my question? Asking if I understand ES3
really isn't in any way specific.

>> Please stop insisting that I'm proposing that nonsense. I'm
>> not. I never did.
> 
> I haven't seen a sensible and compatible proposal yet.

That's because you read wild ideas into my code snippets and
then assume that that's my proposal.

I'm always wondering if your vague protests, usually ending
in "so your proposal won't work," are about one of my real
proposals (and if so, which one), or if you have once again
looked at one of my code snippets and built theories about me
wanting to redefine the fundamentals of the language because
there's an if() before a var.

I'm quite astonished. I've read more than half the discussion
archives of ES3.1 and ES4. All other discussions seem serious.
You have some special problem with me. I seems it started when
you misunderstood that code snippet.

I care a lot about JavaScript, and have collected several ideas
over the years. Some of them could be quite useful. But I doubt
that I can present an idea here and expect it to be taken for
what it is.

The reason I'm reading the discussion archives so much is that
I want to see which ones among my ideas can be useful. I do this
because I care about contributing constructively. I'm serious
about such things.

Everywhere else I'm well respected. This is a new situation for
me. It feels unreal.

>>> 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.
> 
> So you're saying that your {{}} proposal has nothing to do with this?

Yes. We're discussing hoisting to a different scope. With {{ }}
there is no hoisting to a different scope. Everything stays in
the scope where it's declared.

Igor Bukanov made a nice summary by pointing out that {{ code }}
becomes syntax sugar for (function() { code })(). Indeed, if you
write the latter using ES3, without any scope blocks and let
declarations, that's exactly the scoping that you get with {{ }}.

And this shows that the {{ }} proposal is unrelated to function
hoisting like var. In that context there are no scope blocks that
var will hoist out of.

-- 
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