<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class="">On May 8, 2018, at 12:31 PM, Brian Goetz <<a href="mailto:brian.goetz@oracle.com" class="">brian.goetz@oracle.com</a>> wrote:<br class=""><div><blockquote type="cite" class=""><br class="Apple-interchange-newline"><div class=""><span style="font-family: Helvetica; font-size: 16px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class="">If continue is the right notion, our choice is essentially between:</span><br style="font-family: Helvetica; font-size: 16px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 16px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""> - drive towards continue in switch working like it does in everything else, and deal with ambiguities (switch in loop) as they come up;</span><br style="font-family: Helvetica; font-size: 16px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px;" class=""><span style="font-family: Helvetica; font-size: 16px; font-style: normal; font-variant-caps: normal; font-weight: normal; letter-spacing: normal; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; word-spacing: 0px; -webkit-text-stroke-width: 0px; float: none; display: inline !important;" class=""> - drive towards something that looks sufficiently different from "naked" continue (continue with label, "continue switch") in switches, and accept there will be a permanent seam (in switch you do it this way, in loops, this other way.)</span></div></blockquote></div><br class=""><div class="">(Cards on table:  I'm on Team `continue switch;` and $0.02 follows.)</div><div class=""><br class=""></div><div class="">The permanent seam doesn't bother me very much, for the following</div><div class="">explainable reason:  Switch is not a proper loop, although it is a</div><div class="">continuable statement.  If you want to continue a continuable</div><div class="">statement that isn't a loop, you have to be specific.  (And you</div><div class="">should be specific anyway if your code is deeply nested.)</div><div class=""><br class=""></div><div class="">Classic Java loops while/for/do-while are (usually) not statically</div><div class="">bounded.  Depending on their logic they can run an arbitrary</div><div class="">number of times.</div><div class=""><br class=""></div><div class="">Now, by saying "continue" in a switch, we are ascribing it a loopish</div><div class="">nature.  And this is true, because a switch is (in our new theory)</div><div class="">a _decision chain_, that is a finite, sequence of statically defined</div><div class="">predicates which are tested in order until one matches.  The number</div><div class="">of predicates is arbitrary (like a loop) but they are listed statically</div><div class="">(unlike a loop).</div><div class=""><br class=""></div><div class="">In both loops and decision chains there is a well-defined and very</div><div class="">useful control flow operation, which is "continue to the next step".</div><div class="">The next step of the loop is to run the iteration and execute the</div><div class="">loop body again.  The next step of the decision chain is to abandon</div><div class="">the current predicate and test the next one.</div><div class=""><br class=""></div><div class="">The "continue" concept fits the bill for both.  But compatibility says</div><div class="">a bare "continue" must go to a proper loop, not a decision chain.</div><div class="">Thus, bare continue always reaches the enclosing proper loop,</div><div class="">but "continue switch" or "continue L" (L labeling the switch)</div><div class="">reaches the enclosing switch, because although switches are</div><div class="">not proper loops, they are loopish, they are continuable even</div><div class="">if they cannot run forever like a loop.</div><div class=""><br class=""></div><div class="">Explained this way, the "seam" Brian is referring to is an artifact,</div><div class="">not of history, but of the distinction between proper loops and</div><div class="">other continuable ("loopish") constructs.  Personally I'd be fine</div><div class="">with this seam as a permanent thing.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">The seam can be reduced also, and I think that is what Brian</div><div class="">is aiming at.  Bare "continue" in an expression switch will always be</div><div class="">unambiguous, since it *cannot* reach an enclosing loop (no branches</div><div class="">out from an expression.)  Sailing yet more closely to the wind:  Bare</div><div class="">"continue" in a switch *not* nested in a loop is also unambiguous,</div><div class="">since there's no proper loop to reach.</div><div class=""><br class=""></div><div class="">Brian, are you thinking that bare continue, inside switch, *inside loop*,</div><div class="">is an *ambiguity error*?  That would be worth warning about about:</div><div class="">Today's correct code would become an error tomorrow:</div><div class=""><br class=""></div><div class="">for (;;) {</div><div class="">   switch (x) {</div><div class="">      case 0:</div><div class="">        continue;  // Error/warning: ambiguous unlabeled continue</div><div class="">   }</div><div class="">}</div><div class=""><br class=""></div><div class="">The message could say "unlabeled continue is ambiguous when</div><div class="">nested in both switch and proper loop, repair by saying either</div><div class="">'continue switch' or 'continue for', or use a label."</div><div class=""><br class=""></div><div class="">In that case, the warnings could be sent even after feature adoption.</div><div class="">Eventually when the warnings turn to errors, no code changes</div><div class="">semantics, but some code breaks.  Or make it be a warning forever.</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">OK, now for some "decision chain theory".</div><div class=""><br class=""></div><div class="">Besides switches, decision chains have two other forms which are</div><div class="">worth contemplating, as elucidating the essential structure in another</div><div class="">surface form, and also as a possible refactoring target.</div><div class=""><br class=""></div><div class="">switch (x) {</div><div class=""> case P1 -> S1;  // suppressing legacy fallthrough for simplicity</div><div class=""> case P2 -> S2;</div><div class=""> …</div><div class="">}</div><div class=""><br class=""></div><div class="">if (x matches P1) S1;</div><div class="">else if (x matches P2)  S2;</div><div class="">else …</div><div class=""><br class=""></div><div class="">for (Function<XType, Optional<YType>> casef : List.of(</div><div class="">   x -> x matches P1 ? Optional.of(S1) : Optional.none(),</div><div class="">   x -> x matches P2 ? Optional.of(S2) : Optional.none()</div><div class="">   )) {</div><div class="">  var y = casef.apply(x);</div><div class="">  if (!y.isPresent())   continue;</div><div class="">  result = y.get();</div><div class="">  break;</div><div class="">}</div><div class=""><br class=""></div><div class="">The third form seems exotic but it is a coding pattern some</div><div class="">of us have surely used for decision chains, test lists, and</div><div class="">on similar occasions.  (I have!)</div><div class=""><br class=""></div><div class="">The continue keyword is native in the third form, and corresponds</div><div class="">exactly to the proposed continue [switch] form for the first form.</div><div class=""><br class=""></div><div class="">OK, now I'm going to push farther, by your leave, with a thought</div><div class="">experiment exploring and extending the correspondence between</div><div class="">the first two forms of decision chain, switch and if/else.</div><div class=""><br class=""></div><div class="">The second form is of course a far more common refactoring of</div><div class="">decision chains; in fact one of the motivations of pattern-switch</div><div class="">is to refactor many existing if/else decision chains into easier-to-read</div><div class="">switches.  Here's an example:</div><div class=""><br class=""></div><div class=""><div class="">if (x matches Plus(0.0, var a)) {</div><div class="">   res = a;</div><div class="">} else if (x matches Plus(var a, 0.0)) {</div><div class=""><div class="">   res = a;</div><div class="">} else {</div></div><div class="">   res = x;</div><div class="">}</div><div class=""><br class=""></div><div class="">This simplifies to a switch-based decision chain:</div><div class=""><br class=""></div></div><div class=""><div class="">switch (x) {</div><div class="">case Plus(0.0, var a) -> res = a;</div><div class="">case Plus(var a, 0.0) -> res = a;</div><div class="">default -> res = x;</div><div class="">}</div></div><div class=""><br class=""></div><div class="">Here's the odd part:  The close duality between if and switch forms</div><div class="">suggests that we should also consider "continue if", as a form which</div><div class="">means "find the innermost enclosing 'if' with a continuation point</div><div class="">and branch to it".  What on earth is a continuation point of an 'if'?</div><div class="">That's easy, it's spelled "else".  In other words, if/else (not just</div><div class="">if w/o else) is loopish too.  (And "if" without an "else" gets passed</div><div class="">by from "continue" since there is no continuation point.)</div><div class=""><br class=""></div><div class="">Here's the same example, but enhanced with guards (isNan):</div><div class=""><br class=""></div><div class="">switch (x) {</div><div class="">case Plus(0.0, var a):</div><div class="">   if (a.isNan())  continue switch;</div><div class="">   res = a;</div><div class="">   break;</div><div class="">case Plus(var a, 0.0):</div><div class="">   if (a.isNan())  continue switch;</div><div class="">   res = a;</div><div class="">   break;</div><div class="">default:</div><div class="">  res = x;</div><div class="">}</div><div class=""><br class=""></div><div class="">What's the equivalent if/else decision chain, with the same guards?</div><div class=""><br class=""></div><div class=""><div class="">if (x matches Plus(0.0, var a) && !a.isNan()) {</div><div class="">   res = a;</div><div class="">} else if (x matches Plus(var a, 0.0) && !a.isNan()) {</div><div class=""><div class="">   res = a;</div><div class="">} else {</div></div><div class="">   res = x;</div><div class="">}</div></div><div class=""><br class=""></div><div class="">But this one pushes the guards to one side, making the</div><div class="">basic structure easier to read (in some cases, not all!):</div><div class=""><br class=""></div><div class="">if (x matches Plus(0.0, var a)) {</div><div class="">   if (a.isNan())  continue if;</div><div class="">   res = a;</div><div class="">} else if (x matches Plus(var a, 0.0)) {</div><div class=""><div class="">   if (a.isNan())  continue if;</div><div class="">   res = a;</div><div class="">} else {</div></div><div class="">   res = x;</div><div class="">}</div><div class=""><br class=""></div><div class="">How often have you added a complicated "&& !foo" rider expression</div><div class="">to an otherwise clear "if/else" chain, just to push control forward towards</div><div class="">the next "else" which miight handle your marginal "foo" condition?</div><div class="">I have, many times.  I think "continue if" would have been helpful.</div><div class=""><br class=""></div><div class="">(Note that "break if" is slightly useful too, if you just want to execute</div><div class="">a localized action for some marginal condition and be done.  Today</div><div class="">you need to nest your main action equally with the marginal action,</div><div class="">putting an if/else inside the if/else.  That's not too bad, but we might</div><div class="">sometime might prefer the option of making the marginal case be</div><div class="">an asymmetrical add-on to the main flow of logic.)</div><div class=""><br class=""></div><div class="">— John</div><div class=""><br class=""></div></body></html>