Block Lambdas: break and continue

Brendan Eich brendan at
Sun Jan 15 23:16:12 PST 2012

> Allen Wirfs-Brock <mailto:allen at>
> 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) {


   for x {||}; y; z {

(as in Go, btw).

Proposed for-prefix followed by headless body:

   for x {||}; y; 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

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:indefinitelyiterate 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.

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: compose-unknown-contact.jpg
Type: image/jpeg
Size: 770 bytes
Desc: not available
URL: <>

More information about the es-discuss mailing list