January 15, 2012 2:31 PM

On Jan 15, 2012, at 11:56 AM, Brendan Eich wrote:
Thanks for writing this down. You're right, I was wrong: a new

for arr.forEach { |o|
  if (...) break;
}

special form would not require exception-reified break and continue given the desugaring you show.

I really like this because it lexically connects the loop label (explicit or implicit) with those referenced by break/continue within the associated block lambda. This eliminates all the dynamic scoping issues we had been grappling with on this thread.

Yeah, but why have it at all if users of functions such as forEach (canonical example, not only case) used with block-lambdas do not need break or continue much (or at all)?

(The "for" repeating after the keyword in the "forEach" name is unfortunate, but let's say we can migrate to a JQuery-like "each" name to resolve this glitch.)


While "for" may or may not be the best keyword, but you seem to be reading additional implications into it here and below in the context of arr.forEach.

No, come on -- I mean, seriously! I tried to make it as plain as possible by writing this parenthetical aside that this comment about forEach stuttering the proposed 'for' keyword prefix is not a big deal. It's not nothing either.

Yes, there are other use-case names than forEach. No, I do not agree that 'for' is the obvious best keyword prefix, for the reasons I gave: it's future-hostile and it does not define a loop with head and body.

This is a general constructor for labeling exit points from calls that take literal block lambdas as arguments.

Right, but if break and continue from "loops" such as arr.forEach are rare enough, why not use a real label for labeling the exit point?

 The "forEach" is just one of an indefinite number of function names that might be called with such a construct.  Some of those, may have names that clash with the initial keyword, what it is.

Whatever the method name, if you do not commit to a keyword, then the programmer can pick a *label* that does not stutter.

This is all a sideshow, and the parentheses I used should indicate. The big issues remain future-hostility and body-less head (or is it head-less body?).

Even with paren-free moving toward more significant newlines, the newline in the example does not terminate the loop "head", it is part of the block-lambda in the head. So this is future-hostile to paren-free.

I assume that the syntactic production we are de-sugaring is:
   IterationStatement :: for [no  ( here] _expression_ ;

The [no ( here] is to disambiguate from other for forms.  If we used a new contextual keyword it would be 
   IterationStatement :: EGloop [no  LineTerminator here] _expression_ ;

I don't see in either case, why you would have any paren-free problems as it is essentially just an _expression_ statement with a prefix keyword. In particular, there is no "loop head" and no "body".

I think you are forgetting the for(;;) loop. Paren-ful for loop:

  for (x {||}; y; z) {
    w;
  }

Paren-free:

  for x {||}; y; z {
    w;
  }

(as in Go, btw).

Proposed for-prefix followed by headless body:

  for x {||}; y; z {
    z;
  }

A syntax error if the left brace is as shown. But either move the { to the next line, or get rid of bracing and use significant newline to terminate the head, and we have:

  for x {||}; y; z
  {
    w;
  }

With the for-prefix proposed first by Axel, this is either (for x {||}); y; z; { w; } or for (x {||}; y; z) { w; }. There are two ways to parse a valid sentence. The grammar is ambiguous.

that is just an interpretation of the arguments for some specific use cases.  In fact, the functional abstraction may not be a loop at all. 

Also, things like the following
   for firstCall({|| ... break ...}).secondCall({||...break...}).thirdCall {||...break...};

are possible.  In these cases, the first and second breaks may be a bit unintuitive as they break out of the statement rather than the call that contains them.

Skipping the prefix magic and making the user, for this exceedingly rare hard case, use an explicit label, seems much better. Then there is no "may be a bit unintuitive" issue (which could be an understatement, if this case were not so rare that we cared!).
   
I wonder whether we need this use of "for". It's not clear the continue case arises enough with forEach to be worth it. The break case is already satisfied by some/every. If we can defer this sugar until it's clear we know the need for it is strong enough to measure, we ought to -- esp. given the paren-free conflict.

I don't understand the conflict,

See above.

but I agree we could do block lambdas while deferring extended break/continue semantics .  But this does seem like a neat approach.

I'm not slamming the door, but I do not want to lumber block-lambdas with more complexity if the hard case is as rare as I contend. Anyway "neat" is not enough. We need an unambiguous prefix syntax.

We could try for other syntax, but it too would have the body-less problem. Unless we use a new keyword:

loop arr.forEach { |o|
  if (...) break;
}


Even though these are not necessarily "loops", I think loop is still a plausible keyword.

"do" is even better, but obviously ambiguous with do-while loops. If you are going to write

  do arr.forEach { |o|
    if (...) break;
    ...
  } while (0);

then you might as well just use a label:

  L: arr.forEach { |o|
    if (...) break L;
    ...
  }

Or consider in as a prefix operator:

in arr.forEach { |o|
     if (...) break;
}

Horribly ambiguous, ES3 and up do not forbid line terminator to left of 'in' operator.

or

here: in arr.forEach { |o|
     elsewhere: while (true) {
      if (...) break here; else break elsewhere;
}

The labels mean you don't need "in" at all:

here: arr.forEach { |o|
     elsewhere: while (true) {
      if (...) break here; else break elsewhere;
}
 
What was the "in" for again? No "continue" usage here, which is more awkwardly translated to a break from inner labeled block as Grant showed. But continue is an even rarer hard case.

We could contextually reserve loop. But statements are part of JS, and so I suspect people would want this to do what Doug Crockford has suggested it to: indefinitely iterate its body (here the arr.forEach call _expression_ statement, which would have a semicolon inserted automatically after). This is a conflict, since forEach does its own looping.

I suspect that learning that loop does do what Doug suggested is less of a burden than leaning what {||...} means.

Did you mean "does not"?

"loop" is the wrong word if there is no loop, which you point out is possible -- but not plausible if we are discussing break and (especially) continue. I think we'd be nuts to use "loop" other than as Doug showed in that talk, but then I'd rather stick with for(;;) or while(true) for an iloop -- and so would most developers, since they have to do it today and it's not a big hardship.

Other ideas?

label: do (arr.forEach {|o|
   ...
   if (...) break label;
});

hate the extra ( ) needed to disambiguate other do usages.

I do! :-P

label: break {arr.forEach {|o|
   ...
   if (...) break label;
}};

extra { } not much better

I repeat if you are willing to use a label, you don't need a prefix keyword. See Grant's desugarings.

/be

_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss

Reply via email to