Block Lambdas: break and continue

Brendan Eich brendan at mozilla.org
Sat Jan 14 18:46:47 PST 2012


> Tab Atkins Jr. <mailto:jackalmage at gmail.com>
> January 14, 2012 5:01 PM
>
> It's likely no accident, but the direct Lisp equivalent to this (BLOCK
> and RETURN-FROM
> <http://www.lispworks.com/documentation/HyperSpec/Body/s_block.htm>)
> have proven useful to me many times when I needed to either continue
> or break from a macro-defined looping construct.

You: preacher; me: choir ;-).

>
> I support the author being able to declare their own capacity to
> escape from block-lambda-driven "loops" using this mechanism. I like
> it *much* better than attempting to discriminate somehow between
> lambda-driven "loops" and other lambda-driven constructs, or some
> lambda-driven constructs invisibly handling break/continue themselves.

Please be clear so we can throw stones or flowers appropriately: you 
mean by "attempting to discriminate" any reification of break and 
continue as exceptions?

> Plus, it's *very* often useful to escape multiple levels of loops,
> which is not solved by extending break/continue,

JS has had break/continue to label (a la Java) since ES3. (Why don't 
more people know about this? Have to talk to PR... :-)

> but *is* solved by
> return-to-label.

This is not a good argument by itself, given break/continue-to-label. JS 
long ago passed up the chance to generalize those special forms, so they 
are with us and used on the web.

Adding escape continuations is another case of warty + shiny ~~~> shiny 
promised land hope as the evolutionary game we have to play on the web. 
Still, I'm in favor of escape continuations but I do not want to bundle 
them with block-lambdas. One mountain to climb at a time.

/be
>
> ~TJ
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 2:05 PM
>> Herby Vojc(ík <mailto:herby at mailbox.sk>
>> January 14, 2012 1:46 PM
>> === Brendan Eich wrote ===
>> This doesn't address Herby's TCP-violating wish for a non-return that 
>> completes the block-lambda's control flow normally with a value (the 
>> message to which I was replying). But you're right that it wouldn't 
>> violate TCP either if we support it the same in a block statement as 
>> in a block-lambda downward funarg.
>> ===
>>
>> No. it wasn't my primary wish to have a local return.
>
> I understand, but (as you seem to concede below) if the TCP violation 
> this introduces is enough to kill it, I thought I'd argue against it 
> on that basis.
>
> Sorry for seeming to miss your larger point -- I am following it 
> closely too ;-).
>
>> I wanted to make break and/or continue _semantics_ for 
>> lambda-block-loop-like-constructs.
>
> Understood.
>
>> So since local return is already used for 'continue' in forEach, I 
>> just generalized the syntax continue |expr|; (note the | bars, they 
>> are part of the syntax)
>
> Oh! I didn't know that. Often we (certainly I do this, others too) use 
> bars in such sketches as meta not concrete syntax. Thanks for clarifying.
>
> Anyway, as others have written, this seems an abuse of 'continue'.
>
> Also, you cannot avoid the exception-like effect if this is to 
> propagate to the forEach's internal loop. There's no loop visible to 
> the spec or an implementation's compiler. So this really is a throwing 
> form. Again I do not see how it can be "exception-less".
>
>> to do the local return, thereby functioning as a continue statement. 
>> (It would be convenient to have local return, but not the local 
>> return itself was the goal).
>
> Local return violates TCP, so we should debate that vs. convenience if 
> you like. Sorry if that is not something you want to debate, but I 
> think you raised the issue directly and should respond.
>>
>> You are true it breaks TCP. (It could be done so that it does not by 
>> generalizing the syntax so it works in syntax-loop construct as well 
>> with "end loop body with expr as the completion value" semantics; 
>> it's only btw, it's too crazy to be considered, probably) So it 
>> cannot be used. :-/
>
> Agreed. But let's debate the exception-less claim anyway, to each 
> mutual understanding.
>>
>> By "this is de-facto continue" I was thinking more in higher semantic 
>> level - continue as in "continue past this block", which in loops 
>> means "to the next iteration" but beyond loops it may mean anything 
>> that is going to happen after block completes.
>
> The problem is the loop in Array forEach, or any such dynamically 
> dispatched caller of a block-lambda that is acting as a mapfun or 
> iterator, is hidden from static analysis.
>
> So such a de-facto continue (I see what you mean now; I mentioned 
> early return from forEach callback as continue myself) must throw. It 
> cannot be exception-free.
>
> Sorry to harp on this, I wonder if one of us is still misunderstanding 
> something.
>>
>> Also, break is hard to do similar way, because I count out 
>> (automatically set up) exceptions (I still live in the impression 
>> they are, performance-wise, expensive).
>
> Yes.
>
>> It seems to be possible to have "break |expr| label;" syntax working: 
>> if the function calling the lambda-block is labeled, it should be 
>> possible to just make it return the expr, but it is clumsy (and there 
>> is no label-less version).
>
> This reminds me of dherman's escape continuation proposal:
>
> http://wiki.ecmascript.org/doku.php?id=strawman:return_to_label
>
> We did not promote it from Strawman to Harmony status.
>>
>> Overall, I am a bit optimistic, since (if I am not mistaken) 
>> lambda-blocks only work inside its scope (as Self blocks, not as 
>> Smalltalk blocks), which saves a lot of complications.
>
> Block-lambdas can escape via the heap and be called later (so-called 
> async. callbacks). That is not a problem if they do not use return, 
> break, or continue. The TCP conformance for |this| is still a win. The 
> completion implicit return can be a win too.
>
> /be
>>
>> Herby
>
> [Finally trimming overcites!]
>
> /be
> Herby Vojc(ík <mailto:herby at mailbox.sk>
> January 14, 2012 1:46 PM
> === Brendan Eich wrote ===
> This doesn't address Herby's TCP-violating wish for a non-return that 
> completes the block-lambda's control flow normally with a value (the 
> message to which I was replying). But you're right that it wouldn't 
> violate TCP either if we support it the same in a block statement as 
> in a block-lambda downward funarg.
> ===
>
> No. it wasn't my primary wish to have a local return. I wanted to make 
> break and/or continue _semantics_ for lambda-block-loop-like-constructs.
> So since local return is already used for 'continue' in forEach, I 
> just generalized the syntax continue |expr|; (note the | bars, they 
> are part of the syntax) to do the local return, thereby functioning as 
> a continue statement. (It would be convenient to have local return, 
> but not the local return itself was the goal).
>
> You are true it breaks TCP. (It could be done so that it does not by 
> generalizing the syntax so it works in syntax-loop construct as well 
> with "end loop body with expr as the completion value" semantics; it's 
> only btw, it's too crazy to be considered, probably) So it cannot be 
> used. :-/
>
> By "this is de-facto continue" I was thinking more in higher semantic 
> level - continue as in "continue past this block", which in loops 
> means "to the next iteration" but beyond loops it may mean anything 
> that is going to happen after block completes.
>
> Also, break is hard to do similar way, because I count out 
> (automatically set up) exceptions (I still live in the impression they 
> are, performance-wise, expensive). It seems to be possible to have 
> "break |expr| label;" syntax working: if the function calling the 
> lambda-block is labeled, it should be possible to just make it return 
> the expr, but it is clumsy (and there is no label-less version). (This 
> "break |expr| label" may, too, be generalized if TCP is of concern; 
> again just BTW).
>
> Overall, I am a bit optimistic, since (if I am not mistaken) 
> lambda-blocks only work inside its scope (as Self blocks, not as 
> Smalltalk blocks), which saves a lot of complications.
>
> Herby
>
> -----Pôvodná správa----- From: Brendan Eich
> Sent: Saturday, January 14, 2012 10:16 PM
> To: François REMY
> Cc: Herby Vojc(ík ; es-discuss at mozilla.org
> Subject: Re: Block Lambdas: break and continue
>
>
>
> François REMY
> January 14, 2012 1:01 PM
> If we want to avoid to break TCP, we can go with "throw break;" and 
> "throw continue;".
>
> This doesn't address Herby's TCP-violating wish for a non-return that 
> completes the block-lambda's control flow normally with a value (the 
> message to which I was replying). But you're right that it wouldn't 
> violate TCP either if we support it the same in a block statement as 
> in a block-lambda downward funarg.
>
>
> It would throw a new BreakException or a new ContinueException, from 
> the place where they are executed. If it's outside a block lambda, 
> it's outside a block lambda. It doesn't matter.
>
> Yes, this would avoid TCP violations but not carry a return value -- 
> Herby's wish.
>
>
> But it would set a "standard" for breaking throug 'function loops'.
>
> I considered this in drafting the block-lambda revival strawman. Other 
> languages have gone here. Nevertheless, I would like to leave it out 
> (remember N. Wirth on language design). It adds more complexity for a 
> use-case that I bet is rare (in any case it needs credible 
> demonstration of being quite common).
>
> The complexity in the semantics is one issue Dave raised. This 
> corresponds to complexity for optimizing engines, compared to the 
> purely static break/continue semantics in the strawman.
>
> Finally, the Array extras ship sailed. People already have to use some 
> or every in lieu of a break-from-forEach. Using a function callback 
> with forEach, one needs only to return to simulate continue. Now if we 
> do standardize block-lambdas and throw break or throw continue, we 
> certainly can elaborate the extras to catch these exceptions.
>
> Such a more complex design seems workable with the costs noted above. 
> But will the benefits really outweigh those costs? I doubt it. First, 
> Array forEach and other uses will continue to use functions for quite 
> a while, or else a compiler from new standard JS to old. In the 
> compiler case, throw and try/catch will be required, and the compiler 
> will have to monkey-patch the extras to deal with the new exceptions. 
> This will be a performance killer, and no fun to debug.
>
> So my thinking remains that we are better off, when in doubt, leaving 
> reified break and continue exceptions "out".
>
> /be
>
>
> François
>
> From: Brendan Eich
> Sent: Saturday, January 14, 2012 9:51 PM
> To: Herby Vojc(ík
> Cc: es-discuss at mozilla.org
> Subject: Re: Block Lambdas: break and continue
>
>
>
> Herby Vojc(ík
> January 14, 2012 10:42 AM
> === David Herman wrote ===
> This *may* not violate TCP (I'm not quite sure), but I'm not 
> enthusiastic about the idea. The semantics is significantly more 
> complicated, and it requires you to understand whether a higher-order 
> function like forEach is catching these exceptions or not. So it 
> becomes an additional part of the API of a function. If someone 
> doesn't document what they do with BreakException and 
> ContinueException, then writing callbacks you won't actually be able 
> to predict what `break` and `continue` will do.
> ===
>
> What about the exception-less suggestion I put in? It should work in 
> any loop construct with lambda-block, even if you must know a little 
> about the loop implementation itself. That is, to be able to put:
>
>   continue |expression|;
>
> Who says the block-lambda is being called from a loop at all? Why 
> should use-cases that want an early result and completion have to use 
> continue, which is for loops?
>
> Worse, this violates TCP. Now you copy and paste this block-lambda 
> code back into a block statement to refactor the other direction, and 
> no such "here is the completion value, do not flow past this point in 
> the block" semantics obtain.
>
>
> as a statement in lambda block which instructs the lambda-block itself 
> (not the outer function) to return the expression? This is the 
> de-facto continue semantics (lambda-block, do return a value and the 
> enclosing loop will continue to the next iteration (possibly stopping 
> the loop if it chooses not to have more iterations)).
>
> No it's not. There is no de-facto continue semantics for block-lambdas 
> because they haven't been prototyped. For block statements, no such 
> continue semantics exists.
>
>
> It is not possible to enforce break in the same manner, but for 
> continue, it is possible.
>
> It's possible to abuse any existing keyword, but first: why must there 
> be a new TCP violation? Block-lambda bodies are often expressions, or 
> if statements, then short/functional-style statements, not large 
> bodies demonstrating early-normal-completion opportunities.
>
> We should not eliminate TCP violations only to add new ones, 
> especially without any evidence they're needed and pay their way. 
> Otherwise we'll get an infinite regress of 
> TCP-pure-then-add-new-exceptions-and-repeat additions.
>
> /be
>
>
> Herby
>
> -----Pôvodná správa----- From: David Herman
> Sent: Saturday, January 14, 2012 6:12 PM
> To: Axel Rauschmayer
> Cc: Brendan Eich ; es-discuss at mozilla.org
> Subject: Re: Block Lambdas: break and continue
>
> On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:
>
>
> If I understand your suggestion, you're proposing that non-local break 
> and continue should be exposed as standard exceptions, and then 
> implementors of loop-like abstractions could choose to catch them. 
> E.g. you could implement forEach as:
>
>   Array.prototype.forEach = function(f) {
>       for (let i = 0, n = this.length; i < n; i++) {
>           try {
>               f.call(this, this[i], i);
>           } catch (e) {
>               if (e instanceof BreakException)
>                   break;
>               else if (e instanceof ContinueException)
>                   continue;
>               else
>                   throw e;
>           }
>       }
>   };
>
> Whereas a function that does *not* want to expose whether it's using 
> loops would simply do nothing with BreakException and 
> ContinueException, and they would propagate out and you'd get the 
> lexical scoping semantics. Meanwhile, break/continue with an explicit 
> target would never be catch-able.
>
> Did I understand your suggestion correctly?
>
> This *may* not violate TCP (I'm not quite sure), but I'm not 
> enthusiastic about the idea. The semantics is significantly more 
> complicated, and it requires you to understand whether a higher-order 
> function like forEach is catching these exceptions or not. So it 
> becomes an additional part of the API of a function. If someone 
> doesn't document what they do with BreakException and 
> ContinueException, then writing callbacks you won't actually be able 
> to predict what `break` and `continue` will do.
>
> Dave
>
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
>
> David Herman
> January 14, 2012 9:12 AM
>
> If I understand your suggestion, you're proposing that non-local break 
> and continue should be exposed as standard exceptions, and then 
> implementors of loop-like abstractions could choose to catch them. 
> E.g. you could implement forEach as:
>
> Array.prototype.forEach = function(f) {
> for (let i = 0, n = this.length; i < n; i++) {
> try {
> f.call(this, this[i], i);
> } catch (e) {
> if (e instanceof BreakException)
> break;
> else if (e instanceof ContinueException)
> continue;
> else
> throw e;
> }
> }
> };
>
> Whereas a function that does *not* want to expose whether it's using 
> loops would simply do nothing with BreakException and 
> ContinueException, and they would propagate out and you'd get the 
> lexical scoping semantics. Meanwhile, break/continue with an explicit 
> target would never be catch-able.
>
> Did I understand your suggestion correctly?
>
> This *may* not violate TCP (I'm not quite sure), but I'm not 
> enthusiastic about the idea. The semantics is significantly more 
> complicated, and it requires you to understand whether a higher-order 
> function like forEach is catching these exceptions or not. So it 
> becomes an additional part of the API of a function. If someone 
> doesn't document what they do with BreakException and 
> ContinueException, then writing callbacks you won't actually be able 
> to predict what `break` and `continue` will do.
>
> Dave
>
>
>
> Axel Rauschmayer
> January 13, 2012 9:04 PM
> I think it's a valid concern. The idea is: If I can implement my own 
> loops (the nice-looking paren-free syntax feeds that illusion!) then I 
> also want those loops to have break and continue. You could statically 
> determine what construct, say, a break applies to and either throw a 
> BreakException (if it applies to a lambda) or TCP-break (if it applies 
> to an enclosing non-lambda loop). In the examples below, when I see a 
> continue, I look for the innermost enclosing loop braces and the ones 
> belong to list[i].forEach are definitely candidates.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 1:16 PM
>> François REMY <mailto:fremycompany_pub at yahoo.fr>
>> January 14, 2012 1:01 PM
>> If we want to avoid to break TCP, we can go with "throw break;" and 
>> "throw continue;".
>
> This doesn't address Herby's TCP-violating wish for a non-return that  
> completes the block-lambda's control flow normally with a value (the 
> message to which I was replying). But you're right that it wouldn't 
> violate TCP either if we support it the same in a block statement as 
> in a block-lambda downward funarg.
>
>> It would throw a new BreakException or a new ContinueException, from 
>> the place where they are executed. If it's outside a block lambda, 
>> it's outside a block lambda. It doesn't matter.
>
> Yes, this would avoid TCP violations but not carry a return value -- 
> Herby's wish.
>> But it would set a "standard" for breaking throug 'function loops'.
>
> I considered this in drafting the block-lambda revival strawman. Other 
> languages have gone here. Nevertheless, I would like to leave it out 
> (remember N. Wirth on language design). It adds more complexity for a 
> use-case that I bet is rare (in any case it needs credible 
> demonstration of being quite common).
>
> The complexity in the semantics is one issue Dave raised. This 
> corresponds to complexity for optimizing engines, compared to the 
> purely static break/continue semantics in the strawman.
>
> Finally, the Array extras ship sailed. People already have to use some 
> or every in lieu of a break-from-forEach. Using a function callback 
> with forEach, one needs only to return to simulate continue. Now if we 
> do standardize block-lambdas and throw break or throw continue, we 
> certainly can elaborate the extras to catch these exceptions.
>
> Such a more complex design seems workable with the costs noted above. 
> But will the benefits really outweigh those costs? I doubt it. First, 
> Array forEach and other uses will continue to use functions for quite 
> a while, or else a compiler from new standard JS to old. In the 
> compiler case, throw and try/catch will be required, and the compiler 
> will have to monkey-patch the extras to deal with the new exceptions. 
> This will be a performance killer, and no fun to debug.
>
> So my thinking remains that we are better off, when in doubt, leaving 
> reified break and continue exceptions "out".
>
> /be
>
>> François
>> *From:* Brendan Eich <mailto:brendan at mozilla.org>
>> *Sent:* Saturday, January 14, 2012 9:51 PM
>> *To:* Herby Vojc(ík <mailto:herby at mailbox.sk>
>> *Cc:* es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
>> *Subject:* Re: Block Lambdas: break and continue
>>> Herby Vojc(ík <mailto:herby at mailbox.sk>
>>> January 14, 2012 10:42 AM
>>> === David Herman wrote ===
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>> ===
>>>
>>> What about the exception-less suggestion I put in? It should work in 
>>> any loop construct with lambda-block, even if you must know a little 
>>> about the loop implementation itself. That is, to be able to put:
>>>
>>>    continue |expression|;
>>
>> Who says the block-lambda is being called from a loop at all? Why 
>> should use-cases that want an early result and completion have to use 
>> continue, which is for loops?
>>
>> Worse, this violates TCP. Now you copy and paste this block-lambda 
>> code back into a block statement to refactor the other direction, and 
>> no such "here is the completion value, do not flow past this point in 
>> the block" semantics obtain.
>>>
>>> as a statement in lambda block which instructs the lambda-block 
>>> itself (not the outer function) to return the expression? This is 
>>> the de-facto continue semantics (lambda-block, do return a value and 
>>> the enclosing loop will continue to the next iteration (possibly 
>>> stopping the loop if it chooses not to have more iterations)).
>>
>> No it's not. There is no de-facto continue semantics for 
>> block-lambdas because they haven't been prototyped. For block 
>> statements, no such continue semantics exists.
>>
>>> It is not possible to enforce break in the same manner, but for 
>>> continue, it is possible.
>>
>> It's possible to abuse any existing keyword, but first: why must 
>> there be a new TCP violation? Block-lambda bodies are often 
>> expressions, or if statements, then short/functional-style 
>> statements, not large bodies demonstrating early-normal-completion 
>> opportunities.
>>
>> We should not eliminate TCP violations only to add new ones, 
>> especially without any evidence they're needed and pay their way. 
>> Otherwise we'll get an infinite regress of 
>> TCP-pure-then-add-new-exceptions-and-repeat additions.
>>
>> /be
>>>
>>> Herby
>>>
>>> -----Pôvodná správa----- From: David Herman
>>> Sent: Saturday, January 14, 2012 6:12 PM
>>> To: Axel Rauschmayer
>>> Cc: Brendan Eich ; es-discuss at mozilla.org
>>> Subject: Re: Block Lambdas: break and continue
>>>
>>> On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:
>>>
>>>
>>> If I understand your suggestion, you're proposing that non-local 
>>> break and continue should be exposed as standard exceptions, and 
>>> then implementors of loop-like abstractions could choose to catch 
>>> them. E.g. you could implement forEach as:
>>>
>>>    Array.prototype.forEach = function(f) {
>>>        for (let i = 0, n = this.length; i < n; i++) {
>>>            try {
>>>                f.call(this, this[i], i);
>>>            } catch (e) {
>>>                if (e instanceof BreakException)
>>>                    break;
>>>                else if (e instanceof ContinueException)
>>>                    continue;
>>>                else
>>>                    throw e;
>>>            }
>>>        }
>>>    };
>>>
>>> Whereas a function that does *not* want to expose whether it's using 
>>> loops would simply do nothing with BreakException and 
>>> ContinueException, and they would propagate out and you'd get the 
>>> lexical scoping semantics. Meanwhile, break/continue with an 
>>> explicit target would never be catch-able.
>>>
>>> Did I understand your suggestion correctly?
>>>
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>>
>>> Dave
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> David Herman <mailto:dherman at mozilla.com>
>>> January 14, 2012 9:12 AM
>>>
>>> If I understand your suggestion, you're proposing that non-local 
>>> break and continue should be exposed as standard exceptions, and 
>>> then implementors of loop-like abstractions could choose to catch 
>>> them. E.g. you could implement forEach as:
>>>
>>> Array.prototype.forEach = function(f) {
>>> for (let i = 0, n = this.length; i < n; i++) {
>>> try {
>>> f.call(this, this[i], i);
>>> } catch (e) {
>>> if (e instanceof BreakException)
>>> break;
>>> else if (e instanceof ContinueException)
>>> continue;
>>> else
>>> throw e;
>>> }
>>> }
>>> };
>>>
>>> Whereas a function that does *not* want to expose whether it's using 
>>> loops would simply do nothing with BreakException and 
>>> ContinueException, and they would propagate out and you'd get the 
>>> lexical scoping semantics. Meanwhile, break/continue with an 
>>> explicit target would never be catch-able.
>>>
>>> Did I understand your suggestion correctly?
>>>
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>>
>>> Dave
>>>
>>>
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 13, 2012 9:04 PM
>>> I think it's a valid concern. The idea is: If I can implement my own 
>>> loops (the nice-looking paren-free syntax feeds that illusion!) then 
>>> I also want those loops to have break and continue. You could 
>>> statically determine what construct, say, a break applies to and 
>>> either throw a BreakException (if it applies to a lambda) or 
>>> TCP-break (if it applies to an enclosing non-lambda loop). In the 
>>> examples below, when I see a continue, I look for the innermost 
>>> enclosing loop braces and the ones belong to list[i].forEach are 
>>> definitely candidates.
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 13, 2012 8:54 PM
>>>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>>>> January 13, 2012 7:29 PM
>>>> Block lambdas have been a hot topic, recently, but there's a point 
>>>> of significant divergence between Ruby (which appears to be the 
>>>> inspiration)
>>>
>>> Not Ruby alone, and not in any chauvinist my-language-is-better 
>>> sense. Smalltalk is the original inspiration for Ruby blocks, and 
>>> the correspondence principle has deep roots.
>>>
>>>> and the proposed solution, in the handling of continue (called 
>>>> 'next', in Ruby) and 'break'.
>>>> To whit: In Ruby, 'next' will end the current run (iteration) of 
>>>> the block, and 'break' will (somehow) terminate the method 
>>>> lexically connected with the block. It can be claimed that this is 
>>>> more intuitive than the current proposal, which aims to make 
>>>> 'break' and 'continue' propagate through block lambdas in the same 
>>>> way 'return' would.
>>>
>>> "Intuitive" depends on intuition, which is not well-defined. Do you 
>>> mean a Rubyist might expect different behavior for break? That is 
>>> possible but JS ain't Ruby and break should not change to do 
>>> something like what it does in Ruby (and we aren't defining a next 
>>> equivalent for JS).
>>>
>>>> Ruby does also support syntactic loops and the same keywords 
>>>> therein and so directly violates Tennent's Correspondence 
>>>> Principle, even though such has been touted as a core reason for 
>>>> the construct. Instead, I believe it reasonable to invoke intuition 
>>>> in this matter. It is intuitive for 'return' to return a value from 
>>>> the lexically enclosing method and it is intuitive for 'continue' 
>>>> to commence the next iteration of the current loop,
>>>
>>> Wait, why do you think break and continue without label operands do 
>>> anything other than break from the nearest enclosing loop (or switch 
>>> or labeled statement if break), or continue the nearest enclosing 
>>> loop? The proposal specifies this.
>>>
>>> function find_odds_in_arrays(list,        // array of arrays
>>>                              skip)        // if found, skip rest
>>> {
>>>   let a = [];
>>>   for (let i = 0; i < list.length; i++) {
>>>     list[i].forEach {
>>>       |e|
>>>       if (e === skip) {
>>>         continue;                         // continue the for loop
>>>       }
>>>       if (e & 1) {
>>>         a.push(e);
>>>       }
>>>     }
>>>   }
>>>   return a;
>>> }
>>>
>>> function find_more_odds(list, stop) {
>>>   let a = [];
>>>   for (let i = 0; i < list.length; i++) {
>>>     list[i].forEach {
>>>       |e|
>>>       if (e === stop) {
>>>         break;                      // break from the for loop
>>>       }
>>>       if (e & 1) {
>>>         a.push(e);
>>>       }
>>>     }
>>>   }
>>>   return a;
>>> }
>>>
>>>> however that loop is constructed.
>>>
>>> What do you mean by this? The spec talks about nearest enclosing 
>>> loop or relevant control structure in the source code. Are you 
>>> talking about internal loops in implementations (dynamically 
>>> dispatched at that) of methods that take block-lambdas as arguments? 
>>> I.e.
>>>
>>>
>>> function find_first_odd(a) {
>>>   a.forEach { |e, i|
>>>               if (e & 1) return i; }  // returns from function
>>>   return -1;
>>> }
>>>
>>>
>>> The Array.prototype.forEach method's internal implementation is its 
>>> business, and a break instead of the return would be a static error 
>>> in this example. It would not be a dynamic throw-like construct that 
>>> is caught by forEach's implementation.
>>>
>>> /be
>>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>>> January 13, 2012 7:29 PM
>>> Block lambdas have been a hot topic, recently, but there's a point 
>>> of significant divergence between Ruby (which appears to be the 
>>> inspiration) and the proposed solution, in the handling of continue 
>>> (called 'next', in Ruby) and 'break'.
>>> To whit: In Ruby, 'next' will end the current run (iteration) of the 
>>> block, and 'break' will (somehow) terminate the method lexically 
>>> connected with the block. It can be claimed that this is more 
>>> intuitive than the current proposal, which aims to make 'break' and 
>>> 'continue' propagate through block lambdas in the same way 'return' 
>>> would.
>>> Ruby does also support syntactic loops and the same keywords therein 
>>> and so directly violates Tennent's Correspondence Principle, even 
>>> though such has been touted as a core reason for the construct. 
>>> Instead, I believe it reasonable to invoke intuition in this matter. 
>>> It is intuitive for 'return' to return a value from the lexically 
>>> enclosing method and it is intuitive for 'continue' to commence the 
>>> next iteration of the current loop, however that loop is constructed.
>>> Note that the label-based break/continue could still have the 
>>> desired effect, if the proposal was updated to be more like Ruby's 
>>> blocks.
>>> I don't have a strong opinion on the subject, but I hadn't noticed 
>>> the above being discussed, elsewhere, and thought it worth raising. 
>>> If there is a better place for me to raise this, please let me know 
>>> where and accept my apologies.
>>> Regards,
>>> Grant Husbands.
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> ------------------------------------------------------------------------
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 14, 2012 12:51 PM
>>> Herby Vojc(ík <mailto:herby at mailbox.sk>
>>> January 14, 2012 10:42 AM
>>> === David Herman wrote ===
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>> ===
>>>
>>> What about the exception-less suggestion I put in? It should work in 
>>> any loop construct with lambda-block, even if you must know a little 
>>> about the loop implementation itself. That is, to be able to put:
>>>
>>>    continue |expression|;
>>
>> Who says the block-lambda is being called from a loop at all? Why 
>> should use-cases that want an early result and completion have to use 
>> continue, which is for loops?
>>
>> Worse, this violates TCP. Now you copy and paste this block-lambda 
>> code back into a block statement to refactor the other direction, and 
>> no such "here is the completion value, do not flow past this point in 
>> the block" semantics obtain.
>>>
>>> as a statement in lambda block which instructs the lambda-block 
>>> itself (not the outer function) to return the expression? This is 
>>> the de-facto continue semantics (lambda-block, do return a value and 
>>> the enclosing loop will continue to the next iteration (possibly 
>>> stopping the loop if it chooses not to have more iterations)).
>>
>> No it's not. There is no de-facto continue semantics for 
>> block-lambdas because they haven't been prototyped. For block 
>> statements, no such continue semantics exists.
>>
>>> It is not possible to enforce break in the same manner, but for 
>>> continue, it is possible.
>>
>> It's possible to abuse any existing keyword, but first: why must 
>> there be a new TCP violation? Block-lambda bodies are often 
>> expressions, or if statements, then short/functional-style 
>> statements, not large bodies demonstrating early-normal-completion 
>> opportunities.
>>
>> We should not eliminate TCP violations only to add new ones, 
>> especially without any evidence they're needed and pay their way. 
>> Otherwise we'll get an infinite regress of 
>> TCP-pure-then-add-new-exceptions-and-repeat additions.
>>
>> /be
>>>
>>> Herby
>>>
>>> -----Pôvodná správa----- From: David Herman
>>> Sent: Saturday, January 14, 2012 6:12 PM
>>> To: Axel Rauschmayer
>>> Cc: Brendan Eich ; es-discuss at mozilla.org
>>> Subject: Re: Block Lambdas: break and continue
>>>
>>> On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:
>>>
>>>
>>> If I understand your suggestion, you're proposing that non-local 
>>> break and continue should be exposed as standard exceptions, and 
>>> then implementors of loop-like abstractions could choose to catch 
>>> them. E.g. you could implement forEach as:
>>>
>>>    Array.prototype.forEach = function(f) {
>>>        for (let i = 0, n = this.length; i < n; i++) {
>>>            try {
>>>                f.call(this, this[i], i);
>>>            } catch (e) {
>>>                if (e instanceof BreakException)
>>>                    break;
>>>                else if (e instanceof ContinueException)
>>>                    continue;
>>>                else
>>>                    throw e;
>>>            }
>>>        }
>>>    };
>>>
>>> Whereas a function that does *not* want to expose whether it's using 
>>> loops would simply do nothing with BreakException and 
>>> ContinueException, and they would propagate out and you'd get the 
>>> lexical scoping semantics. Meanwhile, break/continue with an 
>>> explicit target would never be catch-able.
>>>
>>> Did I understand your suggestion correctly?
>>>
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>>
>>> Dave
>>>
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>>> David Herman <mailto:dherman at mozilla.com>
>>> January 14, 2012 9:12 AM
>>>
>>> If I understand your suggestion, you're proposing that non-local 
>>> break and continue should be exposed as standard exceptions, and 
>>> then implementors of loop-like abstractions could choose to catch 
>>> them. E.g. you could implement forEach as:
>>>
>>> Array.prototype.forEach = function(f) {
>>> for (let i = 0, n = this.length; i < n; i++) {
>>> try {
>>> f.call(this, this[i], i);
>>> } catch (e) {
>>> if (e instanceof BreakException)
>>> break;
>>> else if (e instanceof ContinueException)
>>> continue;
>>> else
>>> throw e;
>>> }
>>> }
>>> };
>>>
>>> Whereas a function that does *not* want to expose whether it's using 
>>> loops would simply do nothing with BreakException and 
>>> ContinueException, and they would propagate out and you'd get the 
>>> lexical scoping semantics. Meanwhile, break/continue with an 
>>> explicit target would never be catch-able.
>>>
>>> Did I understand your suggestion correctly?
>>>
>>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>>> enthusiastic about the idea. The semantics is significantly more 
>>> complicated, and it requires you to understand whether a 
>>> higher-order function like forEach is catching these exceptions or 
>>> not. So it becomes an additional part of the API of a function. If 
>>> someone doesn't document what they do with BreakException and 
>>> ContinueException, then writing callbacks you won't actually be able 
>>> to predict what `break` and `continue` will do.
>>>
>>> Dave
>>>
>>>
>>> Axel Rauschmayer <mailto:axel at rauschma.de>
>>> January 13, 2012 9:04 PM
>>> I think it's a valid concern. The idea is: If I can implement my own 
>>> loops (the nice-looking paren-free syntax feeds that illusion!) then 
>>> I also want those loops to have break and continue. You could 
>>> statically determine what construct, say, a break applies to and 
>>> either throw a BreakException (if it applies to a lambda) or 
>>> TCP-break (if it applies to an enclosing non-lambda loop). In the 
>>> examples below, when I see a continue, I look for the innermost 
>>> enclosing loop braces and the ones belong to list[i].forEach are 
>>> definitely candidates.
>>>
>>>
>>>
>>> -- 
>>> Dr. Axel Rauschmayer
>>> axel at rauschma.de <mailto:axel at rauschma.de>
>>>
>>> home: rauschma.de <http://rauschma.de>
>>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>>> blog: 2ality.com <http://2ality.com>
>>>
>>> Brendan Eich <mailto:brendan at mozilla.org>
>>> January 13, 2012 8:54 PM
>>>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>>>> January 13, 2012 7:29 PM
>>>> Block lambdas have been a hot topic, recently, but there's a point 
>>>> of significant divergence between Ruby (which appears to be the 
>>>> inspiration)
>>>
>>> Not Ruby alone, and not in any chauvinist my-language-is-better 
>>> sense. Smalltalk is the original inspiration for Ruby blocks, and 
>>> the correspondence principle has deep roots.
>>>
>>>> and the proposed solution, in the handling of continue (called 
>>>> 'next', in Ruby) and 'break'.
>>>>
>>>> To whit: In Ruby, 'next' will end the current run (iteration) of 
>>>> the block, and 'break' will (somehow) terminate the method 
>>>> lexically connected with the block. It can be claimed that this is 
>>>> more intuitive than the current proposal, which aims to make 
>>>> 'break' and 'continue' propagate through block lambdas in the same 
>>>> way 'return' would.
>>>
>>> "Intuitive" depends on intuition, which is not well-defined. Do you 
>>> mean a Rubyist might expect different behavior for break? That is 
>>> possible but JS ain't Ruby and break should not change to do 
>>> something like what it does in Ruby (and we aren't defining a next 
>>> equivalent for JS).
>>>
>>>> Ruby does also support syntactic loops and the same keywords 
>>>> therein and so directly violates Tennent's Correspondence 
>>>> Principle, even though such has been touted as a core reason for 
>>>> the construct. Instead, I believe it reasonable to invoke intuition 
>>>> in this matter. It is intuitive for 'return' to return a value from 
>>>> the lexically enclosing method and it is intuitive for 'continue' 
>>>> to commence the next iteration of the current loop,
>>>
>>> Wait, why do you think break and continue without label operands do 
>>> anything other than break from the nearest enclosing loop (or switch 
>>> or labeled statement if break), or continue the nearest enclosing 
>>> loop? The proposal specifies this.
>>>
>>> function find_odds_in_arrays(list,        // array of arrays
>>>                              skip)        // if found, skip rest
>>> {
>>>   let a = [];
>>>   for (let i = 0; i < list.length; i++) {
>>>     list[i].forEach {
>>>       |e|
>>>       if (e === skip) {
>>>         continue;                         // continue the for loop
>>>       }
>>>       if (e & 1) {
>>>         a.push(e);
>>>       }
>>>     }
>>>   }
>>>   return a;
>>> }
>>>
>>> function find_more_odds(list, stop) {
>>>   let a = [];
>>>   for (let i = 0; i < list.length; i++) {
>>>     list[i].forEach {
>>>       |e|
>>>       if (e === stop) {
>>>         break;                      // break from the for loop
>>>       }
>>>       if (e & 1) {
>>>         a.push(e);
>>>       }
>>>     }
>>>   }
>>>   return a;
>>> }
>>>
>>>> however that loop is constructed.
>>>
>>> What do you mean by this? The spec talks about nearest enclosing 
>>> loop or relevant control structure in the source code. Are you 
>>> talking about internal loops in implementations (dynamically 
>>> dispatched at that) of methods that take block-lambdas as arguments? 
>>> I.e.
>>>
>>>
>>> function find_first_odd(a) {
>>>   a.forEach { |e, i|
>>>               if (e & 1) return i; }  // returns from function
>>>   return -1;
>>> }
>>>
>>>
>>> The Array.prototype.forEach method's internal implementation is its 
>>> business, and a break instead of the return would be a static error 
>>> in this example. It would not be a dynamic throw-like construct that 
>>> is caught by forEach's implementation.
>>>
>>> /be
>>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>>> January 13, 2012 7:29 PM
>>> Block lambdas have been a hot topic, recently, but there's a point 
>>> of significant divergence between Ruby (which appears to be the 
>>> inspiration) and the proposed solution, in the handling of continue 
>>> (called 'next', in Ruby) and 'break'.
>>>
>>> To whit: In Ruby, 'next' will end the current run (iteration) of the 
>>> block, and 'break' will (somehow) terminate the method lexically 
>>> connected with the block. It can be claimed that this is more 
>>> intuitive than the current proposal, which aims to make 'break' and 
>>> 'continue' propagate through block lambdas in the same way 'return' 
>>> would.
>>>
>>> Ruby does also support syntactic loops and the same keywords therein 
>>> and so directly violates Tennent's Correspondence Principle, even 
>>> though such has been touted as a core reason for the construct. 
>>> Instead, I believe it reasonable to invoke intuition in this matter. 
>>> It is intuitive for 'return' to return a value from the lexically 
>>> enclosing method and it is intuitive for 'continue' to commence the 
>>> next iteration of the current loop, however that loop is constructed.
>>>
>>> Note that the label-based break/continue could still have the 
>>> desired effect, if the proposal was updated to be more like Ruby's 
>>> blocks.
>>>
>>> I don't have a strong opinion on the subject, but I hadn't noticed 
>>> the above being discussed, elsewhere, and thought it worth raising. 
>>> If there is a better place for me to raise this, please let me know 
>>> where and accept my apologies.
>>>
>>> Regards,
>>> Grant Husbands.
>>> _______________________________________________
>>> es-discuss mailing list
>>> es-discuss at mozilla.org
>>> https://mail.mozilla.org/listinfo/es-discuss
>> Herby Vojc(ík <mailto:herby at mailbox.sk>
>> January 14, 2012 10:42 AM
>> === David Herman wrote ===
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>> ===
>>
>> What about the exception-less suggestion I put in? It should work in 
>> any loop construct with lambda-block, even if you must know a little 
>> about the loop implementation itself. That is, to be able to put:
>>
>>    continue |expression|;
>>
>> as a statement in lambda block which instructs the lambda-block 
>> itself (not the outer function) to return the expression? This is the 
>> de-facto continue semantics (lambda-block, do return a value and the 
>> enclosing loop will continue to the next iteration (possibly stopping 
>> the loop if it chooses not to have more iterations)). It is not 
>> possible to enforce break in the same manner, but for continue, it is 
>> possible.
>>
>> Herby
>>
>> -----Pôvodná správa----- From: David Herman
>> Sent: Saturday, January 14, 2012 6:12 PM
>> To: Axel Rauschmayer
>> Cc: Brendan Eich ; es-discuss at mozilla.org
>> Subject: Re: Block Lambdas: break and continue
>>
>> On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:
>>
>>
>> If I understand your suggestion, you're proposing that non-local 
>> break and continue should be exposed as standard exceptions, and then 
>> implementors of loop-like abstractions could choose to catch them. 
>> E.g. you could implement forEach as:
>>
>>    Array.prototype.forEach = function(f) {
>>        for (let i = 0, n = this.length; i < n; i++) {
>>            try {
>>                f.call(this, this[i], i);
>>            } catch (e) {
>>                if (e instanceof BreakException)
>>                    break;
>>                else if (e instanceof ContinueException)
>>                    continue;
>>                else
>>                    throw e;
>>            }
>>        }
>>    };
>>
>> Whereas a function that does *not* want to expose whether it's using 
>> loops would simply do nothing with BreakException and 
>> ContinueException, and they would propagate out and you'd get the 
>> lexical scoping semantics. Meanwhile, break/continue with an explicit 
>> target would never be catch-able.
>>
>> Did I understand your suggestion correctly?
>>
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>>
>> Dave
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>>
>> David Herman <mailto:dherman at mozilla.com>
>> January 14, 2012 9:12 AM
>>
>> If I understand your suggestion, you're proposing that non-local 
>> break and continue should be exposed as standard exceptions, and then 
>> implementors of loop-like abstractions could choose to catch them. 
>> E.g. you could implement forEach as:
>>
>> Array.prototype.forEach = function(f) {
>> for (let i = 0, n = this.length; i < n; i++) {
>> try {
>> f.call(this, this[i], i);
>> } catch (e) {
>> if (e instanceof BreakException)
>> break;
>> else if (e instanceof ContinueException)
>> continue;
>> else
>> throw e;
>> }
>> }
>> };
>>
>> Whereas a function that does *not* want to expose whether it's using 
>> loops would simply do nothing with BreakException and 
>> ContinueException, and they would propagate out and you'd get the 
>> lexical scoping semantics. Meanwhile, break/continue with an explicit 
>> target would never be catch-able.
>>
>> Did I understand your suggestion correctly?
>>
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>>
>> Dave
>>
>>
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 13, 2012 9:04 PM
>> I think it's a valid concern. The idea is: If I can implement my own 
>> loops (the nice-looking paren-free syntax feeds that illusion!) then 
>> I also want those loops to have break and continue. You could 
>> statically determine what construct, say, a break applies to and 
>> either throw a BreakException (if it applies to a lambda) or 
>> TCP-break (if it applies to an enclosing non-lambda loop). In the 
>> examples below, when I see a continue, I look for the innermost 
>> enclosing loop braces and the ones belong to list[i].forEach are 
>> definitely candidates.
>>
>>
>>
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>>
> François REMY <mailto:fremycompany_pub at yahoo.fr>
> January 14, 2012 1:01 PM
> If we want to avoid to break TCP, we can go with "throw break;" and 
> "throw continue;". It would throw a new BreakException or a new 
> ContinueException, from the place where they are executed. If it's 
> outside a block lambda, it's outside a block lambda. It doesn't matter.
> But it would set a "standard" for breaking throug 'function loops'.
> François
> *From:* Brendan Eich <mailto:brendan at mozilla.org>
> *Sent:* Saturday, January 14, 2012 9:51 PM
> *To:* Herby Vojc(ík <mailto:herby at mailbox.sk>
> *Cc:* es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> *Subject:* Re: Block Lambdas: break and continue
>> Herby Vojc(ík <mailto:herby at mailbox.sk>
>> January 14, 2012 10:42 AM
>> === David Herman wrote ===
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>> ===
>>
>> What about the exception-less suggestion I put in? It should work in 
>> any loop construct with lambda-block, even if you must know a little 
>> about the loop implementation itself. That is, to be able to put:
>>
>>    continue |expression|;
>
> Who says the block-lambda is being called from a loop at all? Why 
> should use-cases that want an early result and completion have to use 
> continue, which is for loops?
>
> Worse, this violates TCP. Now you copy and paste this block-lambda 
> code back into a block statement to refactor the other direction, and 
> no such "here is the completion value, do not flow past this point in 
> the block" semantics obtain.
>>
>> as a statement in lambda block which instructs the lambda-block 
>> itself (not the outer function) to return the expression? This is the 
>> de-facto continue semantics (lambda-block, do return a value and the 
>> enclosing loop will continue to the next iteration (possibly stopping 
>> the loop if it chooses not to have more iterations)).
>
> No it's not. There is no de-facto continue semantics for block-lambdas 
> because they haven't been prototyped. For block statements, no such 
> continue semantics exists.
>
>> It is not possible to enforce break in the same manner, but for 
>> continue, it is possible.
>
> It's possible to abuse any existing keyword, but first: why must there 
> be a new TCP violation? Block-lambda bodies are often expressions, or 
> if statements, then short/functional-style statements, not large 
> bodies demonstrating early-normal-completion opportunities.
>
> We should not eliminate TCP violations only to add new ones, 
> especially without any evidence they're needed and pay their way. 
> Otherwise we'll get an infinite regress of 
> TCP-pure-then-add-new-exceptions-and-repeat additions.
>
> /be
>>
>> Herby
>>
>> -----Pôvodná správa----- From: David Herman
>> Sent: Saturday, January 14, 2012 6:12 PM
>> To: Axel Rauschmayer
>> Cc: Brendan Eich ; es-discuss at mozilla.org
>> Subject: Re: Block Lambdas: break and continue
>>
>> On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:
>>
>>
>> If I understand your suggestion, you're proposing that non-local 
>> break and continue should be exposed as standard exceptions, and then 
>> implementors of loop-like abstractions could choose to catch them. 
>> E.g. you could implement forEach as:
>>
>>    Array.prototype.forEach = function(f) {
>>        for (let i = 0, n = this.length; i < n; i++) {
>>            try {
>>                f.call(this, this[i], i);
>>            } catch (e) {
>>                if (e instanceof BreakException)
>>                    break;
>>                else if (e instanceof ContinueException)
>>                    continue;
>>                else
>>                    throw e;
>>            }
>>        }
>>    };
>>
>> Whereas a function that does *not* want to expose whether it's using 
>> loops would simply do nothing with BreakException and 
>> ContinueException, and they would propagate out and you'd get the 
>> lexical scoping semantics. Meanwhile, break/continue with an explicit 
>> target would never be catch-able.
>>
>> Did I understand your suggestion correctly?
>>
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>>
>> Dave
>>
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>> David Herman <mailto:dherman at mozilla.com>
>> January 14, 2012 9:12 AM
>>
>> If I understand your suggestion, you're proposing that non-local 
>> break and continue should be exposed as standard exceptions, and then 
>> implementors of loop-like abstractions could choose to catch them. 
>> E.g. you could implement forEach as:
>>
>> Array.prototype.forEach = function(f) {
>> for (let i = 0, n = this.length; i < n; i++) {
>> try {
>> f.call(this, this[i], i);
>> } catch (e) {
>> if (e instanceof BreakException)
>> break;
>> else if (e instanceof ContinueException)
>> continue;
>> else
>> throw e;
>> }
>> }
>> };
>>
>> Whereas a function that does *not* want to expose whether it's using 
>> loops would simply do nothing with BreakException and 
>> ContinueException, and they would propagate out and you'd get the 
>> lexical scoping semantics. Meanwhile, break/continue with an explicit 
>> target would never be catch-able.
>>
>> Did I understand your suggestion correctly?
>>
>> This *may* not violate TCP (I'm not quite sure), but I'm not 
>> enthusiastic about the idea. The semantics is significantly more 
>> complicated, and it requires you to understand whether a higher-order 
>> function like forEach is catching these exceptions or not. So it 
>> becomes an additional part of the API of a function. If someone 
>> doesn't document what they do with BreakException and 
>> ContinueException, then writing callbacks you won't actually be able 
>> to predict what `break` and `continue` will do.
>>
>> Dave
>>
>>
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 13, 2012 9:04 PM
>> I think it's a valid concern. The idea is: If I can implement my own 
>> loops (the nice-looking paren-free syntax feeds that illusion!) then 
>> I also want those loops to have break and continue. You could 
>> statically determine what construct, say, a break applies to and 
>> either throw a BreakException (if it applies to a lambda) or 
>> TCP-break (if it applies to an enclosing non-lambda loop). In the 
>> examples below, when I see a continue, I look for the innermost 
>> enclosing loop braces and the ones belong to list[i].forEach are 
>> definitely candidates.
>> -- 
>> Dr. Axel Rauschmayer
>> axel at rauschma.de <mailto:axel at rauschma.de>
>> home: rauschma.de <http://rauschma.de>
>> twitter: twitter.com/rauschma <http://twitter.com/rauschma>
>> blog: 2ality.com <http://2ality.com>
>> Brendan Eich <mailto:brendan at mozilla.org>
>> January 13, 2012 8:54 PM
>>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>>> January 13, 2012 7:29 PM
>>> Block lambdas have been a hot topic, recently, but there's a point 
>>> of significant divergence between Ruby (which appears to be the 
>>> inspiration)
>>
>> Not Ruby alone, and not in any chauvinist my-language-is-better 
>> sense. Smalltalk is the original inspiration for Ruby blocks, and the 
>> correspondence principle has deep roots.
>>
>>> and the proposed solution, in the handling of continue (called 
>>> 'next', in Ruby) and 'break'.
>>> To whit: In Ruby, 'next' will end the current run (iteration) of the 
>>> block, and 'break' will (somehow) terminate the method lexically 
>>> connected with the block. It can be claimed that this is more 
>>> intuitive than the current proposal, which aims to make 'break' and 
>>> 'continue' propagate through block lambdas in the same way 'return' 
>>> would.
>>
>> "Intuitive" depends on intuition, which is not well-defined. Do you 
>> mean a Rubyist might expect different behavior for break? That is 
>> possible but JS ain't Ruby and break should not change to do 
>> something like what it does in Ruby (and we aren't defining a next 
>> equivalent for JS).
>>
>>> Ruby does also support syntactic loops and the same keywords therein 
>>> and so directly violates Tennent's Correspondence Principle, even 
>>> though such has been touted as a core reason for the construct. 
>>> Instead, I believe it reasonable to invoke intuition in this matter. 
>>> It is intuitive for 'return' to return a value from the lexically 
>>> enclosing method and it is intuitive for 'continue' to commence the 
>>> next iteration of the current loop,
>>
>> Wait, why do you think break and continue without label operands do 
>> anything other than break from the nearest enclosing loop (or switch 
>> or labeled statement if break), or continue the nearest enclosing 
>> loop? The proposal specifies this.
>>
>> function find_odds_in_arrays(list,        // array of arrays
>>                              skip)        // if found, skip rest
>> {
>>   let a = [];
>>   for (let i = 0; i < list.length; i++) {
>>     list[i].forEach {
>>       |e|
>>       if (e === skip) {
>>         continue;                         // continue the for loop
>>       }
>>       if (e & 1) {
>>         a.push(e);
>>       }
>>     }
>>   }
>>   return a;
>> }
>>
>> function find_more_odds(list, stop) {
>>   let a = [];
>>   for (let i = 0; i < list.length; i++) {
>>     list[i].forEach {
>>       |e|
>>       if (e === stop) {
>>         break;                      // break from the for loop
>>       }
>>       if (e & 1) {
>>         a.push(e);
>>       }
>>     }
>>   }
>>   return a;
>> }
>>
>>> however that loop is constructed.
>>
>> What do you mean by this? The spec talks about nearest enclosing loop 
>> or relevant control structure in the source code. Are you talking 
>> about internal loops in implementations (dynamically dispatched at 
>> that) of methods that take block-lambdas as arguments? I.e.
>>
>>
>> function find_first_odd(a) {
>>   a.forEach { |e, i|
>>               if (e & 1) return i; }  // returns from function
>>   return -1;
>> }
>>
>>
>> The Array.prototype.forEach method's internal implementation is its 
>> business, and a break instead of the return would be a static error 
>> in this example. It would not be a dynamic throw-like construct that 
>> is caught by forEach's implementation.
>>
>> /be
>> Grant Husbands <mailto:esdiscuss at grant.x43.net>
>> January 13, 2012 7:29 PM
>> Block lambdas have been a hot topic, recently, but there's a point of 
>> significant divergence between Ruby (which appears to be the 
>> inspiration) and the proposed solution, in the handling of continue 
>> (called 'next', in Ruby) and 'break'.
>> To whit: In Ruby, 'next' will end the current run (iteration) of the 
>> block, and 'break' will (somehow) terminate the method lexically 
>> connected with the block. It can be claimed that this is more 
>> intuitive than the current proposal, which aims to make 'break' and 
>> 'continue' propagate through block lambdas in the same way 'return' 
>> would.
>> Ruby does also support syntactic loops and the same keywords therein 
>> and so directly violates Tennent's Correspondence Principle, even 
>> though such has been touted as a core reason for the construct. 
>> Instead, I believe it reasonable to invoke intuition in this matter. 
>> It is intuitive for 'return' to return a value from the lexically 
>> enclosing method and it is intuitive for 'continue' to commence the 
>> next iteration of the current loop, however that loop is constructed.
>> Note that the label-based break/continue could still have the desired 
>> effect, if the proposal was updated to be more like Ruby's blocks.
>> I don't have a strong opinion on the subject, but I hadn't noticed 
>> the above being discussed, elsewhere, and thought it worth raising. 
>> If there is a better place for me to raise this, please let me know 
>> where and accept my apologies.
>> Regards,
>> Grant Husbands.
>> _______________________________________________
>> es-discuss mailing list
>> es-discuss at mozilla.org
>> https://mail.mozilla.org/listinfo/es-discuss
>
> ------------------------------------------------------------------------
> _______________________________________________
> es-discuss mailing list
> es-discuss at mozilla.org
> https://mail.mozilla.org/listinfo/es-discuss
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1021 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0007.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1290 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0008.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1080 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0009.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1327 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0010.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1254 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0011.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postbox-contact.jpg
Type: image/jpeg
Size: 1222 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0012.jpg>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: compose-unknown-contact.jpg
Type: image/jpeg
Size: 770 bytes
Desc: not available
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/5ce79c63/attachment-0013.jpg>


More information about the es-discuss mailing list