Wait, wait! As an intuitive kind of person I often come to results but don’t exactly know the logical path, which I must painfully reconstruct later.
That said, I have to strongly oppose this:

===
Anyway, as others have written, this seems an abuse of 'continue'.
===

On the contrary, I would say. I will explain below. It is related to the next cited paragraph.

===
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".
===

My view on continue was "stop evaluating the block and continue with the control-flow". This is purely control-flow friendly, I would even say control-flow-submissive notion of continue. I see no need for any exceptions there.

So, I would like to elaborate here a in more detail about continue and break. It shows my proposal for early return was not abuse of continue keyword, and also lays down a possibility to reuse both continue and break naturally without any need for exceptions.

For now, the semantics of "continue;" is explained this way:

Stop the processing of nearest enclosing loop body and continue the control flow with the next iteration of the loop.

Now, my bold thesis is: This is not the true meaning of continue. The true meaning of continue (as per the Occam razor) is:

Stop the processing of nearest enclosing continue-scope-block and continue with the control flow.

You don't need the notion of iterator, you even don't need the notion of loop. You only need the (weak and general) continue-scope-block for this to work. Continue-scope-block is presently defined as "body of for statement; or body of while statement; or body of do-while statement" (it is probably only a coincidence they are all loops :-) ).

Give it a though, just for a while.

[Note: Even from "humanly" point of view, the latter seems more natural and better to grasp; the former seems too artifical. Maybe it's only me.]

First thing to note: If you accept the latter definition of "continue;" (I will cover labeled one, later), you did not break anything in the ES5. It simply works. Second thing to note: Since lambda-blocks are new to ES.next, you can define behaviour that relates it as you please. By defining what continue and break mean inside a lambda-block, you do not break any old code (since it did not contain any lambda-block).

Now, let us do simple addition to definition of continue-scope-block by adding "; or lambda-block" to it. Leave the rest unchanged.

Now "continue;" work (naturally) the same as for for body, while body and do-while body - it stops completion of the lambda-block and lets the outer control-flow go on. In other words, "continue".

This should complete first half of my defense against "you abuse continue". I never meant to. I just grasped its true nature. The one of "skip to the end of block and continue".

But the lambda-block now, getting promoted into continue-scope-blocks, may raise a humble protest: "Well, for the other colleagues, the complation value does not matter at all, so it is fine when continue gives them undefined. But for me, it does. I am defined to return a value!".

FIne, "continue;" stops completion; it should have the possibility to specify the completion value. For example

    continue | expression |;

The bars differentate it from label and also give a hint that the value only matters for the lambda-block (it does not harm in any way when used in syntax-loop).

This should conclude the second half of my defense against continue abuse.

---

As for the labeled continue, the loop-less and iteration-less definition works, too. You just change a few words to get:

Stop the processing of enclosing continue-scope-block whose respective control structure is labeled by label and continue with the control flow.

Here, the respective control structure is for-loop for for-body, while-loop for while-body, do-while-loop for do-while-body and function call that uses it as parameter for lambda-block.

In a sense, labeled continue is weaker than non-labeled, since lambda-block must be (lexically) present inside labeled call. If it is not, the continue label statement should be an error.

And, of course

   continue | expr | label;

is possible.

---

So the bottom line is: new continue is functionally equivalent to the old continue (only its meaning is re-specified), and its works in lambda-block with consistent meaning, exception-less.

---

As for the break, ... I can do very similar thing. But first thing one should realize is, continue and break are _not_ close siblings. Because break already works in switch. They _seem_ similar, but they are different.

The loop-less definition of labeled break is:

Stop the processing of respective control structure (labeled by label) of enclosing break-scope-block, and go on with the control flow.

So the break, well, breaks out of control structure labeled by label, which must be the respective control structure of break-scope-block. This definition is a bit clumsy of the first look, but it is so that unlabeled break can be similar:

Stop the processing of respective control structure of innermost enclosing break-scope-block, and go on with the control flow.

See? You always break out of respective control structure of break-scope-block, either innermost one, or one that has its control structure lebeled.

Now, again, we include lambda-block in the pack along with for-body, while-body, do-while-body and switch-body (because that is in break-scope-block group, too).

And again, lambda-block gives a humble complaint that for the colleagues, the completion value of the respective control structure does not matter, but for lambda-block, it matters. It is a function call, after all, its value may be used!

And again, we may give break the syntax

   break | expr | [label];

to solve the problem.

---

Another bottom line is, this is again consistently (after re-thinking the previous definition; break is changed less) working for previous code as well as for lambda-blocks and is exception-less.

So I’d say there is no need for throw continue; and throw break;. You can reuse continue and break - they are good enough for that, they are just shy to show you their real nature :-)

---

Here I'd stress again the difference between unlabeled continue and the rest. Unlabeled continue is most control-flow friendly construct; you can say it is "relative". It works purely on local ground: stop completion of innermost continue-scope-block and that's it. Works like charm for the lambda-blocks (and, just by the side-effect, it does the local return).

You can use this continue in scenarios like:

   a = {|v,k,c| ...};
   ...
   label: collection.forEach(a);

But all the rest (labeled continue, labeled break, and unlabeled break which is in fact implicitly-labeled-break (think about it; it not local and relative, it break out of the respective control structure, so it is lexically bound; thus implicitly labeled)) is lexically bound. These need their scope-block be lexically enclosed inside the (explicit or implicit) label.

So, in above example, you cannot use "break;". Because {|v,k,c| ...} is present freely, not as part of its respective control structure, that is, the function call. Of course the is true for labeled ones with label, since {|v,k,c| ...} is not lexically present inside label: control structure.

Herby

P.S.: I really mean it. No joke. I just seem to write densely. Ask if there is misunderstanding. Thanks.

-----Pôvodná správa----- From: Brendan Eich
Sent: Saturday, January 14, 2012 11:05 PM
To: Herby Vojčík
Cc: François REMY ; [email protected]
Subject: Re: Block Lambdas: break and continue



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

Reply via email to