<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>