Block Lambdas: break and continue

Brendan Eich brendan at mozilla.org
Sat Jan 14 13:24:20 PST 2012


> François REMY <mailto:fremycompany_pub at yahoo.fr>
> January 14, 2012 1:14 PM
> What you said about ‘large-scale’ try/catch seems irrevelant to me.

Not at all, since you among others are proposing break and continue 
exceptions (whether thrown explicitly via new syntax, or implicitly by 
break and continue statements). This as Allen's examples showed requires 
scaling across more than one activation, and across dynamic dispatch (no 
static-only break or continue as in the strawman).

> If you make a function that takes a callback as an argument, you 
> already HAVE to use a try-catch,

Not so. First, real code does not use try/catch in such callback calling 
functions at all. The Array extras do not try/catch, whether they are 
implemented in JS or some host VM implementation language (usually C++). 
Exceptions propagate and unwind without any try-guarding or 
catch/finally handling.

> since you have to prevent any faillure in the code of the callback 
> since you can’t trust it, even if he’s empty (as you noted).

I noted no such thing.

> So, the BreakException has no chance to go outside the function who 
> called the block lambda.

The problem is that all callback-calling functions that "loop" (however 
defined) must now try/catch, where before they did not. Yes, there is a 
benefit: in your proposal one can "throw break" or "throw continue" from 
a block-lambda provided as the callback, *if* one knows the API well and 
is sure of which calling function was dispatched dynamically.

For other functions, and when composing deeper callstacks (as in Allen's 
logging example), then one has to think about whether to try or not. It 
may be that the thrown break or continue can propagate without handling, 
but if the intervening function is in a loop, then not so: that function 
must try and rethrow in its catch clause, in order to get the "throw 
break" to reach the grand-caller of the block-lambda. This is an onerous 
requirement in the limit!

> If you want to implement a function that loops, you just need to add 
> some code to handle BreakException and ContinueException.

Yes, but handle how? This is a new and harsh requirement. 
Callback-calling (downward-funarg calling, let's say) functions written 
yesterday and today simply do *not* try/catch because a callback might 
throw. Exception handling is seldom used in the large except to void the 
exception.

/be
> If you don’t want, you just don’t use it. It’s up to you.
> *From:* Brendan Eich <mailto:brendan at mozilla.org>
> *Sent:* Saturday, January 14, 2012 10:00 PM
> *To:* Axel Rauschmayer <mailto:axel at rauschma.de>
> *Cc:* es-discuss at mozilla.org <mailto:es-discuss at mozilla.org>
> *Subject:* Re: Block Lambdas: break and continue
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 14, 2012 12:35 PM
>> 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.
>
> See Allen's post for how this does not compose well.
>> I don’t think it’s a must-have, but whenever you catch exceptions, 
>> you have similar issues.
>
> Sorry, termination style exception handling is pretty much a failure 
> outside of local protocols with automated catching (for/of in Harmony 
> loops, comprehensions, and generator expressions).
>
> People build local ad-hoc try/catch machines, e.g. for generator 
> schedulers, and those can be managed even without syntax, but these 
> "kernels" require expertise and careful API design.
>
> But in general when you see a try/catch, the catch is empty and the 
> try is because of a call out of module or ownable unit of code, into 
> some hostile subsystem or "other" that has in the past thrown a random 
> exception.
>
> IOW, termination-style exception handling does not scale. It's also a 
> dynamic thing, like magic return codes. Intervening functions on the 
> call stack with no necessary static relation to one another may have 
> to try and catch (or at least try and finally) to unwind-protect. Not 
> for memory management, of course, but for other mandatory protocols 
> such as RAII patterns.
>
> So I'm against adding BreakException and ContinueException and 
> defining block-lambda break and continue in terms of throw. As Dave 
> and Allen argued, this enlarges the contract of every possible 
> intervening function to include exceptions. Exceptions are usually 
> ignored at high level, used only locally (across shallow continuation 
> calls).
>
> We should avoid adding dependencies on exceptions that must be managed 
> by arbitrarily large codebases and arbitrarily deep callstacks.
>
> /be
>> -- 
>> 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>
>>
>> _______________________________________________
>> 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
> Brendan Eich <mailto:brendan at mozilla.org>
> January 14, 2012 1:00 PM
>> Axel Rauschmayer <mailto:axel at rauschma.de>
>> January 14, 2012 12:35 PM
>>
>> 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.
>
> See Allen's post for how this does not compose well.
>>
>> I don’t think it’s a must-have, but whenever you catch exceptions, 
>> you have similar issues.
>
> Sorry, termination style exception handling is pretty much a failure 
> outside of local protocols with automated catching (for/of in Harmony 
> loops, comprehensions, and generator expressions).
>
> People build local ad-hoc try/catch machines, e.g. for generator 
> schedulers, and those can be managed even without syntax, but these 
> "kernels" require expertise and careful API design.
>
> But in general when you see a try/catch, the catch is empty and the 
> try is because of a call out of module or ownable unit of code, into 
> some hostile subsystem or "other" that has in the past thrown a random 
> exception.
>
> IOW, termination-style exception handling does not scale. It's also a 
> dynamic thing, like magic return codes. Intervening functions on the 
> call stack with no necessary static relation to one another may have 
> to try and catch (or at least try and finally) to unwind-protect. Not 
> for memory management, of course, but for other mandatory protocols 
> such as RAII patterns.
>
> So I'm against adding BreakException and ContinueException and 
> defining block-lambda break and continue in terms of throw. As Dave 
> and Allen argued, this enlarges the contract of every possible 
> intervening function to include exceptions. Exceptions are usually 
> ignored at high level, used only locally (across shallow continuation 
> calls).
>
> We should avoid adding dependencies on exceptions that must be managed 
> by arbitrarily large codebases and arbitrarily deep callstacks.
>
> /be
>>
>> -- 
>> 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>
>>
>> _______________________________________________
>> 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
> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.mozilla.org/pipermail/es-discuss/attachments/20120114/9f33df93/attachment-0001.html>
-------------- 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/9f33df93/attachment-0005.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/9f33df93/attachment-0006.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/9f33df93/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/9f33df93/attachment-0008.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/9f33df93/attachment-0009.jpg>


More information about the es-discuss mailing list