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