Regarding my explanations quoted below, did they clarify
things?
It would be a relief to know that things are now clear and
understandable, if they are. If there are still things
that seem unclear, strange, incompatible or impractical,
please ask about them!
After some thinking I've found that I could have explained
some of the things below better, making them clearer. Should
I post better explanations?
I'm really very curious about that rebinding problem! Both
what it means, and where I seem to propose it (or actually
propose it, inadvertently).
Ingvar
Ingvar von Schoultz wrote:
Sorry about the length of this, but I'm trying to cover the
unclear things, and often I don't know which things are unclear.
Brendan Eich wrote:
On Jul 26, 2008, at 2:07 PM, Ingvar von Schoultz wrote:
You can't get away from supporting this:
{
function a(){}
var b = a;
}
ES4 is planning to support function declarations locally
bound in blocks, so the above is valid ES4 code.
What you see above is function b() hoisting like var.
(I said b, not a.)
What you said does not make sense. It's true that var b is hoisted to
the top of the program or function body. But it is not initialized
until control flows through the assignment b = a that is part of the
var declaration. So there is no capture problem.
That's my point! There isn't any capture problem. That's exactly
what I'm showing here. And, more importantly, you can't insert a
capture problem while keeping the structure intact. The arrangement
is inherently well-behaved.
I'm trying to show that you can support functions that hoist like
var in a well-behaving way, and that this is /not/ complicated.
And the snippet is intended as proof.
But perhaps I should interpret what you said somewhat differently.
The snippet, as shown here, does not have a capture problem.
But if you improve the hoisting carelessly, there's a threat
of a capture problem lurking within it. (That's assuming that
I understand capture problem correctly -- this is a rare
case of Wikipedia not explaining a computing term.)
Somebody might want to improve the hoisting by making it so
that the function is assigned (is callable) from before we enter
the global scope.
Let's assume, for the argument, that the above code magically
starts to behave that way. Then it will work fine as long as it
stays the same. But later the programmer might decide to add a
local variable:
{
function a(){}
var b = a;
let c = 3;
}
c is now visible from inside a(), but exists only when we enter
the block. So in this situation a() must not be called before
we enter the block. The function must no longer be assigned
(callable) at the top of the global scope.
I would consider it extremely surprising semantics if I can call
a() above the block, but only before I add that local variable,
and this suddenly changes just because I add c. It's a huge change,
it's far away, and it's unrelated.
So for the sake of consistency and predictability we must assign
|undefined| whenever the code structure allows a capture problem
to be inserted as a side effect of doing something unrelated,
like adding |let c|.
That's why I said this, in the email where I first showed this
snippet:
,---
| 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.
`---
It may not look sequential, but that's just because I left out
a lot of details, in an attempt to keep it as brief as ever
possible, to minimize any misunderstandings.
As you can see, in my opinion, what I'm saying does make perfect
sense!
The claim that just because of this limitation my proposal
doesn't work is in my opinion quite mistaken. I consider
this functionality fully acceptable. It's simple, understandable
and predictable. Sure it's a limitation, but a minor one. It's
/far/ better and more useful and intuitive than having functions
not hoisting out at all.
The usual use case will be an if() or some such. Then the
programmer fully expects the function to be available only
afterward. Very useful. Using it in a bare block like the
above will be unusual, but if used, the rules are simple.
There is no far-too-complicated split-scope complexity. There
is no capturing of variables that haven't been declared yet.
It's simple, intuitive, well-defined and well-behaved.
Thanks, I agree. But it is not what you proposed.
Sorry, I don't understand. What is not what I proposed, and
in which one of my proposals did I not propose it?
Again, from
Waldemar's original reply, but with your proposed {{}} interpolated
and the elided code amended to say what the consequence is:
// outer scope
function c() ...;
// inner scope
{{
if (foo) {
const c = 37;
}
... c in