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 ; [email protected]
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
[email protected]
https://mail.mozilla.org/listinfo/es-discuss
_______________________________________________
es-discuss mailing list
[email protected]
https://mail.mozilla.org/listinfo/es-discuss
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
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.
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
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.
|