<html><head></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><div>On Jan 14, 2012, at 9:12 AM, David Herman wrote:</div><br class="Apple-interchange-newline"><blockquote type="cite"><div>On Jan 13, 2012, at 9:04 PM, Axel Rauschmayer wrote:<br><br><blockquote type="cite">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.<br></blockquote></div></blockquote><div><br></div><div>I also think there is a valid concern here.  The issue isn't whether the current break/continue statement semantics can be preserved when encapsulated by a block lambda.  That is clearly possible because the semantics are tied to specific syntactically identifiable statements.  The real issue is whether the same syntax can support break/continue-like semantics for user defined controls abstractions built using Block Lambdas.  Put another way, can Block Lambdas based abstractions completely replace (at least conceptually) the built-in loop statements. This would seem desirable, but the answer isn't obviously yes -- hence the concern.</div><div><br></div><div>Consider the following function:</div><div><br></div><div>function forNum(start, end,increment=1,body={||}) {</div><div>   if (start > end) return;</div><div>   body(start);</div><div>   return forNum(start+increment,end,increment,body);</div><div>}</div><div><br></div><div>User of the looping abstraction might reasonably expect to say something like:</div><div><br></div><div>forNum(first,last,1) {|n|</div><div>    if (n&1) continue;</div><div>    if (n==special) {</div><div>        doSpecialStuff(n);</div><div>        break;</div><div>     }</div><div>     doEven(n);</div><div>};</div><div><br></div><div>with the meaning being essentially the same as they would get if they had used a for statement instead of the forNum function. Similarly for:</div><div><br></div><div><div>forNum(first,last,1) {|x|</div><div>    forNum(firstY(x),lastY(x),1) {|n|</div><div>       if (n&1) continue;</div><div>       if (n==special) {</div><div>          doSpecialStuff(n);</div><div>          break;</div><div>       }</div><div>       doEven(n);</div><div>   }</div><div>};</div><div><br></div></div><div>where the continue and break apply to the inner loop rather than the outer loop. Before trying to figure out how to we could make these work (something like exceptions seems like a reasonable path) we should for look at some other possible use cases.  Consider:</div><div><br></div><div>function logBlk(label, block) {</div><div>    logStream.put("starting "+label +Date());</div><div>    block();</div><div>    logStream.put("completed "+label +Date());</div><div>}</div><div>    </div><div>used in:</div><div><br></div><div><div>forNum(first,last,1) {|n|</div><div>    logBlk("process even values") {||</div><div>       if (n&1) continue;</div><div>       if (n==special) {</div><div>          doSpecialStuff(n);</div><div>          break;</div><div>       }</div><div>       doEven(n);</div><div>   }</div><div>};</div><div><br></div></div><div>This later use of forNum has essentially the same shape (think about them with forNum and logBlk renamed to func1 and func2) as the doubly nested example, yet the "binding" of the continue and break affects need to be different. How can this be achieved?  Apparently there needs to be something additional in the implementation of forNum that won't occur in logBlk.  Also, from a usage perspective, how does the user of these functions know that logBlk is transparent to break/continue while forNum is not?  Presumably, this needs to be part of the usage "contract" of those functions.  How can that be expressed?  Is it implicit or explicit?  </div><div><br></div><div>I'm not aware of very many languages that have addressed these issues, particularly in combination with syntactic looping constructs. The problem doesn't exist in Smalltalk because it doesn't have either break/continue or syntactic loops. It sounds like Ruby's solution may not be quite right.  The most serious attempt that I know of to address this issue is in the BGGA closure proposal for Java:  <a href="http://www.javac.info/closures-v05.html">http://www.javac.info/closures-v05.html</a> (see Loop Abstractions section), <a href="http://gafter.blogspot.com/2006/10/iterative-control-abstraction-user.html">http://gafter.blogspot.com/2006/10/iterative-control-abstraction-user.html</a> <a href="http://tronicek.blogspot.com/2008/08/nonlocal-transfer.html">http://tronicek.blogspot.com/2008/08/nonlocal-transfer.html</a> <a href="http://tronicek.blogspot.com/2008/08/modifier-for.html">http://tronicek.blogspot.com/2008/08/modifier-for.html</a> </div><div><br></div><br><blockquote type="cite"><div><br>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:<br><br>    Array.prototype.forEach = function(f) {<br>        for (let i = 0, n = this.length; i < n; i++) {<br>            try {<br>                f.call(this, this[i], i);<br>            } catch (e) {<br>                if (e instanceof BreakException)<br>                    break;<br>                else if (e instanceof ContinueException)<br>                    continue;<br>                else<br>                    throw e;<br>            }<br>        }<br>    };<br><br>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.<br><br>Did I understand your suggestion correctly?<br><br>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.<br><br>Dave<br><br>_______________________________________________<br>es-discuss mailing list<br><a href="mailto:es-discuss@mozilla.org">es-discuss@mozilla.org</a><br>https://mail.mozilla.org/listinfo/es-discuss<br><br></div></blockquote></div><br></body></html>