Re: lexical for-in/for-of loose end

2012-02-07 Thread Andy Wingo
Hi Allen,

On Mon, 2012-02-06 at 11:08 -0800, Allen Wirfs-Brock wrote:
 We're putting a lot of energy into trying to figure out how to fix
 for(let;;) when it probably shouldn't even be the preferred form of
 numeric looping going forward. If we can make for-in/for-of attractive
 enough then it is less clear why we need to fix for(;;)
[...]
 Maybe don't even add let/const forms to for(;;).

Just as food for thought, here's a C# designer on why they decided to
leave for (int i=0; iN; i++) alone, when they decided to make for
(int i in L) bind a fresh i:

We have this same problem in for blocks, but for blocks are
much looser about what the loop variable is; there can be more
than one variable declared in the for loop header, it can be
incremented in odd ways, and it seems implausible that people
would consider each iteration of the for loop to contain a
fresh crop of variables. When you say for(int i; i  10; i += 1)
it seems dead obvious that the i += 1 means increment the
loop variable and that there is one loop variable for the whole
loop, not a new fresh variable i every time through! We
certainly would not make this proposed change apply to for
loops.


http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

Regards,

Andy
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Herby Vojčík



Brendan Eich wrote:

Grant Husbands wrote:

* Both of them make closures in the init and in the rest see a
consistent world, for as long as is possible, while still giving body
closures unique copies per iteration.


Is there any inconsistency observable after as long as possible? I
can't find one.


It is a question if it is inconsistency, but init-closures called from 
inside body-closures have different impact when called while 
body-closure is live and after it is detached.


The from-live call works the actual variables, which the caller actually 
uses, but in the from-detached call, they work with the past-the-loop 
loop-context/last-iter-context variables, while the body-closure has its 
own local copies.



/be


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Herby Vojčík



Allen Wirfs-Brock wrote:

With body-swap it concerns me a bit that is an arbitrary and
variable number of closures may need to be updated each iteration
(consider a body containing a while loop that spits out closures that
capture init bindings.)


I think there are some lazy possibilities for that. Create the copy 
beforehand by sort-of Object.create(loopContext) then either use the 
transparent proxy to loopContext while running / iterEnd: populate copy 
with let-vars / proxy become: copy (pity there probably is not #become: 
(or #forwardBecome:) in vm) or store copy to @wouldBeCopy in 
loopContext / closure-created: store copy to [[ContextForDetachment]] in 
closure / use actual record (loop-context) since 
actual-record-to-use.@wouldBeCopy === [[ContextForDetachment]] / 
iterEnd: populate copy with let-vars  loopContext.@wouldBeCopy = null 
(later different object in subsequent iterations) / in detached closure, 
lazily do actual-context-to-use = [[ContextForDetachment]] since 
actual-record-to-use.@wouldBeCopy exists and is different from 
[[ContextForDetachment]].


Implementors can probably find better ones.


Allen


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Andreas Rossberg
On 6 February 2012 21:03, Herby Vojčík he...@mailbox.sk wrote:
 Well, in fact what I proposed was not changing the behaviour of
 head-closures, but body-closures. Basically, the idea was: let the loop run
 in legacy semantics (only one block for the whole for-loop). All closures
 (head ones and body ones) access the same let-var and modify it.
 But, for at the end of each iteration, if body-closures were created, rebind
 that closures to copy (or, better, child) of actual execution record with
 let-var being local (de-facto simulating per-iteration binding, but lazily;
 and only for async closures).

What are the closures that were created, and how do you keep track
of them? What should the following do?

let a = [], b = [], c = [], d = []
for (int i = 0, f = function(){ return function() { ++i } }; i  10;
d[i] = function() { return i }, ++i) {
  a[i] = f;
  b[i] = f();
  c[i] = function(){ return i }
}
print(c[2]()); print(d[2]())
a[4]()(); print(c[4]()); print(d[4]())
b[7](); print(c[7]()); print(d[7]())

And imagine the fun you can have with generators!


 Spec-wise, I think it only needs to define two things, CopyExecutionRecord,
 which Grant already mentioned, or something similar like
 CopyWithFixedVars(ExecutionRecord, vars-to-fix) and
 ReplaceExecutionRecordBinding to allow closure(s) bound to original
 execution record re-bind it to its copy created by the former.

Allow me to be blunt: this is literally raping the concepts of
declarative environment and lexical closure. It is a hack, completely
non-orthogonal, and I predict that the hidden statefulness it
introduces to closure environments would be going to bite back in gore
proportions.

Seriously, before we consider going there, I'd rather have a step on
the break and stick to C-style for-semantics.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


RE: lexical for-in/for-of loose end

2012-02-07 Thread Domenic Denicola
This seems like a highly compelling argument. I hope I'm not the only one who 
thinks the existing behavior of `for(;;)` makes sense. Granted, that comes from 
understanding the detail that closures close over variables and not values, 
which most users will not. But in general, `for(;;)` makes it very clear that 
there's a single mutating variable, which the desugarings proposed here seem to 
overcomplicate and contradict.

As exemplified by the comments to Eric's blog post, the current behavior is not 
very astonishing to many developers, and once it's explained once, becomes 
intuitive. (Yes, sampling bias, but still.) The question is whether saving that 
pedagogic burden is worthwhile.

I argue that it isn't. `for(;;)` is a low-level construct even in ES5, where 
`Array.prototype.forEach` supplants it for the most part. It doesn't have to do 
what I mean; it should do what I say. Introducing an extra set of curly 
braces around the loop so that my `let` loop variables don't get hoisted to 
the outer scope is the most work that makes sense to me.

-Domenic

-Original Message-
From: es-discuss-boun...@mozilla.org [mailto:es-discuss-boun...@mozilla.org] On 
Behalf Of Andy Wingo
Sent: Tuesday, February 07, 2012 5:15
To: es-discuss@mozilla.org
Subject: Re: lexical for-in/for-of loose end

Hi Allen,

On Mon, 2012-02-06 at 11:08 -0800, Allen Wirfs-Brock wrote:
 We're putting a lot of energy into trying to figure out how to fix
 for(let;;) when it probably shouldn't even be the preferred form of 
 numeric looping going forward. If we can make for-in/for-of attractive 
 enough then it is less clear why we need to fix for(;;)
[...]
 Maybe don't even add let/const forms to for(;;).

Just as food for thought, here's a C# designer on why they decided to leave 
for (int i=0; iN; i++) alone, when they decided to make for (int i in L) 
bind a fresh i:

We have this same problem in for blocks, but for blocks are
much looser about what the loop variable is; there can be more
than one variable declared in the for loop header, it can be
incremented in odd ways, and it seems implausible that people
would consider each iteration of the for loop to contain a
fresh crop of variables. When you say for(int i; i  10; i += 1)
it seems dead obvious that the i += 1 means increment the
loop variable and that there is one loop variable for the whole
loop, not a new fresh variable i every time through! We
certainly would not make this proposed change apply to for
loops.


http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered-harmful.aspx

Regards,

Andy
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Grant Husbands
On 7 February 2012 13:32, Andreas Rossberg rossb...@google.com wrote:
 What are the closures that were created,

They are the closures that lie lexically within the loop's code,
outside of the init, (or, put differently, those that have the loop's
environment record in scope) and that were instantiated in the current
iteration.

 and how do you keep track of them?

In a linked list or array. Somewhere that sits with the loop state. In
desugaring terms, it could be expressed as an array, perhaps of weak
pointers, in the outer loop scope. I haven't got up to speed on Chez
Scheme's closures enough to answer this question for those.

 What should the following do?
 let a = [], b = [], c = [], d = []
 for (int i = 0, f = function(){ return function() { ++i } }; i  10;
 d[i] = function() { return i }, ++i) {
  a[i] = f;
  b[i] = f();
  c[i] = function(){ return i }
 }
 print(c[2]()); print(d[2]())
 a[4]()(); print(c[4]()); print(d[4]())
 b[7](); print(c[7]()); print(d[7]())

Here's what it does under the proposed scheme:
c[2]() - 2
d[2]() - 3
a[4]()() increments the i that is 10 to 11.
c[4]() - 4
d[4]() - 5
b[7]() increments the i that is now 11 to 12.
c[7]() - 7
d[7]() - 8
Basically, the closures lexically within the loop init always see the
latest version of i. Those lexically within the rest of the loop
always see the i that was available at the end of their iteration. The
iteration part of the loop is considered to be at the start. A related
gotcha is that d[9]() would return 12, if added to the end of that
sequence of calls. (I'm answering to the best of my ability, not
trying to defend anything.)

 And imagine the fun you can have with generators!

Indeed, you'd need to alter the [[ExecutionContext]] of suspended
generators whose [[Code]] lies within the loop (as well as altering
the [[Scope]]), which may be a bigger deal than just modifying
[[Scope]]s.

 Allow me to be blunt: this is literally raping the concepts of
 declarative environment and lexical closure. It is a hack, completely
 non-orthogonal,

I think that may be more than blunt; it is strongly emotive and also
vague enough that it can't really be answered. However, I and others
do share the concern that it may introduce too much complexity, and
then only really add support for a very rare usage.

 and I predict that the hidden statefulness it introduces to closure
 environments would be going to bite back in gore proportions.

It's a possibility, indeed. The variant that alters only the closures
lexically within the loop init has less chance of that, and could
potentially have restriction imposed, but all of these variants do
indeed introduce a way of (slightly) modifying the scope of a
particular group of closures after they have been created.

 Seriously, before we consider going there, I'd rather have a step on
 the break and stick to C-style for-semantics.

I think we wouldn't need to go that far; your generalization of Mark's
desugaring covers plenty of use cases and certainly the one of most
common loop/closure gotchas. To my understanding, that's the favoured
idea, so far, and we're just exploring this one to see where it might
lead.

Regards,
Grant Husbands.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Andreas Rossberg
On 7 February 2012 15:40, Grant Husbands esdisc...@grant.x43.net wrote:
 On 7 February 2012 13:32, Andreas Rossberg rossb...@google.com wrote:
 What should the following do?
 let a = [], b = [], c = [], d = []
 for (int i = 0, f = function(){ return function() { ++i } }; i  10;
 d[i] = function() { return i }, ++i) {
  a[i] = f;
  b[i] = f();
  c[i] = function(){ return i }
 }
 print(c[2]()); print(d[2]())
 a[4]()(); print(c[4]()); print(d[4]())
 b[7](); print(c[7]()); print(d[7]())

 Here's what it does under the proposed scheme:
 c[2]() - 2
 d[2]() - 3
 a[4]()() increments the i that is 10 to 11.
 c[4]() - 4
 d[4]() - 5
 b[7]() increments the i that is now 11 to 12.
 c[7]() - 7
 d[7]() - 8
 Basically, the closures lexically within the loop init always see the
 latest version of i. Those lexically within the rest of the loop
 always see the i that was available at the end of their iteration. The
 iteration part of the loop is considered to be at the start. A related
 gotcha is that d[9]() would return 12, if added to the end of that
 sequence of calls. (I'm answering to the best of my ability, not
 trying to defend anything.)

But note that the environment in b's closures is not the for-loop
environment. Instead, it is a local environment, whose parent is the
for-loop environment. To make that case work, it is not enough to be
able to modify the [[Scope]] environment of closures -- in general,
you'd need to swap out arbitrary parts of an environment chain.


 Allow me to be blunt: this is literally raping the concepts of
 declarative environment and lexical closure. It is a hack, completely
 non-orthogonal,

 I think that may be more than blunt; it is strongly emotive and also
 vague enough that it can't really be answered. However, I and others
 do share the concern that it may introduce too much complexity, and
 then only really add support for a very rare usage.

You are right, I should have been more careful. I am sorry about that.

This is not merely a question of complexity, though. It's more
fundamental. Environments are immutable mappings from names to
locations -- that's a basic axiom of lexical scoping. Breaking it will
have unforeseeable consequences. That may be vague, I agree, but I
prefer not to find out concretely. :)  Subtle combinations of
higher-orderness and state rarely let you down in terms of nasty
surprises.


 Seriously, before we consider going there, I'd rather have a step on
 the break and stick to C-style for-semantics.

 I think we wouldn't need to go that far; your generalization of Mark's
 desugaring covers plenty of use cases and certainly the one of most
 common loop/closure gotchas. To my understanding, that's the favoured
 idea, so far, and we're just exploring this one to see where it might
 lead.

I am fine with either. So far I have favoured
fresh-variables-per-iteration, but the current discussion has raised
some doubts in the back of my mind.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Explicit local copies (was: Re: lexical for-in/for-of loose end)

2012-02-07 Thread Herby Vojčík



Andreas Rossberg wrote:

let a = [], b = [], c = [], d = []
for (int i = 0, f = function(){ return function() { ++i } }; i  10;
d[i] = function() { return i }, ++i) {
  a[i] = f;
  b[i] = f();
  c[i] = function(){ return i }
}


But note that the environment in b's closures is not the for-loop
environment. Instead, it is a local environment, whose parent is the
for-loop environment. To make that case work, it is not enough to be
able to modify the [[Scope]] environment of closures -- in general,
you'd need to swap out arbitrary parts of an environment chain.


Oww, that's a problem. Copies are hard here... refs can do it, but no 
one likes them.



I am fine with either. So far I have favoured
fresh-variables-per-iteration, but the current discussion has raised
some doubts in the back of my mind.


This whole discussion leads me to completely different opinion: what 
about to embrace explicit is better than implicit for this case, and 
allow let/const without initializer to create local copy of an outer 
variable?


Then, if you need to capture a value, you write it down (I know this 
appeared to avoid capturing by hand in (function (i) { ... })(i), but 
reusing normal blocks to do it may work, wouldn't it?


It disallows some of the ninjutsu (init-closures will work with 
different i than localized closures), but it will be clearly visible:


  let a = [], b = [], c = [], d = [], e = []
  for (int i = 0, f = function(){ return function() { ++i } }; i  10;
d[i] = function() { return i }, ++i) {
a[i] = f; // this
b[i] = f(); // and this
c[i] = function(){ return i } // and this work with loop-level i
{
   let i;
   e[i] = function () { return i; } // this has local i
}
  }


/Andreas


There can be even shorter syntax, like
   e[i] = function () let i { return i; } // this has local i
or
   e[i] = function () { let i; return i; } // this has local i
whoch can avoid need of {} block, but it must be thought of if it is 
possible consistently (the former should, the latter may be questionable).


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-07 Thread Grant Husbands
Andreas Rossberg wrote:
 let a = [], b = [], c = [], d = []
 for (int i = 0, f = function(){ return function() { ++i } }; i  10;
 d[i] = function() { return i }, ++i) {
  a[i] = f;
  b[i] = f();
  c[i] = function(){ return i }
 }
 But note that the environment in b's closures is not the for-loop
 environment. Instead, it is a local environment, whose parent is the
 for-loop environment. To make that case work, it is not enough to be
 able to modify the [[Scope]] environment of closures -- in general,
 you'd need to swap out arbitrary parts of an environment chain.

Indeed. I specified that operation as a recursive, constructive
operation, in something like spec-lingo, in one of my emails, and
pointed out that it is likely to introduce an extra indirection in
most JS engines, for at least some scopes, due to its assumptions
about envRec; I noted (and Brendan agreed) that such could be a major
problem. To recap, it looked like this (this time writing it as JS):
function ReplaceEnvInEnv(E, C) {
  if (E==C) return new Env(E.outer, E.envRec.clone());
  else return new Env(ReplaceEnvInEnv(E.outer, C), E.envRec);
}
(You would pass it the [[Scope]] of a closure in E and the loop
iteration scope in C and it would give you a new [[Scope]] for that
closure.)

I'm hoping that someone knowledgeable about Chez Scheme's flat
closures will let us know about the similar detail of that and the
pitfalls.

 This is not merely a question of complexity, though. It's more
 fundamental. Environments are immutable mappings from names to
 locations -- that's a basic axiom of lexical scoping.

Well, lexical scoping is scoping in which names refer to (more or
less) the local lexical environment. That is still true under the
proposal. It might indeed be desirable for the mapping to be
immutable, but I don't think that's a given. The above recursive
design does not require direct alteration of mappings.

 Breaking it will have unforeseeable consequences.

I'd agree with a phrasing like Breaking it may have unforeseen consequences.

 That may be vague, I agree, but I
 prefer not to find out concretely. :)  Subtle combinations of
 higher-orderness and state rarely let you down in terms of nasty
 surprises.

I agree that this proposal may run up against some of the core values
and idioms of javascript and may want to be rejected for that reason;
in fact, I would probably currently vote against it, if I were asked
to participate in a vote. However, I'll continue to try to make sure
we're all on the same page as regards the potential design details and
workarounds, and hope not to bother people too much.

Regards,
Grant.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Andreas Rossberg
On 4 February 2012 21:55, Brendan Eich bren...@mozilla.org wrote:
 The argument is as follows:

  for (let i = 0, a = some.array, n = a.length; i  n; i++) { ... }

 here we definitely want the a in a.length (n's initializer) to be scoped by
 the head let -- to be the a declared by the second declarator.

 Now consider a bit of eta conversion:

  for (let i = 0, a = some.array, n = (function(){return a})().length; i  n;
 i++) { ... }

Nit: That is a beta-conversion, not an eta-conversion. ;-)
(Fortunately, because eta-conversions are not actually
semantics-preserving in an impure language.)


 I claim implementation is not the driver here. User expectations, esp. savvy
 users who might make some practical or theoretical (testing) use of eta
 conversion, matter more.

Agreed. As long as we don't spec something weird, the extra effort for
implementations shouldn't be much more than that of an extra block
around the loop body.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Andreas Rossberg
On 4 February 2012 18:49, Brendan Eich bren...@mozilla.org wrote:
 I agree we want to capture the first-iteration bindings in any closures in
 those declarators' initializers.

 This requires unrolling the loop once. Let's see how the desugaring from:

  for (let d1 = e1, ... dN = eN; cond; update) {
    body;
  }

 looks. It doesn't seem terrible:

  $loopEnd: {
    let d1 = e1, ... dN = eN;
    if (cond) {
      body;
      update;
      const $loop = { |d1, ... dN|
        if (!cond) break $loopEnd;
        body;
        update;
        $loop(d1, ... dN);
      }
      $loop(d1, ... dN);
    }
  }

I'm at a loss what this is trying to achieve. Modifications of the
loop variables by the loop _body_ will be visible to future iterations
if they happen in the first, but not if they happen in any consecutive
iteration? That seems odd. What is the use case for this, and why
should we support it as a special case?

In other words, why isn't the following good enough?

{
 let d1 = e1, ..., dN = eN;
 const $loop = { |d1, ..., dN|
   if (cond) {
 body
 update;
 $loop(d1, ..., dN);
   }
 }
 $loop(d1, ..., dN);
}

FWIW, this is simply the generalization of Mark's desugaring so that
it works with destructuring, multiple bindings, and recursive
bindings. Plus, it removes the redundant second lambda in his version.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Andreas Rossberg wrote:

On 4 February 2012 21:55, Brendan Eichbren...@mozilla.org  wrote:

  The argument is as follows:

for (let i = 0, a = some.array, n = a.length; i  n; i++) { ... }

  here we definitely want the a in a.length (n's initializer) to be scoped by
  the head let -- to be the a declared by the second declarator.

  Now consider a bit of eta conversion:

for (let i = 0, a = some.array, n = (function(){return a})().length; i  n;
  i++) { ... }


Nit: That is a beta-conversion, not an eta-conversion.;-)
(Fortunately, because eta-conversions are not actually
semantics-preserving in an impure language.)


(B-key was sticky :-P)

Thanks...



  I claim implementation is not the driver here. User expectations, esp. savvy
  users who might make some practical or theoretical (testing) use of [beta]
  conversion, matter more.


Agreed. As long as we don't spec something weird, the extra effort for
implementations shouldn't be much more than that of an extra block
around the loop body.


To take Allen's best shot and re-fire it, what do you think should 
happen here?


  for (let i = 0, skip2 = function(){i++}; i  N; i++) {
foo();
if (bar())
  skip2();
  }

/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Andreas Rossberg wrote:

On 4 February 2012 18:49, Brendan Eichbren...@mozilla.org  wrote:
In other words, why isn't the following good enough?

{
  let d1 = e1, ..., dN = eN;
  const $loop = {|d1, ..., dN|
if (cond) {
  body
  update;
  $loop(d1, ..., dN);
}
  }
  $loop(d1, ..., dN);
}


This is the 0th iteration scope idea. It's good enough if (a) we want 
skip2 (see last post) affecting no iteration's loop variables, and (b) 
we can take the extra scope cost-hit.



FWIW, this is simply the generalization of Mark's desugaring so that
it works with destructuring, multiple bindings, and recursive
bindings. Plus, it removes the redundant second lambda in his version.


It's fine if we like (a) on usability grounds, and (b) is true for all 
implementors and wtf-benchmarketing kooks.


I argued vigorously for 1st-iteration rather than 0th-iteration scope 
but that was to say no to (b). I'm actually not sure what skip2-coding 
authors will want -- probably an error as Allen suggests. But let's say 
yes to (a) -- I can live with (b) absent further evidence.


The issue remains (a). Allen's best shot is the skip2 scenario (advance 
in his post). What's the right outcome for users who write such code?


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Andreas Rossberg
On 6 February 2012 16:28, Brendan Eich bren...@mozilla.org wrote:
 Andreas Rossberg wrote:
 Agreed. As long as we don't spec something weird, the extra effort for
 implementations shouldn't be much more than that of an extra block
 around the loop body.

 To take Allen's best shot and re-fire it, what do you think should happen
 here?

  for (let i = 0, skip2 = function(){i++}; i  N; i++) {
    foo();
    if (bar())
      skip2();
  }

Pretty much the same as in

  let i_ = 0, skip2_ = function(){i_++};
  for (; i_  N; i_++) {
let i = i_, skip2 = skip2_;
    foo();
    if (bar())
      skip2();
  }

Probably not what the programmer expected, but as others have said,
it's a trade-off. This use case seems rare, rather obfuscated style
IMHO, and you can easily work around it. So probably not something you
want to be optimizing the language semantics for.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Andreas Rossberg wrote:

On 6 February 2012 16:28, Brendan Eichbren...@mozilla.org  wrote:

  Andreas Rossberg wrote:

  Agreed. As long as we don't spec something weird, the extra effort for
  implementations shouldn't be much more than that of an extra block
  around the loop body.


  To take Allen's best shot and re-fire it, what do you think should happen
  here?

for (let i = 0, skip2 = function(){i++}; i  N; i++) {
  foo();
  if (bar())
skip2();
}


Pretty much the same as in

   let i_ = 0, skip2_ = function(){i_++};
   for (; i_  N; i_++) {
 let i = i_, skip2 = skip2_;
 foo();
 if (bar())
   skip2();
   }


Yes, this is something Jon Zeppieri brought up as a reason not to do 
fresh-binding-per-iteration in for(let;;) -- moving the initializer part 
out of the loop loses the binding-per-iteration.


You've shown b-p-i restored manually, with i_ vs. i renaming, but in the 
likely scenarios the user sees only one i variable.


Try this version:

  let i = 0, skip2_ = function(){i++};
  for (; i  N; i++) {
foo();
if (bar())
  skip2();
  }


This must work (skip2 must advance the one i binding; note no closure 
capture here) just as it does today. Jon was arguing against a change in 
binding semantics simply due to moving the let into the for-head.


We who were in the TC39 meeting room late the second day have overcome 
Jon's good counter-argument. For the greater good on balance, many of us 
TC39ers want to make for(let;;) a special form, so closure capture works 
as most users expect.


You're right that the trade-off where skip2 fails to update any useful 
loop variable (or the first iteration's loop variable only -- agreed 
that is odd) follows from this greater-good choice. But could we do better?


Making dynamic errors out of skip2-like capture seems bad. We can't 
guarantee early errors. Probably best to avoid adding error cases...



Probably not what the programmer expected, but as others have said,
it's a trade-off. This use case seems rare,


Agreed on rare.


  rather obfuscated style


This isn't so clear to me, since the case where the let is manually 
hoisteid from the for head does work, and hackers who learned C or C++ 
may think that moving the let into the head is just better style, not 
obfuscatory.



IMHO, and you can easily work around it. So probably not something you
want to be optimizing the language semantics for.


I agree with you, but feel a small nagging doubt.

/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Grant Husbands
Brendan Eich wrote:
 [Grant Husbands wrote:]
 'Note' all closures (dynamically) created in (lexically) the loop
 initializer.

 Only in the initializer? Why should closures formed there be dynamically
 scoped to the current iteration?

Because that directly serves the use cases that have
for-initialization-based closures modifying loop variables. None of
the simple desugarings do that, beyond perhaps the first iteration.

 Instead [this requires] a dynamic scope for closures in the initializer
[...]
 Dynamic scope is a warning sign, almost always a mistake.

Indeed, given the eval shape-changing problem you pointed out, this
cannot be properly resolved, in the first form of this proposal. The
second form might not require generalised dynamic scope, though; it
requires an operation that clones a scope with a single environment
record changed, though. More on that, later.

 Or, here's one that copies the other way (and is probably cleaner):
 'Note' all closures (dynamically) created in (lexically,
 post-desugaring) the loop body. Each time you end an iteration, update
 all the loop variable activation record pointers to point at a new
 clone of that activation record.

 This is a more complex spec than one that models each iteration having its
 own lexical scope. The spec needs only declarative environments, not hidden
 references and pointer updates.

I think there may be a misunderstanding, as the operation I'm talking
about can be given in spec language without talk of references and
pointer updates. I'll write it out at the end of this email.

 As an implementation technique, Chez Scheme's heap boxing and assignment
 conversion could be even better.

Indeed, and that introduction of a level of indirection for affected
variables is what Herby was effectively suggesting, if I'm
understanding you both correctly. I was trying to suggest something I
thought was more compatible with the current specification.

  But this is all beyond the spec.

I don't think it is. What Herby's idea and these formulations present
is a way for let-based loops to have modifications in closures
captured in the for-head that alter the loop variables in a way that's
visible to the current loop iteration. As such, choosing whether or
not to use these formulations affects the spec.

I agree that it may indeed be too large a feature, given that
desugarings can cover the vast majority of use-cases well enough. I
just thought it worth following the logic through.

 Still trying to be sure you intended a unique and dynamic scope for the
 initializer (first part) of for(let;;).

In the first version, depending on the definition of dynamic, yes.
In the second version, no, though instead closures inside the loop
body get a similarly 'dynamic' scope.

Here's a longer, still informal version of the second.

Given a loop of this form:
for (let i = 0, inc = function(){i++}; iN; inc()) { ... }
We want the loop to run to completion while ensuring that each closure
within the body gets a copy of 'i' as it existed in the loop iteration
in which the closure was created. One way to do that is to keep track
of the closures that get created during a loop iteration and, at the
end of the loop iteration, give those closures a new environment
([[Scope]]) which is a copy of the old one, but with a clone of the
current loop body environment record replacing the original.

In terms of spec: The 'add the closure to a list' operation could be
described using a desugaring. The 'scope clone with one record
changed' operation and the end-of-iteration use of it would require
spec changes.

In spec language, the main operation would probably be like this (I'm
cargo-culting the format, though, so I apologise if it has
idiosyncrasies):
10.2.2.9 CopyDeclarativeEnvironment (E, C)
When the abstract operation CopyDeclarativeEnvironment is called with
a Lexical Environment
as argument E and a Lexical Environment as argument C the following
steps are performed:
1. Let env be a new Lexical Environment.
2. Let lastOuter be the outer lexical environment of E
3. If E and C are the same:
  a. Let envRec be a copy of the environment record of E.
  b. Set env's environment to be envRec
  c. Set the outer lexical environment reference of env to lastOuter
4. Else
  a. Set the environment record of env  to be the environment record of E
  b. Let outer be the result of calling CopyDeclarativeEnvironment
passing lastOuter and C as the arguments
  c. Set the outer lexical environment reference of env to be outer
5. Return env.

Probably the biggest issue with the above spec is that it assumes
envRec inside an environment is reference-like, which it could easily
not be in current implementations, so this variant would also
introduce an extra indirection in at least some circumstances.

I support the two main destructurings under consideration right now
more than I support the above, though.

Regards,
Grant.
___
es-discuss mailing list

Re: lexical for-in/for-of loose end

2012-02-06 Thread Allen Wirfs-Brock

On Feb 6, 2012, at 7:59 AM, Brendan Eich wrote:

 Andreas Rossberg wrote:
 ...
 
 Yes, this is something Jon Zeppieri brought up as a reason not to do 
 fresh-binding-per-iteration in for(let;;) -- moving the initializer part out 
 of the loop loses the binding-per-iteration.
 
 You've shown b-p-i restored manually, with i_ vs. i renaming, but in the 
 likely scenarios the user sees only one i variable.
 
 Try this version:
 
  let i = 0, skip2_ = function(){i++};
  for (; i  N; i++) {
foo();
if (bar())
  skip2();
  }
 
 This must work (skip2 must advance the one i binding; note no closure 
 capture here) just as it does today. Jon was arguing against a change in 
 binding semantics simply due to moving the let into the for-head.
 
 We who were in the TC39 meeting room late the second day have overcome Jon's 
 good counter-argument. For the greater good on balance, many of us TC39ers 
 want to make for(let;;) a special form, so closure capture works as most 
 users expect.
 
 You're right that the trade-off where skip2 fails to update any useful loop 
 variable (or the first iteration's loop variable only -- agreed that is odd) 
 follows from this greater-good choice. But could we do better?
 
 Making dynamic errors out of skip2-like capture seems bad. We can't guarantee 
 early errors. Probably best to avoid adding error cases...

I probably could be made an early error.  Whether or not that would be a good 
things is a different issue.

 
 Probably not what the programmer expected, but as others have said,
 it's a trade-off. This use case seems rare,
 
 Agreed on rare.
 
  rather obfuscated style
 
 This isn't so clear to me, since the case where the let is manually hoisteid 
 from the for head does work, and hackers who learned C or C++ may think 
 that moving the let into the head is just better style, not obfuscatory.

Also, while it doesn't make a lot of sense for i++ it might if the actual 
skip code code was a complex expression containing long names or if it occurs 
at multiple places in the loop body.

 
 IMHO, and you can easily work around it. So probably not something you
 want to be optimizing the language semantics for.
 
 I agree with you, but feel a small nagging doubt.

We're putting a lot of energy into trying to figure out how to fix for(let;;) 
when it probably shouldn't even be the preferred form of numeric looping going 
forward. If we can make for-in/for-of attractive enough then it is less clear 
why we need to fix for(;;)

for (var i=0, nn; I++) capture(function () {i});

still have to be modified to 

for (let i=0, nn; I++) capture(function () {i});

to fix the capture bug in existing code. Why not make the new better way 
something like

for (let i of range(0,n)) capture(function () {i});

and promote the hell out of it while leavingfor(;;) as is.  Maybe don't even 
add let/const forms to for(;;).

Allen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Grant Husbands wrote:

  But this is all beyond the spec.


I don't think it is. What Herby's idea and these formulations present
is a way for let-based loops to have modifications in closures
captured in the for-head that alter the loop variables in a way that's
visible to the current loop iteration.


And read back changes from the current iteration.

Yes, I see that a new (novel!) observable semantic model is being 
proposed here, not an optimization. At first it seemed Herby was 
proposing just the usual optimizations.


I still do not think it's wise to specify in terms of such 
pointer-updating reference semantics, not for the body closures that 
want to capture loop control variables. But only if closures in the 
for-head capture loop variables? That would be Allen's DWIM (Do What I 
mean) semantics.


DWIM is tempting. Perl is full of it and it can be convenient at first. 
It often blows back, badly, due to ambiguity.



  As such, choosing whether or
not to use these formulations affects the spec.


Agreed, with caution about bending the body closure model around this 
prematurely. If we pull it off, probably the body closures can do the 
same optimization -- but it's not clear we can pull it off.



I agree that it may indeed be too large a feature, given that
desugarings can cover the vast majority of use-cases well enough. I
just thought it worth following the logic through.


Gotcha.

It is tempting to try for this, since it would let a C++-knowing hacker 
write a loop like the one you show below, and have it work -- while the 
common var-capture pigeon-hole problem would be solved too for 
let-bindings captured by body-closures.



  Still trying to be sure you intended a unique and dynamic scope for the
  initializer (first part) of for(let;;).


In the first version, depending on the definition of dynamic, yes.
In the second version, no, though instead closures inside the loop
body get a similarly 'dynamic' scope.

Here's a longer, still informal version of the second.

Given a loop of this form:
for (let i = 0, inc = function(){i++}; iN; inc()) { ... }
We want the loop to run to completion while ensuring that each closure
within the body gets a copy of 'i' as it existed in the loop iteration
in which the closure was created. One way to do that is to keep track
of the closures that get created during a loop iteration and, at the
end of the loop iteration, give those closures a new environment
([[Scope]]) which is a copy of the old one, but with a clone of the
current loop body environment record replacing the original.

[snip]

Probably the biggest issue with the above spec is that it assumes
envRec inside an environment is reference-like, which it could easily
not be in current implementations, so this variant would also
introduce an extra indirection in at least some circumstances.


This is a big issue, I agree.


I support the two main destructurings under consideration right now
more than I support the above, though.


Any preference for 0th over 1st iteration scope for closures in INIT 
binding initializer expressions?


You and Allen remind me that the whole head needs thought:

  for (INIT; TEST; NEXT) BODY

If we do what Andreas suggests, 0th iteration scope for INIT, then TEST 
must use BODY scope for the iteration that follows if TEST evaluates 
truthy. And NEXT must use a new scope for the next iteration too, 
preparing for TEST.


enterblock V
INIT
reenterblock  // Andreas's suggested 0th iteration scope for INIT
goto L2
L1: BODY
reenterblock
NEXT
L2: TEST
iftrue L1
leaveblock

This is not going to give TEST and NEXT the DWIM semantics. Sorry, I'm 
sure you get this, I'm just spelling it out to be sure everyone 
(including me) gets it.


Is this a problem? I don't know. INIT may want the DWIM semantics, but 
TEST and NEXT before it for 2nd through Nth iterations really are more 
like parts of the BODY. Why should they form closures that magically 
reference current iteration scope when called later?


DWIM always falls to ambiguity. What did you mean? I dunno, just do it! :-P

/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Allen Wirfs-Brock wrote:

We're putting a lot of energy into trying to figure out how to fix for(let;;) 
when it probably shouldn't even be the preferred form of numeric looping going forward. 
If we can make for-in/for-of attractive enough then it is less clear why we need to fix 
for(;;)

for (var i=0, nn; I++) capture(function () {i});

still have to be modified to

for (let i=0, nn; I++) capture(function () {i});

to fix the capture bug in existing code. Why not make the new better way 
something like

for (let i of range(0,n)) capture(function () {i});


I'm a big fan of iterators and this will be winning. Indeed many 
languages add a range operator for more convenience:


  for (let i of 0..n) ...

or perhaps ... (yech).


and promote the hell out of it while leavingfor(;;) as is.


Here I demur. We can promote all we want. Won't necessarily do a thing 
to JS developers who see for(let;;) as a better way to write (or 
migrate, note well) code they wrote (or inherited, double note well) 
that uses for(var;;).




   Maybe don't even add let/const forms to for(;;).


Again I dissent. We're not making a new language, so breaking symmetry 
to deprecate looks like nanny-ing. It usually fails, in my experience. 
It doesn't help migration.


You make a good point: we should not overcomplicate for(let;;) at high 
cost. I'm happy with Andreas's proposed 0th-iter scope desugaring, 
modulo nagging doubts that are going away even as I type.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Allen Wirfs-Brock

On Feb 6, 2012, at 11:24 AM, Brendan Eich wrote:

 
 You make a good point: we should not overcomplicate for(let;;) at high cost. 
 I'm happy with Andreas's proposed 0th-iter scope desugaring, modulo nagging 
 doubts that are going away even as I type.


particularly, when somebody who really wants per loop bindings they can easily 
say:

{ let i;
   for (;in;i++; {
...
   }
}
 
Allen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Herby Vojčík

Brendan Eich wrote:

Grant Husbands wrote:

But this is all beyond the spec.


I don't think it is. What Herby's idea and these formulations present
is a way for let-based loops to have modifications in closures
captured in the for-head that alter the loop variables in a way that's
visible to the current loop iteration.


And read back changes from the current iteration.

Yes, I see that a new (novel!) observable semantic model is being
proposed here, not an optimization. At first it seemed Herby was
proposing just the usual optimizations.


Well, I hope I was understood. Even if not and something interesting 
sprang out, it's ok. I'll go into details later.



I still do not think it's wise to specify in terms of such
pointer-updating reference semantics, not for the body closures that
want to capture loop control variables. But only if closures in the
for-head capture loop variables? That would be Allen's DWIM (Do What I
mean) semantics.

DWIM is tempting. Perl is full of it and it can be convenient at first.
It often blows back, badly, due to ambiguity.


As such, choosing whether or
not to use these formulations affects the spec.


Agreed, with caution about bending the body closure model around this
prematurely. If we pull it off, probably the body closures can do the
same optimization -- but it's not clear we can pull it off.


Well, in fact what I proposed was not changing the behaviour of 
head-closures, but body-closures. Basically, the idea was: let the loop 
run in legacy semantics (only one block for the whole for-loop). All 
closures (head ones and body ones) access the same let-var and modify it.
But, for at the end of each iteration, if body-closures were created, 
rebind that closures to copy (or, better, child) of actual execution 
record with let-var being local (de-facto simulating per-iteration 
binding, but lazily; and only for async closures).


Spec-wise, I think it only needs to define two things, 
CopyExecutionRecord, which Grant already mentioned, or something similar 
like CopyWithFixedVars(ExecutionRecord, vars-to-fix) and 
ReplaceExecutionRecordBinding to allow closure(s) bound to original 
execution record re-bind it to its copy created by the former.


It's all. It solves this concern of Allen:

In the case where there are no closure captures in the body we
probably all expect implementations to actually share a single set of
bindings across all iterations. If an implementation did that for
this case it would yield the result the programmer to expecting.  But
if an implementation actually did that in this case it would be an
invalid optimization because of the proposed bind to only first
iteration semantics.


With what I proposed, there is no 0th / 1st iteration semantics etc. 
since all the loop shares the binding through its whole run.



...

Any preference for 0th over 1st iteration scope for closures in INIT
binding initializer expressions?

...

INIT may want the DWIM semantics, but TEST and NEXT before it for 2nd
through Nth iterations really are more like parts of the BODY. Why
should they form closures that magically reference current iteration
scope when called later?


I definitely think INIT should not have any special behaviour for 1st 
and for subsequent iterations. The semantics of the loop is then much 
more straightforward and less magical. So in case of these desugaring, I 
am for 0th iteration. To be able to read loop as { INIT; for 
(;TEST;NEXT) BODY }.



DWIM always falls to ambiguity. What did you mean? I dunno, just do it! :-P

/be


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Herby Vojčík

Herby Vojčík wrote:

I definitely think INIT should not have any special behaviour for 1st
and for subsequent iterations. The semantics of the loop is then much
more straightforward and less magical. So in case of these desugaring, I
am for 0th iteration. To be able to read loop as { INIT; for
(;TEST;NEXT) BODY }.


Of course, with let vars bound to closure per-body... but what I mean 
conceptually draw a line between initialization and loop body, and 
making each loop body obey the same rules. Even if it means head-closure 
will have no effect. But it will not have it regularly, in first as well 
in next iterations.



Herby

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Grant Husbands wrote:

Now, there are some advantages and disadvantages:


Thanks for expanding on your and Herby's proposals, it helps. A 
disadvantage on its face of Herby's init-swap is that variables are 
copied from iteration scope to iteration scope, *and* all contained 
closures need their [[Scope]] adjusted every iteration.


For the code you evaluate Herby's body-swap against, the flat 
closure (Chez Scheme display closure) formation is indeed good 
optimization. The closure passed to setTimeout post-dominates the only 
live (from its point of view) assignments to i, so copying i's value 
into the closure allows very fast access and no scope entrainment at all.


A harder case would mutate upvars after the closure is evaluated, so 
that it must capture references not values. In any case, the scope would 
need to be cloned on each iteration where closures formed, and those 
closures' [[Scope]] internal properties updated.


Allen should comment on whether this attempt to square the circle 
(kidding, mostly) might fly in the spec. Implementors while secondary 
should weigh in too.



* Herby's body-swap gives an apparent desugaring with just one scope
for the loop variables


Yes, that is tempting. Kudoes to you and Herby for pushing this. Great 
use of es-discuss.



* Herby's init-swap might need to be concerned about shape changes
(due to eval or such).


Yes.


* Both of them pretty much require some form of scope cloning or scope
modification that does not match anything currently in the spec.


This is another one for Allen.


* Both of them make closures in the init and in the rest see a
consistent world, for as long as is possible, while still giving body
closures unique copies per iteration.


Is there any inconsistency observable after as long as possible? I 
can't find one.



Sorry for the long email, but I thought the detail could help iron out
any of the remaining (lesser) misunderstandings.


No need to apologize. My warnings about DWIM are general, and you and 
Herby have proposed specific solutions. Worth going over in detail. 
Thanks again,


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Allen Wirfs-Brock

On Feb 6, 2012, at 5:32 PM, Brendan Eich wrote:

 Grant Husbands wrote:
 Now, there are some advantages and disadvantages:
 
 Thanks for expanding on your and Herby's proposals, it helps. A disadvantage 
 on its face of Herby's init-swap is that variables are copied from 
 iteration scope to iteration scope, *and* all contained closures need their 
 [[Scope]] adjusted every iteration.
 
 For the code you evaluate Herby's body-swap against, the flat closure 
 (Chez Scheme display closure) formation is indeed good optimization. The 
 closure passed to setTimeout post-dominates the only live (from its point of 
 view) assignments to i, so copying i's value into the closure allows very 
 fast access and no scope entrainment at all.
 
 A harder case would mutate upvars after the closure is evaluated, so that it 
 must capture references not values. In any case, the scope would need to be 
 cloned on each iteration where closures formed, and those closures' [[Scope]] 
 internal properties updated.
 
 Allen should comment on whether this attempt to square the circle (kidding, 
 mostly) might fly in the spec. Implementors while secondary should weigh in 
 too.

My sense is that we could make these schemes works in spec. space.  Either by 
allowing bindings that have References as values or by adding new mechanisms 
such environment cloning.  

It actually sounds fun and perhaps even the right thing to do.  However,  I 
have some doubts concerning whether the added work and spec. complexity is 
really justified by the benefit.  Maybe, but there are lots seemingly more 
important things to work on.

With body-swap it concerns me a bit that is an arbitrary and variable number 
of closures may need to be updated each iteration (consider a body containing a 
while loop that spits out closures that capture init bindings.)

For both, I need to think about whether it may be possible (and the impact) for 
a closure activation (containing a generator?) to span a iteration boundary.

Allen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-06 Thread Brendan Eich

Allen Wirfs-Brock wrote:

For both, I need to think about whether it may be possible (and the impact) for 
a closure activation (containing a generator?) to span a iteration boundary.


Generators close over their lexical environment, yes. And of course an 
active generator can survive invoking contexts such as a single loop 
iteration.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-05 Thread Herby Vojčík

Grant Husbands wrote:

Herby Vojčík wrote:

It looks to me that all those schemes and desugaring are very complicated


The full problem is complicated, so that's to be expected.


I meant all that copying there and back and having two variables with 
the same name in two scopes and handling all combinations when to access 
which one. I wanted to take this away.



What is bad with scenario of reusing the same variable and isolating it as
local only for the async closures?


Your proposal depends on being able to reassign variable pointers, but
they don't necessarily exist. Though I haven't written a JS engine, I
believe they are allowed to have variables directly in an activation
record (or environment instance) without any (pointer) indirection, so
they'd have no mechanism for performing the operation you describe.


Well, references are all over the spec (or were in times of ES5). Yes, 
they are only virtual objects of the spec, they are not first-class 
and probably not even exist in the implementation; but I just reused 
them in my proposal. So that there will be loop-body-local variable 
_iter_record; 'i' inside body will be Ref(_iter_record, 'i'), and while 
the loop is running, _iter_record is _outer_shared_record, and when 
ending the iteration, _iter_record is assigned to _local_iter_record.


Of course if there are no closures, all this can be optimized away, or 
if eval is not used in them, only those variables that needs to be 
localized should be; but that's beyond scope.


(No, I am not proposing optimization, as Brendan said; I am proposing 
different approach with lazy fixing locals so that complications are not 
present while the loop is running, only when closures are run outside loop)



Or, here's one that copies the other way (and is probably cleaner):
'Note' all closures (dynamically) created in (lexically,
post-desugaring) the loop body. Each time you end an iteration, update
all the loop variable activation record pointers to point at a new
clone of that activation record.


If I understood correctly, this is what I proposed. Or maybe it only 
looks like it?



In each case, you require a list of not-necessarily-predictable size
to note the closures in. That's not a big problem; it's just something
you need to be aware of.


This I did not understand.


Regards,
Grant.


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-05 Thread Grant Husbands
Herby Vojčík wrote:
 I meant all that copying there and back and having two variables with the
 same name in two scopes and handling all combinations when to access which
 one. I wanted to take this away.

Yes, at least my desugaring was indeed overcomplicated. Brendan's and
Mark's desugarings aren't that complicated, though.

 Your proposal depends on being able to reassign variable pointers, but
 they don't necessarily exist.

 Well, references are all over the spec (or were in times of ES5).

What I was calling activation records, the spec calls environment
records and it does not, by my reading, imply that variables within
them are reference-like in nature (read clause 10.2). However, we
don't need to argue this point.

 Or, here's one that copies the other way (and is probably cleaner):
 'Note' all closures (dynamically) created in (lexically,
 post-desugaring) the loop body. Each time you end an iteration, update
 all the loop variable activation record pointers to point at a new
 clone of that activation record.

 If I understood correctly, this is what I proposed. Or maybe it only looks
 like it?

It has the same behaviour, but without needing variable pointers; that
was the idea. I now propose it to the list as a variant of your idea
that I think some may prefer. I'm merely trying to make sure your idea
gets the attention it deserves.

To be completely acceptable, the mechanics would need fleshing out, of course.

Regards,
Grant.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-05 Thread Herby Vojčík

Grant Husbands wrote:

Herby Vojčík wrote:

Your proposal depends on being able to reassign variable pointers, but
they don't necessarily exist.

Well, references are all over the spec (or were in times of ES5).


What I was calling activation records, the spec calls environment
records and it does not, by my reading, imply that variables within
them are reference-like in nature (read clause 10.2). However, we
don't need to argue this point.


I feel like misunderstood. I _know_ there aren't ref variables in the 
spec. I never told that. I was just telling that 'i' inside the loop 
body may be compiled not as Ref(loop-body-record, 'i') whenever it is 
accessed/modified (and optimized when Ref is not really needed, but you 
can see at it as if it is always Ref to local i), but as 
Ref(the-record-to-use, 'i'), the-record-to-use being local variable in 
loop iteration which can change its value (it can be outer-record when 
loop runs, at it can be local-record when iteration ends and values are 
fixed in local 'i').



Or, here's one that copies the other way (and is probably cleaner):
'Note' all closures (dynamically) created in (lexically,
post-desugaring) the loop body. Each time you end an iteration, update
all the loop variable activation record pointers to point at a new
clone of that activation record.

If I understood correctly, this is what I proposed. Or maybe it only looks
like it?


It has the same behaviour, but without needing variable pointers; that
was the idea. I now propose it to the list as a variant of your idea
that I think some may prefer. I'm merely trying to make sure your idea
gets the attention it deserves.


Thanks. Yes it is the same behaviour, the active loop body always uses 
the same shared 'i', only async closures get the fixed copy. Nice.



To be completely acceptable, the mechanics would need fleshing out, of course.

Regards,
Grant.


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-05 Thread Brendan Eich

Grant Husbands wrote:

For what you're talking about, I think this might be an equivalent
proposal that's more spec-friendly:
'Note' all closures (dynamically) created in (lexically) the loop
initializer.


Only in the initializer? Why should closures formed there be dynamically 
scoped to the current iteration? That's something done nowhere in the 
language as extended by let/const, either in JS1.7+ in SpiderMonkey and 
Rhino, or in draft ES6 so far.


I admit it's yet another non-throwing possibility beyond 0th-iteration 
scope and 1st-iteration scope. It is not the same as evaluating the 
initializer in each iteration's scope, though. Instead there's a dynamic 
scope for closures in the initializer (only those using the 
for(let-declared bindings?) not used for expressions or other closures, 
or something like that.


Dynamic scope is a warning sign, almost always a mistake.


  Each time you start an iteration, update all the loop
variable activation record pointers within those to point at the
current iteration's activation record (which should, with care, have
the same shape).


Note that non-strict eval can affect the activation's shape.



Or, here's one that copies the other way (and is probably cleaner):
'Note' all closures (dynamically) created in (lexically,
post-desugaring) the loop body. Each time you end an iteration, update
all the loop variable activation record pointers to point at a new
clone of that activation record.


This is a more complex spec than one that models each iteration having 
its own lexical scope. The spec needs only declarative environments, not 
hidden references and pointer updates.



In each case, you require a list of not-necessarily-predictable size
to note the closures in. That's not a big problem; it's just something
you need to be aware of.


As an implementation technique, Chez Scheme's heap boxing and assignment 
conversion could be even better. But this is all beyond the spec.


Still trying to be sure you intended a unique and dynamic scope for the 
initializer (first part) of for(let;;).


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Jason Orendorff
On Fri, Feb 3, 2012 at 7:26 PM, Allen Wirfs-Brock al...@wirfs-brock.com wrote:
 On Feb 3, 2012, at 4:26 PM, Jason Orendorff wrote:
 On 2/3/12 6:13 PM, Allen Wirfs-Brock wrote:
 But I also have to validly (and hopefully reasonably) specify exactly what 
 happens for the unrealistic use cases. There is a problem with your 
 desugaring in that the evaluation of INIT isn't scoped correctly relative 
 to V.
 Hmmm. I don't see the problem yet. I think it's scoped the way I intended 
 it: INIT is evaluated in the enclosing environment; V isn't in scope.

 Under the scoping rules TC39 has agreed to, the initializer of a let/const is 
 always shadowed by the binding it is initializing[...]

That rule doesn't make sense in this context. There should be either
one V for the whole loop, or one V per iteration. Having both seems
perverse.

Again, one can focus on parallels between for(let V=E;;) and let
V=E; or on parallels between for(let V=E;;) and for (let V of E).
I find the latter more important because experience and evidence
suggest that that is what will affect users in practice.

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Jason Orendorff
On Fri, Feb 3, 2012 at 6:13 PM, Allen Wirfs-Brock al...@wirfs-brock.com wrote:
 On Feb 3, 2012, at 12:44 PM, Jason Orendorff wrote:
 It might also be useful to look at for(var ...;...;...) and for (...;...;...) 
 loops.  Nobody is proposing changing the scoping for those, but they might 
 provide a broader view of how people use the generality of for(;;) loops

Out of about 5,500 for loops, I found none with functions in the head.
Actually, though, Mozilla's codebase is no better than anyone else's
for that purpose. I only picked ours because we have 'let'. I wish we
had a bigger corpus to search.

This is excluding tests generated by Mozilla's JS fuzzers; the fuzzers
often says things like:
for(e in this.__defineSetter__(x,function(){})){}
...and much worse.

 I would actually feel a lot more comfortable with per iteration binding of 
 for(;;) if the for let declaration was limited to a single binding.

Of course, that would inconvenience people doing simple stuff that has
nothing to do with closures, as in:
for (let i = 0, n = a.length; i  n; i++)
for (let child = element.firstChild, stop = element.lastChild;
child !== stop; child = child.nextSibling)

My sense is that these uses are even more common than for-loops with
escaping closures.

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Jason Orendorff
On Sat, Feb 4, 2012 at 8:02 AM, Jason Orendorff
jason.orendo...@gmail.com wrote:
 On Fri, Feb 3, 2012 at 7:26 PM, Allen Wirfs-Brock al...@wirfs-brock.com 
 wrote:
 Under the scoping rules TC39 has agreed to, the initializer of a let/const 
 is always shadowed by the binding it is initializing[...]

 That rule doesn't make sense in this context. There should be either
 one V for the whole loop, or one V per iteration. Having both seems
 perverse.

I just realized—the loop variables have to be visible in the
init-expressions if we want to support this:

for (let a = getThings(), i = 0, n = a.length; i  n; i++)

This is maybe not the best way to write for (thing of getThings()),
but people will write it, and so it probably ought to work. I think
this is more important than escaping closures. This means that if such
loops will have per-iteration bindings, they should have an additional
set of bindings just for initialization-time—which seems ugly. Maybe
it's not worth it.

There is also this:

for (let i = 0; i  n; ) {
setTimeout(...closure using i...);
if (shouldAdvance())
i++;
}

This will not work no matter what semantics we choose. However,
per-iteration bindings risk encouraging people to hit this problem.

These two issues give me pause.

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Allen Wirfs-Brock

On Feb 4, 2012, at 6:02 AM, Jason Orendorff wrote:

 On Fri, Feb 3, 2012 at 7:26 PM, Allen Wirfs-Brock al...@wirfs-brock.com 
 wrote:
 On Feb 3, 2012, at 4:26 PM, Jason Orendorff wrote:
 On 2/3/12 6:13 PM, Allen Wirfs-Brock wrote:
 But I also have to validly (and hopefully reasonably) specify exactly what 
 happens for the unrealistic use cases. There is a problem with your 
 desugaring in that the evaluation of INIT isn't scoped correctly relative 
 to V.
 Hmmm. I don't see the problem yet. I think it's scoped the way I intended 
 it: INIT is evaluated in the enclosing environment; V isn't in scope.
 
 Under the scoping rules TC39 has agreed to, the initializer of a let/const 
 is always shadowed by the binding it is initializing[...]
 
 That rule doesn't make sense in this context. There should be either
 one V for the whole loop, or one V per iteration. Having both seems
 perverse.

I agree, but having both is exactly what for(let;;) requires in order to 
satisfy (actually approach satisfying) everybody.  If you want the simplicity 
of the either or alternative then a good approach seems to be that  
for-in/for-of is one per iteration and for(;;) is one for the whole loop. C# 
recently when though a similar design change and that is exactly where they 
ended up.

Allen

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Allen Wirfs-Brock

On Feb 4, 2012, at 8:01 AM, Jason Orendorff wrote:

 On Sat, Feb 4, 2012 at 8:02 AM, Jason Orendorff
 jason.orendo...@gmail.com wrote:
 On Fri, Feb 3, 2012 at 7:26 PM, Allen Wirfs-Brock al...@wirfs-brock.com 
 wrote:
 Under the scoping rules TC39 has agreed to, the initializer of a let/const 
 is always shadowed by the binding it is initializing[...]
 
 That rule doesn't make sense in this context. There should be either
 one V for the whole loop, or one V per iteration. Having both seems
 perverse.
 
 I just realized—the loop variables have to be visible in the
 init-expressions if we want to support this:
 
for (let a = getThings(), i = 0, n = a.length; i  n; i++)
 
 This is maybe not the best way to write for (thing of getThings()),
 but people will write it, and so it probably ought to work. I think
 this is more important than escaping closures. This means that if such
 loops will have per-iteration bindings, they should have an additional
 set of bindings just for initialization-time—which seems ugly. Maybe
 it's not worth it.

That's what the initial
   let V = INIT
I said you needed in your de-sugaring was intended to address

 
 There is also this:
 
for (let i = 0; i  n; ) {
setTimeout(...closure using i...);
if (shouldAdvance())
i++;
}
 
 This will not work no matter what semantics we choose. However,
 per-iteration bindings risk encouraging people to hit this problem.

The 

  } finally {
%tmp = V
}

in your desugaring takes care of making sure that the the V value of the 
previous iteration is available to the TEST expression



 
 These two issues give me pause.
 
 -j
 

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Brendan Eich

I want off this merry-go-round! Let's recap:

From the January 19th 2012 notes Waldemar took:

Discussion about scope of for-bindings.
for (var x = ...;;) {...}  will, of course, retain ES1 semantics.
for (let x = ...;;) {...}
Allen: This will behave as in C++: x is bound once in a new scope 
immediately surrounding just the for statement.
DaveH: Strangely enough, this creates a new x binding in Dart at each 
iteration.
There's an alternative semantics that creates an iteration-local second 
x inside the loop and copies it back and forth.  Debate about whether to 
go to such complexity.  Many of us are on the fence.
Waldemar: What happens in the forwarding semantics if you capture the x 
inside a lambda in any of the three expressions in the head?

If this happens in the initializer:
DaveH's option: The lambda would capture an outer x.
Alternative: The lambda captures a hidden second x.
Waldemar's option: The lambda would capture the x from the first 
iteration.  The let variable x is bound once through each iteration, 
just before the test, if

  for (let x = expr1; expr2;) {...}
were:
  while (true) {
let x = first_iteration ? expr1 : value_of_x_from_previous_iteration;
if (!expr2)
  break;
...
  }
MarkM:  Just discovered that his desugaring has the same semantics as 
Waldemar's option.


--- end waldemar notes ---

Mark's desugaring 
(https://mail.mozilla.org/pipermail/es-discuss/2008-October/007819.html):


You're right. However, the desugaring is more complex than I expected.
Thanks for asking me to write it down.

  for (keyword  varName  =initExpr;testExpr;updateExpr) {body  }

desugars to (hygienic  renaming aside):

breakTarget: {
  const loop = lambda(iter =initExpr) {
keyword  varName  = iter;
if (!testExpr) { break breakTarget; }
continueTarget: {body  }
lambda(iter2 =varName) {
  keyword  varName  = iter2;
  updateExpr;
  loop(varName);
}();
  };
  loop();
}

I believe this meets all your requirements. However, in contradiction
to my original claim, one couldn't usefully say const instead of
let with a for(;;) loop.

--- end mark desugaring ---

But then Jon Zeppieri wrote in reply 
(https://mail.mozilla.org/pipermail/es-discuss/2008-October/007826.html):


/  I believe this meets all your requirements.

/
I believe it does.  Very cool.  It won't handle the fully general
for(;;).  E.g.,

for (let fn = lambda(n) { ... fn(...) ... };testExpr;updateExpr) ...

Also,

for (let i = 0, j = i + 1; ...) ...

But the modifications needed to make these work are pretty straightforward.

--- end jon citation ---

Then Grant Husbands wrote 
(https://mail.mozilla.org/pipermail/es-discuss/2012-January/019804.html):

How about something like this?
(given for (letvarName  =initExpr;testExpr;updateExpr) {body  } )

{
  letvarName  =initExpr;
  while(true) {
if (!testExpr) { break breakTarget; }
lettempVar  =varName;
{
  // There might be a better way to copy values to/from shadowed variables
  // (using temporaries seems a bit weak)
  letvarName  =tempVar;
  continueTarget: {body  }
  tempVar  =varName;
}
varName  =tempVar;
updateExpr;
  }
}

...

Maybe disallowing capture in the for (let ...;...;...) head would be easier.

--- end grant citation ---


Ok, Brendan here. I agree we want to support multiple declarators for the let 
or const in the for-head's initialization part.

I agree we want to capture the first-iteration bindings in any closures in 
those declarators' initializers.

This requires unrolling the loop once. Let's see how the desugaring from:

  for (let d1 = e1, ... dN = eN; cond; update) {
body;
  }

looks. It doesn't seem terrible:

  $loopEnd: {
let d1 = e1, ... dN = eN;
if (cond) {
  body;
  update;
  const $loop = { |d1, ... dN|
if (!cond) break $loopEnd;
body;
update;
$loop(d1, ... dN);
  }
  $loop(d1, ... dN);
}
  }


Notes:

* ... is meta-syntax, not rest/spread syntax.
* I've left out break and continue in the body.
* I'm using a block lambda for fun.

Mutations to the first-iteration d1, ... dN bindings in any closures in e1...N 
propagate to the second iteration.

Is this enough to restore the consensus we thought we had at the end of the 
meeting day on January 19th?

/be



Jason Orendorff mailto:jason.orendo...@gmail.com
February 4, 2012 8:01 AM
On Sat, Feb 4, 2012 at 8:02 AM, Jason Orendorff

I just realized—the loop variables have to be visible in the
init-expressions if we want to support this:

for (let a = getThings(), i = 0, n = a.length; i  n; i++)

This is maybe not the best way to write for (thing of getThings()),
but people will write it, and so it probably ought to work. I think
this is more important than escaping closures. This means that if such
loops will have per-iteration bindings, they should have an additional
set of bindings just for initialization-time—which seems ugly. Maybe
it's not worth it.

There 

Re: lexical for-in/for-of loose end

2012-02-04 Thread Allen Wirfs-Brock

On Feb 4, 2012, at 9:49 AM, Brendan Eich wrote:

 I want off this merry-go-round! Let's recap:
 
thanks for the recap...


 ...
 From the January 19th 2012 notes Waldemar took:
 
 Discussion about scope of for-bindings.
 for (var x = ...;;) {...}  will, of course, retain ES1 semantics.
 for (let x = ...;;) {...}
 Allen: This will behave as in C++: x is bound once in a new scope immediately 
 surrounding just the for statement.
 DaveH: Strangely enough, this creates a new x binding in Dart at each 
 iteration.
 There's an alternative semantics that creates an iteration-local second x 
 inside the loop and copies it back and forth.  Debate about whether to go to 
 such complexity.  Many of us are on the fence.
 Waldemar: What happens in the forwarding semantics if you capture the x 
 inside a lambda in any of the three expressions in the head?
 If this happens in the initializer:
 DaveH's option: The lambda would capture an outer x.
 Alternative: The lambda captures a hidden second x.
 Waldemar's option: The lambda would capture the x from the first iteration.  
 The let variable x is bound once through each iteration, just before the 
 test, if
 ...

 --- end grant citation ---
 
 
 Ok, Brendan here. I agree we want to support multiple declarators for the let 
 or const in the for-head's initialization part.

check
 
 I agree we want to capture the first-iteration bindings in any closures in 
 those declarators' initializers.

It isn't clear to me why capture first-iteration is abstractly any better than 
capture a hidden second x.  In both cases, in most iterations of the loop, 
evaluation of any such captures is going to reference the wrong binding.  
From a user perspective, the main advantage I see for capture first iteration 
is that it has a slightly smaller window of wrongness.  The captures evaluated 
in the first iteration will reference the correction binding, while latter 
iterations reference the wrong binding.  From an implementation perspective, it 
is probably a bit simpler to not have the extra hidden binding for capture.

There is another alternative that I haven't seen mentioned.  Scope any closures 
in the initialization expression such that any references to loop declared 
binding resolve to undeclared bindings,

For example (using block lambdas for conciseness)
   for( let d1={|| d1}, d2={|| d1+d2};;)...

would actually be interpreted as if it was
   for (let d1={|| return {|| d1}; let d1,d2}(), let d2={|| return {|| d1+d2}; 
let d1,d2}();;)...

In words, any references to loop declarations capture uninitialized bindings 
that never get initialized.  If evaluated they will throw as accesses within 
the TDZ of the binding.

Always making such bindings (when used) a error seems preferable I(from a user 
perspective) than making the first iteration of a loop behave differently from 
subsequent iterations.

 ...

 Mutations to the first-iteration d1, ... dN bindings in any closures in 
 e1...N propagate to the second iteration.
 
 Is this enough to restore the consensus we thought we had at the end of the 
 meeting day on January 19th?

I really don't like the first iteration is different semantics and think we 
should think about the above alternative.

However, such closure capture is very rare (could use of block lambda based 
patterns change that??) so it may come down to judgements about implementation 
costs.  Is capture first going to be significantly easier to implement than my 
alternative scoping? The answer is obvious to me.  In either case an 
implementation is like to special case loops with closure capture in their 
initializers.

Allen


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Brendan Eich

Brendan Eich wrote:


  $loopEnd: {
let d1 = e1, ... dN = eN;
if (cond) {
  body;
  update;
  const $loop = { |d1, ... dN|
if (!cond) break $loopEnd;
body;
update;
$loop(d1, ... dN);
  }
  $loop(d1, ... dN);
}
  }


Notes:

* ... is meta-syntax, not rest/spread syntax.
* I've left out break and continue in the body.
* I'm using a block lambda for fun.

Mutations to the first-iteration d1, ... dN bindings in any closures 
in e1...N propagate to the second iteration.


Another note:

* this is a desugaring, so any of d1...N can be destructuring patterns, 
and with the structuring shorthand for object literals the bindings 
propagate nicely.


Symmetry FTW.

/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Brendan Eich

Allen Wirfs-Brock wrote:

On Feb 4, 2012, at 9:49 AM, Brendan Eich wrote:
I agree we want to capture the first-iteration bindings in any 
closures in those declarators' initializers.


It isn't clear to me why capture first-iteration is abstractly any 
better than capture a hidden second x.  In both cases, in most 
iterations of the loop, evaluation of any such captures is going to 
reference the wrong binding.


The argument is as follows:

  for (let i = 0, a = some.array, n = a.length; i  n; i++) { ... }

here we definitely want the a in a.length (n's initializer) to be scoped 
by the head let -- to be the a declared by the second declarator.


Now consider a bit of eta conversion:

  for (let i = 0, a = some.array, n = (function(){return a})().length; 
i  n; i++) { ... }


It would be quite wrong for the a captured by the anonymous function 
expression to be other than the a binding declared and initialized 
immediately to the left. It would be bad for the eta conversion to break 
equivalence (use a block-lambda instead of a function expression for 
full TCP).



 From a user perspective, the main advantage I see for capture first 
iteration is that it has a slightly smaller window of wrongness.  The 
captures evaluated in the first iteration will reference the 
correction binding, while latter iterations reference the wrong binding.
Users expect and even (now that they know, and Dart raises the ante) 
demand that each iteration gets fresh let bindings. Any who do capture 
an initial binding in a closure must know, or will learn, that it's just 
the first one, which fits the model.


If this were really a footgun (I don't believe it is without actual 
evidence from the field) we could try to ban closures capturing the 
initial bindings. That ad-hoc restriction would be quite a wart. It 
doesn't seem warranted.


 From an implementation perspective, it is probably a bit simpler to 
not have the extra hidden binding for capture.


I don't think so. The unrolling I showed was to use a tail-recursive 
block-lambda helper. But real implementations will do closure analysis 
and optimization (flat AKA display closures, e.g.) and use branch 
instructions for loops, jumps for breaks, etc. Having the first binding 
rib open a bit earlier than subsequent ribs is (I think) a small or 
zero-cost issue.



I really don't like the first iteration is different semantics


Different how? Making the first iteration's binding initialization 
capture guaranteed errors would be different semantics. Capturing the 
first iteration's bindings from closures in their initializers is not 
different any more than having initializers is different. The 
initialization part of the for loop is already special. It's not like 
the update part.



and think we should think about the above alternative.


Eta equivalence matters. Given that we want n = a.length to use the a 
declared to the left in the same for-head declaration, I don't see how 
we can make closures in a right-ward initializer capture some outer binding.


Capturing an error-only binding would need evidence of the footgun not 
being useful for shooting other things. We don't have such evidence, not 
by a long shot.


However, such closure capture is very rare (could use of block lambda 
based patterns change that??)


I don't think so -- equivalences are stronger, not weaker or different, 
with block-lambdas vs. functions, due to TCP.


so it may come down to judgements about implementation costs.  Is 
capture first going to be significantly easier to implement than my 
alternative scoping? The answer is obvious to me.


Did you mean isn't?

 In either case an implementation is like to special case loops with 
closure capture in their initializers.


Varying a sketch Jason posted to the SpiderMonkey internals list:

for (let V = INIT; TEST; UPD)  STMT

compiles to

enterblock V
INIT
goto L2
L1: STMT
reenterblock
UPD
L2: TEST
iftrue L1
leaveblock

This is very close to how SpiderMonkey compiles for(let;;) already -- 
the only new instruction is reenterblock, which exits the current block 
and does the equivalent of enterblock V again.


V is an immediate operand, in SpiderMonkey a block object literal 
created by the compiler. It holds all the bindings and their stack 
offsets, however many declarators with or without destructuring occur 
after let.


I claim implementation is not the driver here. User expectations, esp. 
savvy users who might make some practical or theoretical (testing) use 
of eta conversion, matter more.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Herby Vojčík
It looks to me that all those schemes and desugaring are very 
complicated. What is bad with scenario of reusing the same variable and 
isolating it as local only for the async closures?


The code:

  for (let a=..., b=..., otherInit; cond; update) {
// use a, b and things from otherInit
  }

Now, if you allow first class refs _in_implementation_, it could be 
something like this:


  let a=..., b=..., otherInit;
  while (cond) {
{
  let _inner_a, _inner_b; // undefined as yet;
  let ref a :- ref outer a, ref b :- ref outer b, ...; // (1)
  try {
// use a, b and things from otherInit

// names a, b and others from for-let are compiled using
// refs defined in (1) which point to outer
// variables while processing loop body
  } finally {
_inner_a = a; _inner_b = b; ... // assign actual values
ref a :- ref _inner_a; ref b :- ref _inner_b; ...
// fixing refs at the end of the body to local ones
// async closures will use these (since they use the refs
// which were rerouted)
// the same closures when executed inside loop body would
// of course use the outer shared variables, since fixing
// only happens after the body
// It is always the same ref, but points to different things
// in loop and out of the loop
  }
}
update;
  }

This way, inside the loop, always the same (outer) variables are used, 
any inc() things will act upon them, no copying in-out.
At the end of every iteration, the refs that pointed to these shared 
variables are fixed to inner ones, using actual values.


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Allen Wirfs-Brock

On Feb 4, 2012, at 12:55 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 On Feb 4, 2012, at 9:49 AM, Brendan Eich wrote:
 I agree we want to capture the first-iteration bindings in any closures in 
 those declarators' initializers.
 
 It isn't clear to me why capture first-iteration is abstractly any better 
 than capture a hidden second x.  In both cases, in most iterations of the 
 loop, evaluation of any such captures is going to reference the wrong 
 binding.
 
 The argument is as follows:
 
  for (let i = 0, a = some.array, n = a.length; i  n; i++) { ... }
 
 here we definitely want the a in a.length (n's initializer) to be scoped by 
 the head let -- to be the a declared by the second declarator.
 
 Now consider a bit of eta conversion:
 
  for (let i = 0, a = some.array, n = (function(){return a})().length; i  n; 
 i++) { ... }
 
 It would be quite wrong for the a captured by the anonymous function 
 expression to be other than the a binding declared and initialized 
 immediately to the left.
Yes, I support the general principal that that initializers of bindings capture 
to the left.  But the problem here is that conceptually the let is defining 
multiple bindings (one per iteration).  I don't think many people are actually 
going to understanding the details of the proposed semantics and their 
implications.  Since most uses won't involve closure capture, any of the 
proposed semantics that have per iteration bindings with forward value 
propagation are just going to do the right thing.  That is good.  However, I 
doubt that someone who actually codes a function in a for(let;;) initializer is 
going to be thinking, of course, this only captures the first iteration 
bindings.  

 It would be bad for the eta conversion to break equivalence (use a 
 block-lambda instead of a function expression for full TCP).

With the TDZ alternative I proposed, there  would still be equivalence for:
 for(let x=x;;)...;
and 
 for(let x={|| x}();;)...;

Both throw for accessing an initialized variable.

But you're right that equivalence is lost for 
 for(let x=n, y=x;;)...;
and 
 for(let x=n, y={|| x}();;)...;

Whether this is better or worse than the wrong capture issue complete depends 
upon the actual programmer intent.

 
 
 From a user perspective, the main advantage I see for capture first 
 iteration is that it has a slightly smaller window of wrongness.  The 
 captures evaluated in the first iteration will reference the correction 
 binding, while latter iterations reference the wrong binding.
 Users expect and even (now that they know, and Dart raises the ante) demand 
 that each iteration gets fresh let bindings. Any who do capture an initial 
 binding in a closure must know, or will learn, that it's just the first one, 
 which fits the model.

For the latter, I strongly suspect that they won't know and will be WTF 
surprised when they encounter it.  The saving grace is that this will probably 
be very rare, although its possible that the introduction of block lambdas 
might somewhat change that.  Just don't know...

 If this were really a footgun (I don't believe it is without actual evidence 
 from the field) we could try to ban closures capturing the initial bindings. 
 That ad-hoc restriction would be quite a wart. It doesn't seem warranted.
 
My TDZ solution is such a restriction.  But I don't see how it is any more ad 
hoc than any of the other changes we are talking about here in order to give 
for(;;) per iteration bindings.  Its wartiness  actually seems small and is 
restricted  to a situation where the programmer probably is actually expecting 
C=style per loop rather than per iteration binding. 

 From an implementation perspective, it is probably a bit simpler to not have 
 the extra hidden binding for capture.
 
 I don't think so. The unrolling I showed was to use a tail-recursive 
 block-lambda helper. But real implementations will do closure analysis and 
 optimization (flat AKA display closures, e.g.) and use branch instructions 
 for loops, jumps for breaks, etc. Having the first binding rib open a bit 
 earlier than subsequent ribs is (I think) a small or zero-cost issue.

My comment wasn't about the bind to first unrolling.  It was about the extra 
hidden binding alternative in the first list of alternatives and is probably 
also applicable to by TDZ alternative.

 
 I really don't like the first iteration is different semantics
 
 Different how?

Different from subsequent iterations...

Take Jason's example

   for (let i = 0; i  n; ) {
   setTimeout(...closure using i...);
   if (shouldAdvance())
   i++;
   }

If somebody decided to abstract the increment:

   for (let i = 0; advance={|| i++}; i  n; ) {
   setTimeout(...closure using i...);
   if (shouldAdvance())
 advance();
   }

advance does what is intended if called on the first iteration, but not on 
subsequent one iterations. I'd soon get an error when I ran this than having 

Re: lexical for-in/for-of loose end

2012-02-04 Thread Brendan Eich

Herby Vojčík wrote:
It looks to me that all those schemes and desugaring are very 
complicated.


And what you showed isn't complicated? LOL.

What you're sketching is an optimization, one I believe Chez Scheme and 
other implementations perform. Indeed any good implementation will 
optimize let-bound loop variables to registers and avoid any 
per-iteration overhead in the absence of closures.


But what you sketched is nothing like a specification.

A normative specification does not describe non-observables, except in 
informative asides (which should be used sparingly). A specification 
must simply and clearly state the rules for syntax and semantics.


In particular, the ES6 specification governing the for-let loops we're 
discussing needs specific treatment to avoid the notorious var 
pigeon-hole problem made observable by closures in the loop body that 
capture loop variables. The simplest way to do this is being worked out, 
but it will necessarily entail a scope per iteration.


I argue the first iteration's scope must enclose the initialisers for 
the for(let;;) head's first of three parts. This is an important detail. 
It's not complicated beyond other parts of the spec; it's just detailed.


We have to have consistent for(let;;) semantics, and as the discussion 
has evolved (since 2008) we've come to agree on the constraints by which 
we reckon consistent. The constraints require a specific solution, in 
my view: first iteration's scope covers iniitalizers, and 
observably-distinct let scopes for each iteration.


/be


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Brendan Eich

Allen Wirfs-Brock wrote:

On Feb 4, 2012, at 12:55 PM, Brendan Eich wrote:

  The argument is as follows:
  
for (let i = 0, a = some.array, n = a.length; i  n; i++) { ... }
  
  here we definitely want the a in a.length (n's initializer) to be scoped by the head let -- to be the a declared by the second declarator.
  
  Now consider a bit of eta conversion:
  
for (let i = 0, a = some.array, n = (function(){return a})().length; i  n; i++) { ... }
  
  It would be quite wrong for the a captured by the anonymous function expression to be other than the a binding declared and initialized immediately to the left.

Yes, I support the general principal that that initializers of bindings capture to the 
left.  But the problem here is that conceptually the let is defining multiple bindings 
(one per iteration).  I don't think many people are actually going to understanding the 
details of the proposed semantics and their implications.  Since most uses won't involve 
closure capture, any of the proposed semantics that have per iteration bindings with 
forward value propagation are just going to do the right thing.  That is good.


That's all agreed, yes.


   However, I doubt that someone who actually codes a function in a for(let;;) 
initializer is going to be thinking, of course, this only captures the first 
iteration bindings.


Why? The initializer runs first. There are two working possibilities: 
bindings of the same name in an implicit block around the loop (the 0th 
iteration scope), immediately shadowed on first (if the condition is 
true) by fresh bindings of the same names with values forwarded; or what 
I propose, the first iteration's bindings.


Why is the 0th iteration scope better than the 1st iteration scope? It 
may be that some people think of the INIT part in for (INIT; TEST; UPD) 
STMT as being before the loop starts, but others may see it as part of 
the first iteration.


If we really do have evidence for the 0th iteration scope (hard to 
imagine), then that is doable but it costs a bit more in the 
closure-in-initializer case. All else equal I go with lower cost in that 
rare case.




With the TDZ alternative I proposed, there  would still be equivalence for:
  for(let x=x;;)...;
and
  for(let x={|| x}();;)...;

Both throw for accessing an initialized variable.


Yippee. :-|

Making a dynamic error trap for no good reason is a mistake. Where's the 
evidence that closures in initializers that capture loop control 
variables are wrong? There isn't any, AFAIK.



But you're right that equivalence is lost for
  for(let x=n, y=x;;)...;
and
  for(let x=n, y={|| x}();;)...;

Whether this is better or worse than the wrong capture issue complete depends 
upon the actual programmer intent.


It's worse for the language's consistency of binding rules and 
equivalences. It's a botch. I think we should not equivocate here.




  Users expect and even (now that they know, and Dart raises the ante) demand 
that each iteration gets fresh let bindings. Any who do capture an initial binding 
in a closure must know, or will learn, that it's just the first one, which fits 
the model.

For the latter, I strongly suspect that they won't know and will be WTF 
surprised when they encounter it.


Why? What binding did the think they captured, given the assumption we 
share (since you didn't reject it, we do share this, right?) that they 
know about fresh binding(s) per iteration? Are you implying users will 
expect the 0th-iteration bindings? Could be, but I rather suspect they'd 
want 1st. There isn't another choice. They don't want throwing upvars 
(throwing-up vars :-P).




  Different how?

Different from subsequent iterations...

Take Jason's example

for (let i = 0; i  n; ) {
setTimeout(...closure using i...);
if (shouldAdvance())
i++;
}

If somebody decided to abstract the increment:

for (let i = 0, advance={|| i++}; i  n; ) {
setTimeout(...closure using i...);
if (shouldAdvance())
  advance();
}

advance does what is intended if called on the first iteration, but not on 
subsequent one iterations. I'd soon get an error when I ran this than having it 
get stuck in a loop.


(I s/;/,/ on the for line to fix the typo.)

Anyone who writes that must be thinking the advance block-lambda is in 
the scope of the body, if they understand fresh-binding-per-iteration. 
But the advance block-lambda is not in the body. It's not before the 
loop either (wrong [outer] or no i in scope there). This is simply 
erroneous code by definition -- the assumption of 
fresh-binding-per-iteration having been learned already is violated by 
the structure of the code.


Rather than a dynamic error (might not test this), anyone writing this 
might rather get a static error. A warning would be justifed by a high 
quality implementation, based on analysis of the advance() call possibly 
coming from later iterations.


So here we return to 

Re: lexical for-in/for-of loose end

2012-02-04 Thread Grant Husbands
Brendan Eich wrote:
 I agree we want to capture the first-iteration bindings in any closures in
 those declarators' initializers.

 This requires unrolling the loop once. Let's see how the desugaring from:
[snip]

For what it's worth, I agree with Mark's (2008) and Brendan's
desugarings and hereby withdraw my own, as the value-copying covers
barely any more use cases. Without some extensive feature like
let-aliasing, as Herby suggested, those solutions as good as we're
going to get, though I could have missed some of the discussion so
far.

By the way, if apparent code duplication in Brendan's desugaring is an
issue, a small change will fix that, with little cost, like so:
$loopEnd: {
  let $initdone = false;
  const $loop = { |d1, ... dN|
if (!$initdone) {
  $initdone = true;
  d1 = e1, ... dN = eN;
}
if (!cond) break $loopEnd;
body;
update;
$loop(d1, ... dN);
  }
  $loop()
}

As for modifications done by the for-head's closures, I have no strong
opinions. In both proposed desugarings, those modifications would be
invisible to the loop body fairly quickly. And in all proposals that
don't involve some kind of extensive variable aliasing, the for-head's
closures cannot observe values set in the loop body (beyond the first
iteration).

However, I hope I don't delay consensus by raising the possibility of
another desugaring using consts:

$loopEnd: {
  const d1 = e1, ... dN = eN;
  const $loop = { |d1, ... dN|
if (!cond) break $loopEnd;
body;
update;
$loop(d1, ... dN);
  }
  $loop(d1, ... dN);
}

That way, the for-head's closures can't easily have hidden unexpected
effects, though they'll still observe incorrect iterated values, and
it's less of a sledgehammer than disallowing capture altogether.

It's late, so I apologise if I've missed something really obvious or
typed garbage.

Regards,
Grant.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-04 Thread Grant Husbands
Herby Vojčík wrote:
 It looks to me that all those schemes and desugaring are very complicated

The full problem is complicated, so that's to be expected.

 What is bad with scenario of reusing the same variable and isolating it as
 local only for the async closures?

Your proposal depends on being able to reassign variable pointers, but
they don't necessarily exist. Though I haven't written a JS engine, I
believe they are allowed to have variables directly in an activation
record (or environment instance) without any (pointer) indirection, so
they'd have no mechanism for performing the operation you describe.

However, though I haven't read it, I believe that the spec talks a
great deal about these activation records and environments, so
specifying a mechanism involving those might give you more chance of
finding common ground.

For what you're talking about, I think this might be an equivalent
proposal that's more spec-friendly:
'Note' all closures (dynamically) created in (lexically) the loop
initializer. Each time you start an iteration, update all the loop
variable activation record pointers within those to point at the
current iteration's activation record (which should, with care, have
the same shape).

Or, here's one that copies the other way (and is probably cleaner):
'Note' all closures (dynamically) created in (lexically,
post-desugaring) the loop body. Each time you end an iteration, update
all the loop variable activation record pointers to point at a new
clone of that activation record.

In each case, you require a list of not-necessarily-predictable size
to note the closures in. That's not a big problem; it's just something
you need to be aware of.

Regards,
Grant.
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Andreas Rossberg
On 3 February 2012 03:08, Allen Wirfs-Brock al...@wirfs-brock.com wrote:
 Plus, the desugarings aren't things that are really suitable for teaching
 the semantics to everyday JS programmers.  You instead have to say something
 like:

 Ok, this is really complicated but here goes.  For each let/const declared
 in the for header, a fresh variable is located in the loop body for each
 iteration of the loop. However, the values of the loop variables are
 automatically copied from the previous iteration into the next iterations.
  This means that basic expression operator will work in the loop header
 pretty much like you would expect.  But be careful if you use any function
 expressions in the for header because the loop variables they reference may
 not be from the current iteration and any changes to loop variable they make
 may not have the effect you intended.  But, hey you probably shouldn't do
 those things so it really doesn't matter what it really does.

I don't understand your bit saying that values are copied from the
previous iteration into the next iterations. In any case, it seems to
me that saying the following is perfectly enough:

Declarations in the loop header work like normal declarations.
However, inside the loop body, for each iteration, you get a fresh
copy of all variables bound in the header, with their current values.
(This does not affect uses of the variables in the loop header
itself.)

The last sentence is just for clarity, as it is already implied by the
other two. Moreover, it shouldn't matter for any sane kind of program.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Herby Vojčík

Allen Wirfs-Brock wrote:


On Feb 2, 2012, at 1:38 PM, Brendan Eich wrote:

keys etc. are currently spec'ed to to do own-only, you want allKeys to
do the crazy thing:

http://wiki.ecmascript.org/doku.php?id=harmony:iterators#standard_api

Let's not go around and around on this. Enumerable proto-properties
are harmful, no one wants them.


I'm afraid I have to disagree with the absolutism of this statement.
There are some perfectly reasonable use cases for enumerating proto
properties and they are quite in alignment with prototypal inheritance.
For example, how about defining a set of default property values and
then using prototypal inheritance to selective over-ride them:

let privateImplicitOptions = {a:1,b:2,c:3 /* and on and on */};

function makeOptions(explicitOptions) {
let opt = Object.create(privateImplicitOptions);
for (let [k.,v] of items(explicitOptions)) opt[k]=v;
return opt;
}

myOpts = makeOptions({b:-2});

Why should somebody I pass an options object to need to worry about
whether I used zero, one, or n levels of prototypes to represent by options.


+1


that said, I think we agree that the real problem is the legacy ES
semantics of for-in and its use (or lack of) for the enumerable
attribute. If we can start out with a clean slate conceptual slate for
for-of maybe we don't have to worry about such things. However, carrying
over a lot of historic baggage isn't a clean slate. Thinking in terms of
abstract collections, having keys() not mean all keys in the
collection seems quite absurd. That should be the normal case, the more
verbose name should be used for the exceptional case that excludes some
of the keys. Now in this case, the real problem is in deciding the
default way to view a plain Es objects as an abstract collection. Maybe
only considering own properties as the collection elements is the right
thing. In that case it may be allKeys that is carrying the wrong
implication and instead it should be something like extendedKeys.


deepKeys, maybe?


Allen


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Herby Vojčík

Allen Wirfs-Brock wrote:

for-in works well with a per iteration binding because there is no
header code that runs between iterations. The various desugarings that
have been proposed to provide a fresh copy for each iteration of this
form of for work for propagating simple values between the header and
each iteration but not for closure captures.

Plus, the desugarings aren't things that are really suitable for
teaching the semantics to everyday JS programmers. You instead have to
say something like:

Ok, this is really complicated but here goes. For each let/const
declared in the for header, a fresh variable is located in the loop
body for each iteration of the loop. However, the values of the loop
variables are automatically copied from the previous iteration into
the next iterations. This means that basic expression operator will
work in the loop header pretty much like you would expect. But be
careful if you use any function expressions in the for header
because the loop variables they reference may not be from the
current iteration and any changes to loop variable they make may not
have the effect you intended. But, hey you probably shouldn't do
those things so it really doesn't matter what it really does.


Ok, alternative approach:

for (let i=..., whatever; whatever2; whatever3) {
  // use i
}

be implemented as

let i=..., whatever;
while (whatever2) {
  {
let i=forward_proxy_to(outer i);
// a thing that touches real i, but is different object
// use i
i=current value of inner i; // fix inner i to the value

// The point is, it works for closures, both from inside or
// 'detached' since it is always the same 'i' binding
// in the same execution context
  }
  whatever3;
}


Allen


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Jason Orendorff
On Thu, Feb 2, 2012 at 8:08 PM, Allen Wirfs-Brock al...@wirfs-brock.com wrote:
 On Feb 2, 2012, at 5:07 PM, Jason Orendorff wrote:
  On Thu, Feb 2, 2012 at 5:52 PM, Allen Wirfs-Brock al...@wirfs-brock.com
  wrote:
   for (let i=(geti = function() {return i},expr), expr, incr = 
   function(i++),
   decr=function(i--), cmp=function(){return in}; cmp();incr()) {
 
  What?! We should not reward people writing code like that.

 No reward, it is simply what the current syntax permits, and it is important
 that semantics are consistently applied.

Oh, I definitely think we should have consistent semantics. It's
difficult, because for(;;) is complicated, but it's worth the effort.
Consider:

for (let V in EXPR) STMT
for (let V of EXPR) STMT
for (let V = EXPR; ...; ...) STMT

I propose that in all three cases, the code that runs once, before the
first iteration, namely EXPR, should be evaluated outside the scope of
the loop variable. STMT should be evaluated within the scope of a
per-iteration binding for V. The same semantics in all three cases.
That seems to me both consistent and sensible.

What is even more important than consistency is that the language work
for people. We know that *not* having per-iteration bindings
astonishes users, because that's how SpiderMonkey does it, and people
are regularly astonished.

 Plus, the desugarings aren't things that are really suitable for teaching
 the semantics to everyday JS programmers.  You instead have to say something
 like:

 Ok, this is really complicated but here goes.  For each let/const declared
 in the for header, a fresh variable is located in the loop body for each
 iteration of the loop. However, the values of the loop variables are
 automatically copied from the previous iteration into the next iterations.
  This means that basic expression operator will work in the loop header
 pretty much like you would expect.  But be careful if you use any function
 expressions in the for header because the loop variables they reference may
 not be from the current iteration and any changes to loop variable they make
 may not have the effect you intended.  But, hey you probably shouldn't do
 those things so it really doesn't matter what it really does.

This explanation seems oriented more towards making a rhetorical
point than explaining. If I had to explain this to an everyday Web
programmer, I would just write the three lines of code above and point
out the consistent behavior. That way is simple, and it explains
several things at once.

Anyway, this kind of argument certainly isn't going to win me over,
because the reason I want this change in the first place is so I can
do less explaining! People do write code like this:

for (let i = 0; i  n; i++) {
buttons[i] = makeButton();
buttons[i].click(function () { alert(pushed button  + i); });
}

It fails to work because in SpiderMonkey the binding isn't
per-iteration. Then I have to explain. Per-iteration bindings mean
less explaining, because fewer people will hit problems. Their code
will just work.

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Jason Orendorff
My attempt at a for-let desugaring is:
  https://gist.github.com/1730064

I think this will be very easy to implement, though I haven't tried it yet:
  
http://groups.google.com/group/mozilla.dev.tech.js-engine.internals/browse_thread/thread/4906c7389e18f0f8

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Allen Wirfs-Brock

On Feb 3, 2012, at 8:46 AM, Jason Orendorff wrote:

 My attempt at a for-let desugaring is:
  https://gist.github.com/1730064

yes, similar to  
https://mail.mozilla.org/pipermail/es-discuss/2008-October/007819.html 

the real issue with per iteration binding and for(;;) is closure capture in the 
(;;).  This is admirably rare, but your definition (and Mark's)  means that 
when it occurs it is unlikely to do what the programmer intended. 

Allen___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Jason Orendorff
On Fri, Feb 3, 2012 at 11:23 AM, Allen Wirfs-Brock
al...@wirfs-brock.com wrote:
 On Feb 3, 2012, at 6:42 AM, Jason Orendorff wrote:
    for (let V in EXPR) STMT
    for (let V of EXPR) STMT
    for (let V = EXPR; ...; ...) STMT

 the third case is different in several ways. Including:
   let V =EXPR is a distinct syntactic pattern that has a specific semantics  
 that requires that the EXPR is evaluated in the same scope as EXPR. However, 
 in most cases (but consider |et x=(x=5,++x);) all of EXPR will be in V's 
 temporal dead zone.
   The actual syntax is for (let V1=EXPR1, V2=EXPR2,...;...) STMT Again, the 
 semantics for such declarations is that all of the Vs are defined in the same 
 scope and EXPRn+1 is outside the TDZ for for Vn.

 Using your semantics for the 3rd form would mean that its let clause was not 
 consistent with let declarations in all other contexts.

The behavior of let-declarations is that the name hoists to the
enclosing block. We don't want to be *that* consistent.

So the situation ends up being: we will have a statement that looks
like for(let V = ...). It can either be partly consistent with other
statements that look like for (let ...) or partly consistent with
other statements that look like let V = 

Either choice breaks with consistency on one side; TC39 should pick
the way that astonishes the fewest people.

 What is even more important than consistency is that the language work
 for people. We know that *not* having per-iteration bindings
 astonishes users, because that's how SpiderMonkey does it, and people
 are regularly astonished.

 I think you are over generalizing from a specific use case.  This bug cuts 
 both ways.

Could you explain a little more why you think that's the case?

Grepping finds 1,227 loops of the for (let ...; ...; ...) variety in
Mozilla's codebase. Of these 95% are simple counting loops, and *none*
contain functions in the loop-head. I estimate about 4% of these 1,227
loops have functions in the body, most of which escape, but it is
harder to put precise numbers on that. Loops where the loop variables
are modified in the loop body seem rare (I didn't see any), but I
can't say how rare.

Of the non-simple-counting loops, here is a sample:

for (let child = element.firstChild; child; child = child.nextSibling) ...
for (let frame = aElement.ownerDocument.defaultView; frame !=
content; frame = frame.parent) ...
for (let row; (row = aResultSet.getNextRow());) ...
for (let i = 0; this[i] != null; i++) ...

In all these cases, per-iteration bindings would do the right thing.

If the choice comes down to astonishing this programmer:

   for (let i = 0; i  n; i++) {
   buttons[i] = makeButton();
   buttons[i].click(function () { alert(pushed button  + i); });
   }

or astonishing this one:

let geti;
for (let i=(geti = function() {return i},expr), expr, incr =
function(i++), decr=function(i--), cmp=function(){return in};
cmp();incr()) {
let j=i;
i +=10;
decr();
...
If (j+-9 !== i) ...
...
}

then from where I'm sitting it looks like an easy choice. Let's
support realistic use cases.

Of course if per-iteration bindings would be confusing in practice,
that would be bad. But the for-loop head issue should not be a major
consideration, because people truly almost never write code like that.

I'd be glad to have range(); however I don't think it's effective to
address usability issues by offering more alternatives. :-\

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-03 Thread Allen Wirfs-Brock

On Feb 3, 2012, at 12:44 PM, Jason Orendorff wrote:

 On Fri, Feb 3, 2012 at 11:23 AM, Allen Wirfs-Brock
 al...@wirfs-brock.com wrote:
 On Feb 3, 2012, at 6:42 AM, Jason Orendorff wrote:
for (let V in EXPR) STMT
for (let V of EXPR) STMT
for (let V = EXPR; ...; ...) STMT
 
 the third case is different in several ways. Including:
   let V =EXPR is a distinct syntactic pattern that has a specific semantics  
 that requires that the EXPR is evaluated in the same scope as EXPR. However, 
 in most cases (but consider |et x=(x=5,++x);) all of EXPR will be in V's 
 temporal dead zone.
   The actual syntax is for (let V1=EXPR1, V2=EXPR2,...;...) STMT Again, the 
 semantics for such declarations is that all of the Vs are defined in the 
 same scope and EXPRn+1 is outside the TDZ for for Vn.
 
 Using your semantics for the 3rd form would mean that its let clause was not 
 consistent with let declarations in all other contexts.
 
 The behavior of let-declarations is that the name hoists to the
 enclosing block. We don't want to be *that* consistent.
 
 So the situation ends up being: we will have a statement that looks
 like for(let V = ...). It can either be partly consistent with other
 statements that look like for (let ...) or partly consistent with
 other statements that look like let V = 

I think that the best way to think about this is that all for statements with 
let/const declaration implicit introduce a block to contain those declarations. 
 The base question here is the extent of that block. Does it surround the 
entire for statement,  or just the statement part of the for statement.  Or 
does it need two blocks, one surrounding the for and one surrounding the 
statement. 

The |let| is inside the |for| so I think |for| bounded hoisting is fairly 
consistent with general let declarations.

 
 Either choice breaks with consistency on one side; TC39 should pick
 the way that astonishes the fewest people.
 
 What is even more important than consistency is that the language work
 for people. We know that *not* having per-iteration bindings
 astonishes users, because that's how SpiderMonkey does it, and people
 are regularly astonished.
 
 I think you are over generalizing from a specific use case.  This bug cuts 
 both ways.


Just to reinforce something I think I already said.  I'm not questioning the 
desirability of  per iteration bindings for for-in/for-of.  I'm saying that in 
some cases (not most) that people who use the fully generality of the for(;;) 
statement will be astonished.  I think this is even recognized by you in your 
desugaring by the fact that you use %tmp to propagate the value of V from one 
iteration to the next.  It would be astonishing that a V++ in the loop body did 
not change the value of V seen by the next iteration.

That desugarings work fine in that regard as long as there is no closure 
capture involved. But consider:
 
   for (let keepGoing=true, stopper=function() {keepGoing=false}; keepGoing;) {
  let thisOne = getNext();
  if (thisOne == null) keepGoing = false;
  else thisOne.scanForSomeSpecificConditionAndIfDetectedDo(stopper);
   }

There are lots of other ways to structure such a loop. But given the observable 
value propagation from iteration to iteration, it isn't unreasonable for a 
programmer to expect this to work.  They will be surprised that it doesn't.

 
 Could you explain a little more why you think that's the case?
 
 Grepping finds 1,227 loops of the for (let ...; ...; ...) variety in
 Mozilla's codebase. Of these 95% are simple counting loops, and *none*
 contain functions in the loop-head. I estimate about 4% of these 1,227
 loops have functions in the body, most of which escape, but it is
 harder to put precise numbers on that. Loops where the loop variables
 are modified in the loop body seem rare (I didn't see any), but I
 can't say how rare.

It might also be useful to look at for(var ...;...;...) and for (...;...;...) 
loops.  Nobody is proposing changing the scoping for those, but they might 
provide a broader view of how people use the generality of for(;;) loops

 
 Of the non-simple-counting loops, here is a sample:
 
for (let child = element.firstChild; child; child = child.nextSibling) ...
for (let frame = aElement.ownerDocument.defaultView; frame !=
 content; frame = frame.parent) ...
for (let row; (row = aResultSet.getNextRow());) ...
the above is a little odd
for (let i = 0; this[i] != null; i++) ...
 
 In all these cases, per-iteration bindings would do the right thing.
 
 If the choice comes down to astonishing this programmer:
 
   for (let i = 0; i  n; i++) {
   buttons[i] = makeButton();
   buttons[i].click(function () { alert(pushed button  + i); });
   }

I would actually feel a lot more comfortable with per iteration binding of 
for(;;) if the for let declaration was limited to a single binding.  In that 
case, a programmer really has to go out of their way to closure capture the 

Re: lexical for-in/for-of loose end

2012-02-03 Thread Allen Wirfs-Brock

On Feb 3, 2012, at 4:26 PM, Jason Orendorff wrote:

 On 2/3/12 6:13 PM, Allen Wirfs-Brock wrote:
 But I also have to validly (and hopefully reasonably) specify exactly what 
 happens for the unrealistic use cases. There is a problem with your 
 desugaring in that the evaluation of INIT isn't scoped correctly relative to 
 V.
 Hmmm. I don't see the problem yet. I think it's scoped the way I intended it: 
 INIT is evaluated in the enclosing environment; V isn't in scope.

Under the scoping rules TC39 has agreed to, the initializer of a let/const is 
always shadowed by the binding it is initializing:

{ 
   let i=outer;
   {
  let i=i;   // throws reference error because I is initialized
   }
}

Hence, for consistency, this also should throw in the same way 
{
let i=outer;
for (let i=i;;){}
}

You can tweak your desugaring to get this effect by changing the first part of 
it it:

{
let V = INIT;
let %tmp = V, %first = true;
while (true) {
 
 Anyway—all this should be easier to express in spec language than via 
 desugaring, because in spec language you can give an environment a name. This 
 makes it easier to just say what you mean. Whereas if you're using 
 desugaring, scopes are *places*, so you have to very carefully say half of 
 what you mean while standing in the right place to express the rest.

agreed

 
 -j
 

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-02 Thread Allen Wirfs-Brock

On Feb 1, 2012, at 7:13 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 On Feb 1, 2012, at 3:31 PM, Brendan Eich wrote:
 Brendan Eich wrote:
 ...
 That raises the question of the role of the for-in statement in the language 
 (besides just being an obsolete and less useful alternative to for-of).  
 Being a full fledged syntactic statement of the language, it certainly isn't 
 hiding in an obscure corner.  If we want to think of it as something other 
 than an over exposed reflection tool, what is it for?.  The best expression 
 of this that I can come up with, is that for-in is a statement that is 
 primarily useful for iterating the data member keys of simple record and 
 array like data abstractions.

I probably should have included particularly those created using JSON.parse 
at the end of the last sentence above.
 
 So far, so good, and latent here is the best case for not elaborating the 
 left-hand side to include destructuring, if you can avoid bloating the 
 grammar too much. Contra my regularity plus small use-cases argument, we 
 leave for-in alone. We can even leave it underspecified as in ES5.

but still loose the initializer on var, right?

Also, for consistency I still allow let/const in place or var with with fresh 
bindings on each iterations.  These are really orthogonal issues relating to 
the new blocked scoped declaration and we should handle them constantly 
everywhere. 

 
 From that perspective,  own-ness again is something that should primarily be 
 an implementation concern.
 
 Historically this came up because pre-ES5 people couldn't extend 
 Array.prototype, e.g., without making enumerable properties. But of course 
 then the wisdom said don't use for-in on arrays. Object.prototype is 
 verboten, and at some cost in using custom functional-programming style 
 iteration, we're ok (PrototypeJS status quo).
 
 The problem that may remain is that
 
  for own (k in o) ...;
 
 still beats
 
  for (k in keys(o)) ...;
 
 not only by a couple of chars not counting import or assuming prelude, but in 
 terms of people's historical memory and folk-wisdom learning.

I would think we could make this 
   for (k in own(o)) ...;
and
   for (k of own(o)) ...;
if the object produced by own() provides appropiate definitions for 
[[Enumerate]] and [[Iterate]]

Presumably, the only prior leaning that applies comes from CoffeeScript.  While 
CS is popular, it isn't at all close to being used by the majority of JS 
developers.  CS experience is informative but I don't think we have to worry so 
much about CS derived habits.

 
 Should we ignore all this and say just use for-of with the right iterator?

probably...

 
 To me, for-own-in makes it too easy for client code to create dependencies 
 upon what should inconsequential implementation decisions.
 
 In reality the shoe is on the other foot. Implementation decisions that 
 should be without consequence mess up for-in, requiring hasOwnProperty 
 testing. People want a shorter-path solution.
 
 Giving a longer-path solution with for-of and iterators is good, we want 
 that. Is it good enough to be the only solution we build into the language?

If we agree that for-in is the problem, then it seems like we should be 
trying to make for-of more attractive while at the same time making for-in less 
attractive (or at least not improving for-in in a way that also makes it more 
attractive)

 
 We have a goal of making ES a language that is better for creating and using 
 abstractions.  For-own-in is a tool that tunnels through abstractions.
 
 I think you're making too sweeping a statement about abstractions. Does 
 for-own-in tunnel in ways that violate abstractions, or is it really what the 
 doctor ordered for JS close to today's best practices (not always followed)? 
 I say more the latter, but not clear cut in any event.

JS today isn't a very good language for building abstractions of the sort that 
are useful for structuring complex applications and correcting that is one of 
our goals for Harmony.  Today's JS best practices don't really reflect this 
perspective and we should be careful about enshrining backwards looking 
practices.

 
 Such a tool is fine as part of the reflection facility, but making it a full 
 fledged statement seems like it would be creating an attractive nuisance.
 
 We have a full-fledged statement, for-in. It can't be deprecated quite yet. 
 Adding for-of helps, but is it enough? That's the issue I'm raising.
 
 ...
 
 Our current position is use for-of and an items helper function:
 
 import items from @reflect; // currently @iter; could be part of standard 
 prelude
 
 for ([k, v] of items(o)) ...;
 
 see the main part of the message I referenced above. Pure functional filters 
 like item are unfriendly to collection abstracton builders.
 
 JS has little of the OO collection building you see in Smalltalk or Java. Is 
 this a bug in the language and communities using it? I'm not so sure.

I think this is primarily a 

Re: lexical for-in/for-of loose end

2012-02-02 Thread Allen Wirfs-Brock

On Feb 1, 2012, at 7:19 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 water under the bridge...
 
 Sure, but it matters for distinguishing my inconsistent advocacy of 
 removing the hated initialiser, vs. keeping destructuring in for-in LHS.
 
 Really, I'm ok with not supporting destructuring for-in LHS. That will 
 require more spec work, but so does killing the initialiser for let and const 
 as you noted. So, are we agreed? No initialiser, no for-in destructuring 
 (with or without a declaring keyword)?

sounds good to me, see other message



 
 /be
 

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Maybe ban holes, then... (was: Re: lexical for-in/for-of loose end)

2012-02-02 Thread Herby Vojčík



Allen Wirfs-Brock wrote:

That said, I'd hate to see things like:

[,x] = someArray;

and even
[a,,y] = someArray;

can be easily missed.

It isn't clear to me that we are doing ES programmers (especially those
with poor eyesight) a favor by allowing such expressions. What is the
evidence with JS1.7 up. How much usage do we see of such holes?


If

  {5:x} = someArray;

and

  {0:a, 2:y} = someArray;

would be possible, there is probably little need for holes. In case of

  [a,b,c,d,,f] = someArray;

one could always use (some conventional) anon-var, like _ (from Prolog):

  [a,b,c,d,_,f] = someArray;

Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Maybe ban holes, then... (was: Re: lexical for-in/for-of loose end)

2012-02-02 Thread Brendan Eich
This is all unwanted innovation. We have holes in arrays, and in 
SpiderMonkey and Rhino we've had holes in destructuring array patterns 
for years. No one got confused or went blind. We should stop nanny-ing 
about holes.


Some of you don't like holes, but they're not going away in arrays. They 
are useful in array patterns to avoid _ or junk bindings. That's enough 
to keep them, rather than straining to invent more cumbersome ad-hoc 
replacements.


/be


Herby Vojčík mailto:he...@mailbox.sk
February 2, 2012 11:49 AM




If

  {5:x} = someArray;

and

  {0:a, 2:y} = someArray;

would be possible, there is probably little need for holes. In case of

  [a,b,c,d,,f] = someArray;

one could always use (some conventional) anon-var, like _ (from Prolog):

  [a,b,c,d,_,f] = someArray;

Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Maybe ban holes, then... (was: Re: lexical for-in/for-of loose end)

2012-02-02 Thread Herby Vojčík

Allen Wirfs-Brock wrote:

It isn't just possible, it is already specified as valid. and
youdon't need the quotes around the array indices. But you need a set
ofparams to deal with the expression statement can't start with {
problem. This wouldn't be the case for binding position
destructuring.

({5:x} = someArray);
({0:a, 2:y} = someArray);


A bit off-topic... I don't like paretheses. Not only because I have to 
put them in both ends, but also because of uncomfortable feeling of 
thing closed in parentheses.


Maybe alternative convention to deal with expressions beginning with { 
could be used as well:


  0, {5:x} = someArray;
  0, {0:a, 2:y} = someArray;

Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-02 Thread Brendan Eich

Allen Wirfs-Brock wrote:

On Feb 1, 2012, at 7:13 PM, Brendan Eich wrote:
So far, so good, and latent here is the best case for not elaborating 
the left-hand side to include destructuring, if you can avoid 
bloating the grammar too much. Contra my regularity plus small 
use-cases argument, we leave for-in alone. We can even leave it 
underspecified as in ES5.


but still loose the initializer on var, right?


I think so. We can try. I'll see if Oliver and Gavin are game to try in JSC.

Also, for consistency I still allow let/const in place or var with 
with fresh bindings on each iterations.  These are really orthogonal 
issues relating to the new blocked scoped declaration and we should 
handle them constantly everywhere.


Yes! Good news is we agreed (most of us, it was late the second day of 
last month's TC39 meeting) to make for(;;) have the same fresh binding 
per iteration.



From that perspective,  own-ness again is something that should 
primarily be an implementation concern.


Historically this came up because pre-ES5 people couldn't extend 
Array.prototype, e.g., without making enumerable properties. But of 
course then the wisdom said don't use for-in on arrays. 
Object.prototype is verboten, and at some cost in using custom 
functional-programming style iteration, we're ok (PrototypeJS status 
quo).


The problem that may remain is that

 for own (k in o) ...;

still beats

 for (k in keys(o)) ...;

not only by a couple of chars not counting import or assuming 
prelude, but in terms of people's historical memory and folk-wisdom 
learning.


I would think we could make this
   for (k in own(o)) ...;
and
   for (k of own(o)) ...;


keys etc. are currently spec'ed to to do own-only, you want allKeys to 
do the crazy thing:


http://wiki.ecmascript.org/doku.php?id=harmony:iterators#standard_api

Let's not go around and around on this. Enumerable proto-properties are 
harmful, no one wants them.


if the object produced by own() provides appropiate definitions for 
[[Enumerate]] and [[Iterate]]


Jason's cheat-sheet (I cited it fully) shows good enough sugar if you 
do the import, or we agree on the prelude.


Presumably, the only prior leaning that applies comes from 
CoffeeScript.  While CS is popular, it isn't at all close to being 
used by the majority of JS developers.  CS experience is informative 
but I don't think we have to worry so much about CS derived habits.


It's informative, agreed, and competitive -- my point. TC39 does poorly 
inventing new and untested API. We do better paving cowpaths. I think we 
should be careful not to get too far ahead of de-facto standards, while 
avoiding being hostage to language design flaws from the past. We also 
should not oversell Smalltalk, etc. -- Smalltalk is informative (your 
collections port especially) but not particularly more important (or 
less) than CS.



Should we ignore all this and say just use for-of with the right 
iterator?


probably...


Ok. Is this what you are doing (modulo the initialiser removal)?
I assume that you are arguing that the default @iterator for 
Object.prototype (however it is provided)



No! I clearly said there is no @iterator in Object.prototype.

does a key enumeration just like ES1-5 for-in.  Or are you arguing 
that it produces nothing?


No @iterator in Object.prototype. We've been over this, Jason argued 
convincingly against pre-defining one because it is future-hostile to 
collection iteration.

I just need to find the right thread...


Me too.

There is still a default.  The iterators proposal says that for-of 
falls back to @enumerate if @iterate isn't present.


That's a proxy trap, and I believe that dates from before for-of. We 
need to revisit this now, based on splitting iteration out to for-of and 
leaving for-in the old enumerating mystery meat that it has always been. 
Cc'ing Tom.


It's important to distinguish property default from proxy trap built-in.

If there's an Object.prototype.@iterator that iterates values, or 
[key,value] pairs, or whatever, then we make it hard for new collections 
and set-like abstractions to do something slightly different.


BTW, this is a good example of something that should not be built into 
statement semantics.  Why should |for (v or values(o))...| iterate 
differently than |values(o).forOf({|v|...})|


Do you mean forEach?

Who says values(o) returns an array? That is costly and the point of 
iterators is to use a lazy protocol. Indeed the


http://wiki.ecmascript.org/doku.php?id=harmony:iterators#standard_api

proposal has values a generator (returns an iterator).

My currently thinking (working off of the current wiki proposal) is to 
have [[Enumerate]] and [[Iterate]] internal methods (for all objects) 
and it is in their default implementation that [[Iterate]] delegates 
to [[Enumerate]].


No, see above. We do not want to mix these up. Iteration via for-of uses 
the iteration protocol.



No issue here, I think. We want for (x of {p:1, q:2}) to 

Re: Maybe ban holes, then... (was: Re: lexical for-in/for-of loose end)

2012-02-02 Thread Andreas Rossberg
On 2 February 2012 21:19, Brendan Eich bren...@mozilla.org wrote:
 Some of you don't like holes, but they're not going away in arrays. They are
 useful in array patterns to avoid _ or junk bindings. That's enough to keep
 them, rather than straining to invent more cumbersome ad-hoc replacements.

Are you implying that wildcards are more ad-hoc than array holes? That
doesn't add up for me.

/Andreas
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-02 Thread Allen Wirfs-Brock

On Feb 2, 2012, at 1:38 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 On Feb 1, 2012, at 7:13 PM, Brendan Eich wrote:
 ...

Unfortunately, having thought about it a bit since the meeting, I'm less 
convinced. We didn't look too deeply at possible capture scenarios such as:

let geti;
for (let i=(geti = function() {return i},expr), expr, incr = function(i++), 
decr=function(i--), cmp=function(){return in}; cmp();incr()) {
   let j=i;
   i +=10;
   decr();
   ...
   If (j+-9 !== i) ...
   ...
}

 ...
 
 keys etc. are currently spec'ed to to do own-only, you want allKeys to do the 
 crazy thing:
 
 http://wiki.ecmascript.org/doku.php?id=harmony:iterators#standard_api
 
 Let's not go around and around on this. Enumerable proto-properties are 
 harmful, no one wants them.

I'm afraid I have to disagree with the absolutism of this statement.  There are 
some perfectly reasonable use cases for enumerating proto properties and they 
are quite in alignment with prototypal inheritance.  For example, how about 
defining a set of default property values and then using prototypal inheritance 
to selective over-ride them:

let privateImplicitOptions = {a:1,b:2,c:3 /* and on and on */};

function makeOptions(explicitOptions) {
let opt = Object.create(privateImplicitOptions);
for (let [k.,v] of items(explicitOptions)) opt[k]=v;
return opt;
}

myOpts = makeOptions({b:-2});

Why should somebody I pass an options object to need to worry about whether I 
used zero, one, or n levels of prototypes to represent by options.

that said, I think we agree that the real problem is the legacy ES semantics of 
for-in and its use (or lack of) for the enumerable attribute. If we can start 
out with a clean slate conceptual slate for for-of maybe we don't have to worry 
about such things.  However, carrying over a lot of historic baggage isn't a 
clean slate.  Thinking in terms of abstract collections, having keys() not mean 
all keys in the collection seems quite absurd.  That should be the normal 
case, the more verbose name should be used for the exceptional case that 
excludes some of the keys.  Now in this case, the real problem is in deciding 
the default way to view a plain Es objects as an abstract collection. Maybe 
only considering own properties  as the collection elements is the right thing. 
 In that case it may be allKeys that is carrying the wrong implication and 
instead it should be something like extendedKeys.

 ...
 
 Presumably, the only prior leaning that applies comes from CoffeeScript.  
 While CS is popular, it isn't at all close to being used by the majority of 
 JS developers.  CS experience is informative but I don't think we have to 
 worry so much about CS derived habits.
 
 It's informative, agreed, and competitive -- my point. TC39 does poorly 
 inventing new and untested API. We do better paving cowpaths. I think we 
 should be careful not to get too far ahead of de-facto standards, while 
 avoiding being hostage to language design flaws from the past. We also should 
 not oversell Smalltalk, etc. -- Smalltalk is informative (your collections 
 port especially) but not particularly more important (or less) than CS.

Totally agree concerning Smalltalk  other languages.  However, when we are 
looking to extend into new areas it makes sense to look at languages that excel 
in those areas. We're talking about making ES a better to define object-based 
abstractions and in specifically in this thread collection abstractions. That 
is an area where we can learn a lot from Smalltalk.
 
 ...
 
 There is still a default.  The iterators proposal says that for-of falls 
 back to @enumerate if @iterate isn't present.
 
 That's a proxy trap, and I believe that dates from before for-of. We need to 
 revisit this now, based on splitting iteration out to for-of and leaving 
 for-in the old enumerating mystery meat that it has always been. Cc'ing Tom.
 
 It's important to distinguish property default from proxy trap built-in.

Not sure, I understand why.  Presumably the default behavior of a proxy trap 
should be the same as the built-in behavior.  The internal methods (which need 
to be extended to include things like [[Interator]]) have a specification for  
normal objects and an alternate one for Proxy objects that just invokes the 
trap.  However, the default traps handlers should be specified to turn around 
and invoke the corresponding normal internal method.

 
 If there's an Object.prototype.@iterator that iterates values, or [key,value] 
 pairs, or whatever, then we make it hard for new collections and set-like 
 abstractions to do something slightly different.

I don't see where the hardness comes from.  They just need to define an 
appropaite @Interator method. They are going to have to do so regardless of 
whether a default @Iterator is defined on Object.prototype or whether the 
default is just hardwired (for example to throw).  In either case the default  
needs to be 

Re: lexical for-in/for-of loose end

2012-02-02 Thread Jason Orendorff
On Thu, Feb 2, 2012 at 5:52 PM, Allen Wirfs-Brock al...@wirfs-brock.com wrote:
 Unfortunately, having thought about it a bit since the meeting, I'm less
 convinced. We didn't look too deeply at possible capture scenarios such as:

 let geti;
 for (let i=(geti = function() {return i},expr), expr, incr = function(i++),
 decr=function(i--), cmp=function(){return in}; cmp();incr()) {
        let j=i;
        i +=10;
        decr();
        ...
        If (j+-9 !== i) ...
        ...
 }

What?! We should not reward people writing code like that.

I would be fine with the initializer-expression not being in the scope
of i at all.

-j
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-02 Thread Allen Wirfs-Brock

On Feb 2, 2012, at 5:07 PM, Jason Orendorff wrote:

 On Thu, Feb 2, 2012 at 5:52 PM, Allen Wirfs-Brock al...@wirfs-brock.com 
 wrote:
 Unfortunately, having thought about it a bit since the meeting, I'm less
 convinced. We didn't look too deeply at possible capture scenarios such as:
 
 let geti;
 for (let i=(geti = function() {return i},expr), expr, incr = function(i++),
 decr=function(i--), cmp=function(){return in}; cmp();incr()) {
let j=i;
i +=10;
decr();
...
If (j+-9 !== i) ...
...
 }
 
 What?! We should not reward people writing code like that.
 
 I would be fine with the initializer-expression not being in the scope
 of i at all.

No reward, it is simply what the current syntax permits, and it is important 
that semantics are consistently applied.  The initialization semantics I assume 
here are exactly the same that would apply if if the let clause  of the for was 
pulled out and used as a standalone statement. In general, excluding i from the 
visibility of the initializer expression would prevent writing recursive 
functions definitions such as:

   const fact = function (n) {return n=1 ? 1 : n*fact(n-1)};

for-in works well with a per iteration binding because there is no header code 
that runs between iterations.  The various desugarings that have been proposed 
to provide a fresh copy for each iteration of this form of for work for 
propagating simple values between the header and each iteration but not for 
closure captures.

Plus, the desugarings aren't things that are really suitable for teaching the 
semantics to everyday JS programmers.  You instead have to say something like:

Ok, this is really complicated but here goes.  For each let/const declared in 
the for header, a fresh variable is located in the loop body for each iteration 
of the loop. However, the values of the loop variables are automatically copied 
from the previous iteration into the next iterations.  This means that basic 
expression operator will work in the loop header pretty much like you would 
expect.  But be careful if you use any function expressions in the for header 
because the loop variables they reference may not be from the current iteration 
and any changes to loop variable they make may not have the effect you 
intended.  But, hey you probably shouldn't do those things so it really doesn't 
matter what it really does.

Allen




___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-02-01 Thread Erik Arvidsson
I did a code search for for (var ident = before code search was shut down
and the only code it found was from test suites.
On Jan 31, 2012 6:31 AM, Nadav Shesek na...@shesek.info wrote:

 On Jan 31, 2012 3:38 PM, Sam Tobin-Hochstadt sa...@ccs.neu.edu wrote:
 
  Google Code Search is no longer available, sadly.
 

 Oh, I didn't know it was shutdown. Hopefully some Google internals can
 still use that data to give some stats when those questions arise...

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-02-01 Thread Andrea Giammarchi
I think it would be semantically correct to allow at least let there as
it is for loops

for (let i = 0; i  length; i++); // OK

for (let key in obj); // not OK ?

weird, I know destructuration is OK but for/in are still for loops

my 2 cents,

br
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-02-01 Thread Erik Arvidsson
Sorry, the search was a lot more elaborate to include in too.
On Feb 1, 2012 9:29 AM, Erik Arvidsson erik.arvids...@gmail.com wrote:

 I did a code search for for (var ident = before code search was shut
 down and the only code it found was from test suites.
 On Jan 31, 2012 6:31 AM, Nadav Shesek na...@shesek.info wrote:

 On Jan 31, 2012 3:38 PM, Sam Tobin-Hochstadt sa...@ccs.neu.edu wrote:
 
  Google Code Search is no longer available, sadly.
 

 Oh, I didn't know it was shutdown. Hopefully some Google internals can
 still use that data to give some stats when those questions arise...


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Allen Wirfs-Brock
Another loose end.

With the addition of for-of, for-in reverts back to always iterating over the 
property keys of an object as it does not support the generalized iteration 
protocols.  An implication of this is that using a de-structuring pattern as 
the iteration variable has very limited utility:

for (let [c] in obj) print(c);  //print the first character of each of obj's 
enumerable property names

for (const {length:len}  in obj) print(len);  print the length of each of obj's 
enumerable property names

Given this lack of utility, why should we allow de-structuring in this context?

Eliminating it arguably increases language complexity by introducing in a bit 
more grammar irregularity.  On the other hand, eliminating useless 
functionality can be seen as as simplification.

I'm leaning towards banning destructing in for-in.

Allen




___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Brendan Eich

Allen Wirfs-Brock wrote:

Another loose end.

With the addition of for-of, for-in reverts back to always iterating 
over the property keys of an object as it does not support the 
generalized iteration protocols.  An implication of this is that using 
a de-structuring pattern as the iteration variable has very limited 
utility:


for (let [c] in obj) print(c);  //print the first character of
each of obj's enumerable property names

for (const {length:len}  in obj) print(len);  print the length
of each of obj's enumerable property names


Given this lack of utility, why should we allow de-structuring in this 
context?


These are not totally silly examples. I say when in doubt, let language 
regularity win.


We are also thereby future-friendly in case some evolution of property 
keys becomes even more structured. I know, unlikely, but again: 
regularity when in doubt.


Eliminating it arguably increases language complexity by 
introducing in a bit more grammar irregularity.  On the other hand, 
eliminating useless functionality can be seen as as simplification.


Not useless, your own examples show use-cases.


I'm leaning towards banning destructing in for-in.


Too nannyish!

/be


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Allen Wirfs-Brock

On Feb 1, 2012, at 2:13 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 Another loose end.
 
 With the addition of for-of, for-in reverts back to always iterating over 
 the property keys of an object as it does not support the generalized 
 iteration protocols.  An implication of this is that using a de-structuring 
 pattern as the iteration variable has very limited utility:
 
for (let [c] in obj) print(c);  //print the first character of
each of obj's enumerable property names
 
for (const {length:len}  in obj) print(len);  print the length
of each of obj's enumerable property names
 
 
 Given this lack of utility, why should we allow de-structuring in this 
 context?
 
 These are not totally silly examples. I say when in doubt, let language 
 regularity win.

Well, they're at least 98% silly and these were the only even semi-plausable 
example I could think of.
 
 We are also thereby future-friendly in case some evolution of property keys 
 becomes even more structured. I know, unlikely, but again: regularity when in 
 doubt.

In which case, it would be easy enough to allow them in the future. It's always 
easer to relax a restriction then it is to add one.

 
 Eliminating it arguably increases language complexity by introducing in a 
 bit more grammar irregularity.  On the other hand, eliminating useless 
 functionality can be seen as as simplification.
 
 Not useless, your own examples show use-cases.
 
 I'm leaning towards banning destructing in for-in.
 
 Too nannyish!

I don't really see the consistency in trying to ban the initializer in
  for (var k=42 in obj) ;
because it is a awful, lazy-grammar-resue error.  While adding
  for (var [c] in obj) ;

I don't actually feel too strongly either way and it is actually more work for 
me to disallow the destructuring pattern.  However, I am trying to find some 
basis for consistency in the design choices we make.

Allen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Brendan Eich

Allen Wirfs-Brock wrote:

On Feb 1, 2012, at 2:13 PM, Brendan Eich wrote:

  Allen Wirfs-Brock wrote:

  Another loose end.
  
  With the addition of for-of, for-in reverts back to always iterating over the property keys of an object as it does not support the generalized iteration protocols.  An implication of this is that using a de-structuring pattern as the iteration variable has very limited utility:
  
  for (let [c] in obj) print(c);  //print the first character of

  each of obj's enumerable property names
  
  for (const {length:len}  in obj) print(len);  print the length

  of each of obj's enumerable property names
  
  
  Given this lack of utility, why should we allow de-structuring in this context?
  
  These are not totally silly examples. I say when in doubt, let language regularity win.


Well, they're at least 98% silly and these were the only even semi-plausable 
example I could think of.


So you agree with my point about regularity + non-silly use-cases 
winning? :-/




  We are also thereby future-friendly in case some evolution of property keys 
becomes even more structured. I know, unlikely, but again: regularity when in 
doubt.


In which case, it would be easy enough to allow them in the future. It's always 
easer to relax a restriction then it is to add one.


How would you restrict them? Splitting productions, or semantic 
restrictions? It matters, not just aesthetically but for implementation 
simplicity.




I don't really see the consistency in trying to ban the initializer in
   for (var k=42 in obj) ;
because it is a awful, lazy-grammar-resue error.


ES1 was based on real implementations. The Netscape one did not allow an 
initaliser here. There's no good use for it compared to destructuring 
key lengths or substrings, especially not with var (which hoists). You 
want an initial var value to survive a zero-iteration loop? Hoist the var.



   While adding
   for (var [c] in obj) ;


Are you really adding? It depends on good vs. bad reuse of 
sub-grammar. I appealed to regularity. That means standard rules for 
conjugating verbs, etc., in natural languages.


In programming languages one would hope that binding forms would compose 
the same with for-in as for-of as for;; and other binding contexts, 
modulo the initialiser. At least, that's what users of JS1.7-and-up have 
hoped.


That evidence is tainted because we did make for-in programmable via an 
iteration protocol, as for-of is in ES6. Granted. But unless there's an 
actual hardship in regularity (I don't believe there is with for-in, but 
I welcome evidence from you on spec front, and other implementors on 
impl front) I still think regularity wins.



I don't actually feel too strongly either way and it is actually more work for 
me to disallow the destructuring pattern.


I'm not super-strong on this, don't get me wrong. It's much less of an 
issue either way than banning the initialiser for let and const. I hope 
we agree on that much, as a partial order of preferences or strong 
convictions!


It also seems relevant that you'd have to do more work in the spec, and 
presumably make the grammar bigger, to disallow destructuring (with and 
without var/let/const) in for-in. More spec effort and grammar size make 
for errata habitat. Just as with code.




   However, I am trying to find some basis for consistency in the design 
choices we make.


Again, for-in as designed in JS1 never allowed an initialiser and it's 
an unwanted feature added to match JScript or (perhaps JScript too 
lacked it) to reuse part of the grammar a certain way.


Note that the reuse of VariableDeclarationNoIn didn't save anything in 
the grammar, so I bet the issue was JScript allowing an initialiser. 
Anyone know?


It's never inconsistent to allow one thing and disallow another. The 
particulars matter. This isn't anything goes. Destructuring has a bit 
of utility and a lot of regularity in for-in head position. The 
initialiser from VariableDeclarationNoIn has neither specific utility 
nor regularity with respect to for-in in AWK, Python, or any other 
language with such a construct.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Brendan Eich

Brendan Eich wrote:
It's never inconsistent to allow one thing and disallow another. The 
particulars matter. This isn't anything goes. Destructuring has a 
bit of utility and a lot of regularity in for-in head position. The 
initialiser from VariableDeclarationNoIn has neither specific utility 
nor regularity with respect to for-in in AWK, Python, or any other 
language with such a construct. 


More to say, I sent too soon. Don't take this as more than it is: an 
attempt to explore an alternative meaning of for-in combined with 
certain destructuring patterns.


We have a request that seemed to receive popular support, to support

  for own (k in o) ...;

This could compose with the following nicely, and it tries to pave the 
CoffeeScript cowpath a bit (without taking CoffeeScript as normative or 
overriding or anything like that).


We could make just-so meanings for destructuring in for-in, also 
inspired by CoffeeScript (and JS1.7, which did this too while muddying 
the waters by failing to separate iteration protocol into for-of):


  for ([k, v] in o) ...;

Our current position is use for-of and an items helper function:

  import items from @reflect; // currently @iter; could be part of 
standard prelude


  for ([k, v] of items(o)) ...;

But I think detailing the design of ES6 must be allowed to entertain an 
even easier-to-use extension to for-in.


If you want values not keys, then per the current proposal you use

  for (v of values(o)) ...;

given the necessary values import, or inclusion in a standard prelude.

There's a hole vs. CoffeeScript: we do not want for-of on a plain Object 
to iterate over enumerable property values. Arrays, yes, Objects, no -- 
no Object.prototype.@iterator. So no |for (v of o) ...| for o = {p:1, 
q:2, r:3}.


Also no for each (v in o) as E4X (ECMA-357) promulgated. SpiderMonkey 
and Rhino support it and probably will have to carry it, but such each 
does not say its meaning (values not keys) clearly, and it doesn't 
compose with own nicely.


But with destructuring for-in, you could write

  for ([, v] in o) ...;

This is a bit ugly (holes never look pretty, even when they're useful).

Not sure what I think of this but I thought I'd throw it out here on 
es-discuss. The reason I bring it up is twofold: 1) for own (k in o) 
still needs to be discussed; 2) the for ([k, v] of items(o)) ...; tax is 
a bit higher than I'd like.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-02-01 Thread Allen Wirfs-Brock

On Feb 1, 2012, at 3:31 PM, Brendan Eich wrote:

 Brendan Eich wrote:
 It's never inconsistent to allow one thing and disallow another. The 
 particulars matter. This isn't anything goes. Destructuring has a bit of 
 utility and a lot of regularity in for-in head position. The initialiser 
 from VariableDeclarationNoIn has neither specific utility nor regularity 
 with respect to for-in in AWK, Python, or any other language with such a 
 construct. 
 
 More to say, I sent too soon. Don't take this as more than it is: an attempt 
 to explore an alternative meaning of for-in combined with certain 
 destructuring patterns.
 
 We have a request that seemed to receive popular support, to support
 
  for own (k in o) ...;
 
 This could compose with the following nicely, and it tries to pave the 
 CoffeeScript cowpath a bit (without taking CoffeeScript as normative or 
 overriding or anything like that).

Towards the end of 
https://mail.mozilla.org/pipermail/es-discuss/2011-November/018332.html I made 
an argument  that own-ness of properties  is an implementation choice that 
normally should not be of concern to application clients of an object-based 
abstraction.   Own-ness  is primarily relevant to abstraction implementors or 
somebody who is using reflection to examine the implementation structure of an 
object.

Hopefully reflection is tucked away in a obscure but pleasant corner of the 
language and not widely used for routine application logic. Generally, 
reflection doesn't deserve (or require) statement level support.

That raises the question of the role of the for-in statement in the language 
(besides just being an obsolete and less useful alternative to for-of).  Being 
a full fledged syntactic statement of the language, it certainly isn't hiding 
in an obscure corner.  If we want to think of it as something other than an 
over exposed reflection tool, what is it for?.  The best expression of this 
that I can come up with, is that for-in is a statement that is primarily useful 
for iterating the data member keys of simple record and array like data 
abstractions. From that perspective,  own-ness again is something that should 
primarily be an implementation concern.  Clients who are iterating over my 
public data members shouldn't  be concerned about whether or not I decided to 
implement my data abstraction using a tree of objects or be impacted if I 
change my mind.

To me, for-own-in makes it too easy for client code to create dependencies upon 
what should inconsequential implementation decisions. 

We have a goal of making ES a language that is better for creating and using 
abstractions.  For-own-in is a tool that tunnels through abstractions.  Such a 
tool is fine as part of the reflection facility, but making it a full fledged 
statement seems like it would be creating an attractive nuisance.
 
 We could make just-so meanings for destructuring in for-in, also inspired by 
 CoffeeScript (and JS1.7, which did this too while muddying the waters by 
 failing to separate iteration protocol into for-of):
 
  for ([k, v] in o) ...;

Clearly, this isn't a general destructuring.  It is a special syntactic form 
that is mimicking array destructuring syntax. We could probably have a long 
thread about whether such mimicry is clever language design or a confusing 
creole.  I reserve my opinion on the general questions. 

 
 Our current position is use for-of and an items helper function:
 
  import items from @reflect; // currently @iter; could be part of standard 
 prelude
 
  for ([k, v] of items(o)) ...;

see the main part of the message I referenced above. Pure functional filters 
like item are unfriendly to collection abstracton builders. 

 
 But I think detailing the design of ES6 must be allowed to entertain an even 
 easier-to-use extension to for-in.

Another simplification would be to make default iterator produce {key,value} 
item objects,

Then we have three concise formulations:

for ({key, value} of o) ...;
for  ({key} of o) ...;
for ({value} of o) ...
  
 If you want values not keys, then per the current proposal you use
 
  for (v of values(o)) ...;
 
 given the necessary values import, or inclusion in a standard prelude.
 
 There's a hole vs. CoffeeScript: we do not want for-of on a plain Object to 
 iterate over enumerable property values. Arrays, yes, Objects, no -- no 
 Object.prototype.@iterator. So no |for (v of o) ...| for o = {p:1, q:2, r:3}.
(I note at this point that there are some huge es-discuss threads on topics 
such as the above for which the conclusions (if any) have not been extracted. 
I'm starting to go over them to see what I can extract from them)

I assume that you are arguing that the default @iterator for Object.prototype 
(however it is provided) does a key enumeration just like ES1-5 for-in.  Or are 
you arguing that it produces nothing? I'm not exactly sure why either of those 
is better than |for (v of o) ...| but I think there are a couple threads that I 

Re: lexical for-in/for-of loose end

2012-02-01 Thread Allen Wirfs-Brock

On Feb 1, 2012, at 3:05 PM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
 On Feb 1, 2012, at 2:13 PM, Brendan Eich wrote:
   Allen Wirfs-Brock wrote:
   Another loose end.
 With the addition of for-of, for-in reverts back to always 
  iterating over the property keys of an object as it does not support 
  the generalized iteration protocols.  An implication of this is that 
  using a de-structuring pattern as the iteration variable has very 
  limited utility:
 for (let [c] in obj) print(c);  //print the first character of
   each of obj's enumerable property names
 for (const {length:len}  in obj) print(len);  print the length
   of each of obj's enumerable property names
   Given this lack of utility, why should we allow de-structuring 
  in this context?
 These are not totally silly examples. I say when in doubt, let 
  language regularity win.
 
 Well, they're at least 98% silly and these were the only even semi-plausable 
 example I could think of.
 
 So you agree with my point about regularity + non-silly use-cases winning? :-/

I like regularity, which was why I was content to keep the 98+% silly 
initialization expression in for-in  |for (var k=debugLog(starting loop) in 
obj)...|. keeps it all together for refactoring :-\

But, in reality I was probably more concerned about the breaking change issue.
 
 
   We are also thereby future-friendly in case some evolution of property 
  keys becomes even more structured. I know, unlikely, but again: 
  regularity when in doubt.
 
 In which case, it would be easy enough to allow them in the future. It's 
 always easer to relax a restriction then it is to add one.
 
 How would you restrict them? Splitting productions, or semantic restrictions? 
 It matters, not just aesthetically but for implementation simplicity.

In this particular case I would probably use separate productions (already did 
that to eliminate the initializers for |for (let/const x in)|.  In general, I 
prefer to only use static semantic restrictions only when they involve 
non-nearby contextual information about the program.

However, that is a specification aesthetic.  I don't see why an implementation 
parser would necessarily want to make the same grammar trade-offs. Converting 
grammar to static checks and visa versa seems like very routine parser 
engineering.  
 
 
 
 I don't really see the consistency in trying to ban the initializer in
   for (var k=42 in obj) ;
 because it is a awful, lazy-grammar-resue error.
 
 ES1 was based on real implementations. The Netscape one did not allow an 
 initaliser here. There's no good use for it compared to destructuring key 
 lengths or substrings, especially not with var (which hoists). You want an 
 initial var value to survive a zero-iteration loop? Hoist the var.
 
   While adding
   for (var [c] in obj) ;
 
 Are you really adding? It depends on good vs. bad reuse of sub-grammar. I 
 appealed to regularity. That means standard rules for conjugating verbs, 
 etc., in natural languages.

Well, its adding to the grammar I wrote this morning...
I already have to have  additional productions (or static semantic rules, they 
amount to the same thing) that don't have initializer expressions on the for 
(let/const forms.

But I like standard rules too.  Too many special cases make the language harder 
to learn.  But regularity that allow for WTF situations also isn't good.  There 
is a sweat spot that needs to be found.

 
 In programming languages one would hope that binding forms would compose the 
 same with for-in as for-of as for;; and other binding contexts, modulo the 
 initialiser. At least, that's what users of JS1.7-and-up have hoped.
 
 That evidence is tainted because we did make for-in programmable via an 
 iteration protocol, as for-of is in ES6. Granted. But unless there's an 
 actual hardship in regularity (I don't believe there is with for-in, but I 
 welcome evidence from you on spec front, and other implementors on impl 
 front) I still think regularity wins.

I actually think may two use cases for for-in de-structuring (as currently 
defined)  are pretty close to WTF scenarios.  Most people are going to have to 
stop and think carefully about what they mean.  Probably more important, 
allowing destructuing in for-in is likely to further confuse people regularly 
forget that it iterates over keys rather than values.
 
 I don't actually feel too strongly either way and it is actually more work 
 for me to disallow the destructuring pattern.
 
 I'm not super-strong on this, don't get me wrong. It's much less of an issue 
 either way than banning the initialiser for let and const. I hope we agree on 
 that much, as a partial order of preferences or strong convictions!
 
 It also seems relevant that you'd have to do more work in the spec, and 
 presumably make the grammar bigger, to disallow destructuring (with and 
 without var/let/const) in for-in. More spec effort and grammar size make for 

Re: lexical for-in/for-of loose end

2012-02-01 Thread Brendan Eich

Allen Wirfs-Brock wrote:

On Feb 1, 2012, at 3:31 PM, Brendan Eich wrote:

Brendan Eich wrote:
It's never inconsistent to allow one thing and disallow another. The 
particulars matter. This isn't anything goes. Destructuring has a 
bit of utility and a lot of regularity in for-in head position. The 
initialiser from VariableDeclarationNoIn has neither specific 
utility nor regularity with respect to for-in in AWK, Python, or any 
other language with such a construct.


More to say, I sent too soon. Don't take this as more than it is: an 
attempt to explore an alternative meaning of for-in combined with 
certain destructuring patterns.


We have a request that seemed to receive popular support, to support

 for own (k in o) ...;

This could compose with the following nicely, and it tries to pave 
the CoffeeScript cowpath a bit (without taking CoffeeScript as 
normative or overriding or anything like that).


Towards the end of 
https://mail.mozilla.org/pipermail/es-discuss/2011-November/018332.html I 
made an argument  that own-ness of properties  is an implementation 
choice that normally should not be of concern to application clients 
of an object-based abstraction.   Own-ness  is primarily relevant to 
abstraction implementors or somebody who is using reflection to 
examine the implementation structure of an object.


That's an ideal but the reality is for-in and easy transpilation from 
better derived forms to it are with us, and will be for a while.


Hopefully reflection is tucked away in a obscure but pleasant corner 
of the language and not widely used for routine application logic. 
Generally, reflection doesn't deserve (or require) statement level 
support.


 still want easy array-value iteration. That's what for-of does out of 
the box. This is important, it shouldn't take any more chars than


  for (v of a)...

eliding declaration of v and decl/init of array a.

That raises the question of the role of the for-in statement in the 
language (besides just being an obsolete and less useful alternative 
to for-of).  Being a full fledged syntactic statement of the language, 
it certainly isn't hiding in an obscure corner.  If we want to think 
of it as something other than an over exposed reflection tool, what is 
it for?.  The best expression of this that I can come up with, is that 
for-in is a statement that is primarily useful for iterating the data 
member keys of simple record and array like data abstractions.


So far, so good, and latent here is the best case for not elaborating 
the left-hand side to include destructuring, if you can avoid bloating 
the grammar too much. Contra my regularity plus small use-cases 
argument, we leave for-in alone. We can even leave it underspecified as 
in ES5.


From that perspective,  own-ness again is something that should 
primarily be an implementation concern.


Historically this came up because pre-ES5 people couldn't extend 
Array.prototype, e.g., without making enumerable properties. But of 
course then the wisdom said don't use for-in on arrays. Object.prototype 
is verboten, and at some cost in using custom functional-programming 
style iteration, we're ok (PrototypeJS status quo).


The problem that may remain is that

  for own (k in o) ...;

still beats

  for (k in keys(o)) ...;

not only by a couple of chars not counting import or assuming prelude, 
but in terms of people's historical memory and folk-wisdom learning.


Should we ignore all this and say just use for-of with the right iterator?

To me, for-own-in makes it too easy for client code to create 
dependencies upon what should inconsequential implementation decisions.


In reality the shoe is on the other foot. Implementation decisions that 
should be without consequence mess up for-in, requiring hasOwnProperty 
testing. People want a shorter-path solution.


Giving a longer-path solution with for-of and iterators is good, we want 
that. Is it good enough to be the only solution we build into the language?


We have a goal of making ES a language that is better for creating and 
using abstractions.  For-own-in is a tool that tunnels through 
abstractions.


I think you're making too sweeping a statement about abstractions. Does 
for-own-in tunnel in ways that violate abstractions, or is it really 
what the doctor ordered for JS close to today's best practices (not 
always followed)? I say more the latter, but not clear cut in any event.


 Such a tool is fine as part of the reflection facility, but making it 
a full fledged statement seems like it would be creating an attractive 
nuisance.


We have a full-fledged statement, for-in. It can't be deprecated quite 
yet. Adding for-of helps, but is it enough? That's the issue I'm raising.



We could make just-so meanings for destructuring in for-in, also 
inspired by CoffeeScript (and JS1.7, which did this too while 
muddying the waters by failing to separate iteration protocol into 
for-of):


 for ([k, v] in o) ...;


Clearly, this 

Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-01-31 Thread Herby Vojčík

Erik Arvidsson wrote:

I agree with Brendan, I think we can get rid of the initializer even
for for-in with var. The only code I've seen that uses this is in ES
test suites.


Just an idea. Google crawls over the whole (crawler-accessible) web. 
That means it is indexing a _lot_ of .js files out there.


I saw questions like the one above appear more times on this list. How 
often is [insert-your-own-quirk] used / is it used ever? Code files are 
of defined structure, unlike people-written documents. If Google would 
be able to index code files with some added information that allows to 
search in that structure, it could yield important information about 
usage patterns of this or that programming language.


Herby
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-01-31 Thread Nadav Shesek
Google's code search allows searching for regular expressions. Perhaps that
could be useful.

Sent from Android
On Jan 31, 2012 11:41 AM, Herby Vojčík he...@mailbox.sk wrote:

 Erik Arvidsson wrote:

 I agree with Brendan, I think we can get rid of the initializer even
 for for-in with var. The only code I've seen that uses this is in ES
 test suites.


 Just an idea. Google crawls over the whole (crawler-accessible) web. That
 means it is indexing a _lot_ of .js files out there.

 I saw questions like the one above appear more times on this list. How
 often is [insert-your-own-quirk] used / is it used ever? Code files are of
 defined structure, unlike people-written documents. If Google would be able
 to index code files with some added information that allows to search in
 that structure, it could yield important information about usage patterns
 of this or that programming language.

 Herby
 __**_
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/**listinfo/es-discusshttps://mail.mozilla.org/listinfo/es-discuss

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-01-31 Thread Sam Tobin-Hochstadt
Google Code Search is no longer available, sadly.

On Tue, Jan 31, 2012 at 6:20 AM, Nadav Shesek na...@shesek.info wrote:
 Google's code search allows searching for regular expressions. Perhaps that
 could be useful.

 Sent from Android

 On Jan 31, 2012 11:41 AM, Herby Vojčík he...@mailbox.sk wrote:

 Erik Arvidsson wrote:

 I agree with Brendan, I think we can get rid of the initializer even
 for for-in with var. The only code I've seen that uses this is in ES
 test suites.


 Just an idea. Google crawls over the whole (crawler-accessible) web. That
 means it is indexing a _lot_ of .js files out there.

 I saw questions like the one above appear more times on this list. How
 often is [insert-your-own-quirk] used / is it used ever? Code files are of
 defined structure, unlike people-written documents. If Google would be able
 to index code files with some added information that allows to search in
 that structure, it could yield important information about usage patterns of
 this or that programming language.

 Herby
 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss


 ___
 es-discuss mailing list
 es-discuss@mozilla.org
 https://mail.mozilla.org/listinfo/es-discuss




-- 
sam th
sa...@ccs.neu.edu
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: Google indexing code patterns (Re: lexical for-in/for-of loose end)

2012-01-31 Thread Nadav Shesek
On Jan 31, 2012 3:38 PM, Sam Tobin-Hochstadt sa...@ccs.neu.edu wrote:

 Google Code Search is no longer available, sadly.


Oh, I didn't know it was shutdown. Hopefully some Google internals can
still use that data to give some stats when those questions arise...
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Allen Wirfs-Brock

On Jan 30, 2012, at 6:38 PM, Brendan Eich wrote:

 No! We have a standing agreement to get rid of the awful, lazy-grammar-reuse 
 error in ES1 that allows an initializer in for(var x=i in o). We do not want 
 this at all for 'let' in either for-in or for-of.

Fine by me, WRT let/const

We haven't done a very good job of capturing such agreements.  I don't think 
this one is reflected on the wiki proposals...
 
 I think we should break unconditionally and forbid =i in for(var x=i in o) 
 too, but that is a separate issue. This botch in grammar factoring is a (bad) 
 sunk cost that has zero bearing on the fresh let binding per iteration idea. 
 It's terrible anti-precedent. Just say no.

While I think the language would be better without it.  I don't really see how 
we can justify such a breaking change.  Heck, I can even think of a plausible 
use:

function hasEnumerableProperties (obj) {
   for (var key = false in obj);
   return key!==false
}

Allen

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Brendan Eich

Allen Wirfs-Brock mailto:al...@wirfs-brock.com
January 31, 2012 8:28 AM
On Jan 30, 2012, at 6:38 PM, Brendan Eich wrote:


No! We have a standing agreement to get rid of the awful, lazy-grammar-reuse 
error in ES1 that allows an initializer in for(var x=i in o). We do not want 
this at all for 'let' in either for-in or for-of.


Fine by me, WRT let/const

We haven't done a very good job of capturing such agreements.  I don't think 
this one is reflected on the wiki proposals...


You're right, I thought I'd recorded it. Sorry, fixed:

http://wiki.ecmascript.org/doku.php?id=harmony:iterators#syntax_issues

I think we should break unconditionally and forbid =i in for(var x=i in o) too, 
but that is a separate issue. This botch in grammar factoring is a (bad) sunk 
cost that has zero bearing on the fresh let binding per iteration idea. It's 
terrible anti-precedent. Just say no.


While I think the language would be better without it.  I don't really see how 
we can justify such a breaking change.  Heck, I can even think of a plausible 
use:

function hasEnumerableProperties (obj) {
for (var key = false in obj);
return key!==false
}

Yes, unlike 'let' the binding persists after the loop. We can live with 
this, my hope was that we could also make it an early error and survive 
because as Arv suggests, it's probably used only in testsuites. But you 
never know, and the Web tends to tile all executable surfaces and test 
all features. Let's see how easy it is to split the 'var' and 
'let/const' cases...


But you could make a twisted argument for an effect-ful 'let' 
initialization expression also being useful in this way. We mustn't go 
there.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Allen Wirfs-Brock

On Jan 31, 2012, at 8:36 AM, Brendan Eich wrote:
 
  Let's see how easy it is to split the 'var' and 'let/const' cases...

already done
 
 But you could make a twisted argument for an effect-ful 'let' initialization 
 expression also being useful in this way. We mustn't go there.

might be useful, but it would break the scoping rules.
 
 /be
 

___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Brendan Eich

Allen Wirfs-Brock mailto:al...@wirfs-brock.com
January 31, 2012 8:43 AM
On Jan 31, 2012, at 8:36 AM, Brendan Eich wrote:

  Let's see how easy it is to split the 'var' and 'let/const' cases...


already done


In the latest draft on the wiki? I thought I had downloaded that already.

But you could make a twisted argument for an effect-ful 'let' initialization expression 
also being useful in this way. We mustn't go there.


might be useful, but it would break the scoping rules.


No, I meant this:

  let i = 42, j = 3;
  for (let x = i *= j in {});

No iterations, x not in scope after -- but i is 126 after. Do Not Want 
(the initialiser).


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Allen Wirfs-Brock

On Jan 31, 2012, at 10:09 AM, Brendan Eich wrote:

 Allen Wirfs-Brock mailto:al...@wirfs-brock.com
 January 31, 2012 8:43 AM
 On Jan 31, 2012, at 8:36 AM, Brendan Eich wrote:
  Let's see how easy it is to split the 'var' and 'let/const' cases...
 
 already done
 
 In the latest draft on the wiki? I thought I had downloaded that already.
 But you could make a twisted argument for an effect-ful 'let' 
 initialization expression also being useful in this way. We mustn't go 
 there.
 
 might be useful, but it would break the scoping rules.
 
 No, I meant this:
 
  let i = 42, j = 3;
  for (let x = i *= j in {});
 
 No iterations, x not in scope after -- but i is 126 after. Do Not Want (the 
 initialiser).

 for the same effec:

 let i = 42, j = 3;
 for (let x  in (i *= j ,{}));

Allen



___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Brendan Eich

Allen Wirfs-Brock wrote:

  No, I meant this:
  
let i = 42, j = 3;

for (let x = i *= j in {});
  
  No iterations, x not in scope after -- but i is 126 after. Do Not Want (the initialiser).


  for the same effec:

  let i = 42, j = 3;
  for (let x  in (i *= j ,{}));


So? I wrote effect not scope, now you're defending the unwanted 
degree of side-effecting freedom? :-|.


One can always make expressions have effects. That's not the point. The 
reuse of VariableDeclarationNoIn in 12.6.4 without any refactoring or 
semantic restriction to forbid an initialiser was a mistake. I'm glad to 
get rid of it, but teasing me will cause endless grumpy fear that it 
will live on. :-P


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Allen Wirfs-Brock

On Jan 31, 2012, at 10:25 AM, Brendan Eich wrote:

 Allen Wirfs-Brock wrote:
   No, I meant this:
   let i = 42, j = 3;
 for (let x = i *= j in {});
 No iterations, x not in scope after -- but i is 126 after. Do Not 
  Want (the initialiser).
 
  for the same effec:
 
  let i = 42, j = 3;
  for (let x  in (i *= j ,{}));
 
 So? I wrote effect not scope, now you're defending the unwanted degree of 
 side-effecting freedom? :-|.
 
 One can always make expressions have effects. That's not the point. The reuse 
 of VariableDeclarationNoIn in 12.6.4 without any refactoring or semantic 
 restriction to forbid an initialiser was a mistake. I'm glad to get rid of 
 it, but teasing me will cause endless grumpy fear that it will live on. :-P

Oh, I'm perfectly happy to see the initializer eliminated (for the new syntax).

But  side-effects eradication, in general,  seems like a wack-a-mole effort.

Allen
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Brendan Eich

Brendan Eich mailto:bren...@mozilla.org
January 31, 2012 10:25 AM


So? I wrote effect not scope, now you're defending the unwanted 
degree of side-effecting freedom? :-|.


One can always make expressions have effects. That's not the point. 
The reuse of VariableDeclarationNoIn in 12.6.4 without any refactoring 
or semantic restriction to forbid an initialiser was a mistake. I'm 
glad to get rid of it, but teasing me will cause endless grumpy fear 
that it will live on. :-P


So one approach, which Gavin has already tried in WebKit nightlies by 
reserving 'let' unconditionally (so far so good), is to risk breaking 
the web. How about we remove the optional initialiser hiding in for (var 
... in ...); and see what happens?


We may learn quickly that the web depends on this unwanted feature. That 
would be good, it would mean we must restrict 'let' and 'const' in 
for-in and for-of heads from having initialisers. But if we get away 
with it, over this year in nightlies and product builds, then I think we 
should remove the initialiser from the 'var' case too.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-31 Thread Brendan Eich

Allen Wirfs-Brock wrote:

Oh, I'm perfectly happy to see the initializer eliminated (for the new syntax).


See followup. If we can try to reserve 'let' in non-strict code, we can 
try to remove =i in for (var x=i in o). Why not make the attempt?



But  side-effects eradication, in general,  seems like a wack-a-mole effort.
Clearly I've miscommunicated. I wasn't arguing against effects, only 
against the extra expression option in a zero-iteration for-in 
structure. There's no reason for it, it introduces non-trivial 
complexity into some implementations, and it's a source of minor 
mischief for code analysis.


/be
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-30 Thread Brendan Eich
No! We have a standing agreement to get rid of the awful, 
lazy-grammar-reuse error in ES1 that allows an initializer in for(var 
x=i in o). We do not want this at all for 'let' in either for-in or for-of.


I think we should break unconditionally and forbid =i in for(var x=i in 
o) too, but that is a separate issue. This botch in grammar factoring is 
a (bad) sunk cost that has zero bearing on the fresh let binding per 
iteration idea. It's terrible anti-precedent. Just say no.


/be


Allen Wirfs-Brock mailto:al...@wirfs-brock.com
January 30, 2012 6:09 PM
Here is valid ES6 for-in statement:

for (let p=alert(initializing p+p) in [0,1]) alert(p);

Each iteration gets a fresh p, but does it produce three alerts saying:
Initializing p
0
1
or four alerts saying
Initializing p
0
Initializing p
1

I would expect the first alternative.

However, taking the first alternative, what happens for

let p=outer;
for (let p=alert(initializing p+p) in [0,1]) alert(p);

Is the first alert: initializing outer
or does it throw a reference error for a temporal dead zone violation?

I would expect the reference error.

Essentially the for-in is conceptually wrapped with a block that has 
an uninitialized binding for p. The initializer of the let declaration 
is evaluated once in the scope of that block and then the value is 
discarded because it is inaccessible. For each iterations, a fresh 
block is created containing a binding for p that is initialized to the 
current iteration key and then the statement is evaluated in the scope 
of that block.


Thoughts?

Allen


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss


Re: lexical for-in/for-of loose end

2012-01-30 Thread Erik Arvidsson
On Mon, Jan 30, 2012 at 18:38, Brendan Eich bren...@mozilla.org wrote:
 I think we should break unconditionally and forbid =i in for(var x=i in o)
 too, but that is a separate issue. This botch in grammar factoring is a
 (bad) sunk cost that has zero bearing on the fresh let binding per iteration
 idea. It's terrible anti-precedent. Just say no.

+1

No initializer in for-in with let.

I agree with Brendan, I think we can get rid of the initializer even
for for-in with var. The only code I've seen that uses this is in ES
test suites.

-- 
erik
___
es-discuss mailing list
es-discuss@mozilla.org
https://mail.mozilla.org/listinfo/es-discuss