<div dir="auto"><div dir="ltr"><div><span style="font-family:monospace">Comments:</span></div><div><span style="font-family:monospace">----------------<br></span></div><div><span style="font-family:monospace">The motivation of this feature is well founded. We need a way to handle exceptional scenarios and normal data flows in a uniform way. That said I can feel some of the same scepticism expressed in the experts group regarding adding `case throws` to the switch block.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">One major problem I see is having to learn that the 'normal' cases and the 'exceptional' are mutually exclusive: Flow cannot shift from one to the other. When I am coding normal cases I am thinking "This won't match this case, so will check the next case and so on - so the ordering is semantically important. Whatever way we introduce this feature, it has to 'model' this mutual exclusivity in the structure of the code so that it is plain and obvious.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">One more related problem I see is that I have to learn that an exception in any of the normal case arms will not be caught in the exceptional arms - Here again the ordering of the exceptional cases becomes important - i would then have to write all the exceptional arms first, and then the normal arms, just so that someone reading my code doesn't get confused - but this is not mandated by the language grammar. Then there is this confusion of interleaved normal and exceptional cases which the JEP recommends against like this: "It is strongly recommended to group normal cases together and exception cases together". While this might be ok - and IDEs/auto-formatters can auto-group these cases without loss of semantics (albeit with ensuing format wars - should exceptional arms be first or last?) - to me it looks like a hint that something is missing in the way we are modelling the structure of the code.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">There are other minor issues I have with the syntax proposed in the JEP - I will address those in the next section;<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">In the same thread, Brian spoke about the try monad:</span></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<pre> switch (try e) {
case Success(P1) -> …
case Success(P2) -> …
case Failure(E1) -> …
}
</pre>
</blockquote><div><span style="font-family:monospace">This I think gets to the root of the issue:</span></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<pre>We explored this point as well in the exploration, and backed off.</pre></blockquote><div><span style="font-family:monospace">I would request to go down this road a bit further and not back off. It seems to me that even if we do not actually implement the try monad, completely hashing out a theoretical mental model around the try monad will help us model the language in the right direction - at the very least helping us avoid ending up creating different ways of doing the same thing.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">========================================</span></div><div dir="auto"><span style="font-family:monospace"><br></span></div><div dir="auto"><span style="font-family:monospace"><br></span></div><div dir="auto"><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Suggestion:</span></div><div><span style="font-family:monospace">-----------------------</span></div><div><span style="font-family:monospace">To me it looks like the right direction would be something like "enhanced catch blocks" with the grammar of the catch block mimicking the switch block. The idea being for this to become a uniform way to handle exceptions in *any* place in java - not just in the enhanced switch.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Examples:</span></div><div><span style="font-family:monospace">1. traditional Try block:<br></span></div><div><div><span style="font-family:monospace">try {</span></div><div><span style="font-family:monospace"> doSomething();</span></div><div><span style="font-family:monospace"> thenSomething();
</span></div><div><span style="font-family:monospace">} catch {</span></div><div><span style="font-family:monospace"> IllegalArgumentException e -> handle1(e);</span></div><div><span style="font-family:monospace"> IllegalStateException _ -> handle2();</span></div><div><span style="font-family:monospace"> NoSuchElementException | MissingConfigException lub -> handle3();</span></div><div><span style="font-family:monospace"> default e -> handleDefault(e);<br></span></div><div><span style="font-family:monospace">}<br></span></div><span style="font-family:monospace"><br></span><div><span style="font-family:monospace">Advantages:</span></div><div><span style="font-family:monospace">Works with existing try blocks syntax<br></span></div><div><span style="font-family:monospace">The enhanced catch clause looks more like a switch - less ceremony, more actual code</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Notes:<br></span></div><div><span style="font-family:monospace">It supports the same dominance hierarchy as the old catch, unnamed variables, multi catch, nothing new to learn<br></span></div><div><span style="font-family:monospace">default e compiles to Throwable e - so that devs don't forget to catch some nonException Throwables (This can be skipped if it causes more problems than it solves)</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">2. With enhanced switch:</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">switch try (doSomething().thenSomething()) <br></span></div><div>
<div><span style="font-family:monospace"> catch {</span></div><div><span style="font-family:monospace"> IllegalArgumentException e -> handle1(e);</span></div><div><span style="font-family:monospace"> IllegalStateException _ -> handle2();</span></div><div><span style="font-family:monospace"> NoSuchElementException | MissingConfigException lub -> handle3();</span></div><div><span style="font-family:monospace"> default e -> handleDefault(e);<br></span></div><div><span style="font-family:monospace">} {</span></div><div><span style="font-family:monospace"> case a -> normal1(a);</span></div><div><span style="font-family:monospace"> case b-> normal2(b);<br></span></div><div><span style="font-family:monospace"> default -> noop();<br></span></div><div><span style="font-family:monospace">}<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Advantages:</span></div><div><span style="font-family:monospace">Mutual exclusivity!</span></div><div><span style="font-family:monospace">Catch comes first always as it corresponds to the try- no format wars<br></span></div><div><span style="font-family:monospace">No repetition of 'case throws' on each line - less ceremony - the catch outside the block gives the necessary context. (Tangent: Can we similarly move 'case's outside the case block and avoid the case repetitions?)<br></span></div><div><span style="font-family:monospace">No wars about 'should it be case throws or case throw or case catch or just catch' (I support just catch followed by case throws FWIW)<br></span></div><div><span style="font-family:monospace">Clean mental model: switch has case arms, switch try has catch arms and case arms (no need for normal/exceptional terminology)<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Challenges:</span></div><div><span style="font-family:monospace">A little more verbose with few extra braces.<br></span></div><div><span style="font-family:monospace">Some might balk at the fact that the exception handling comes first - but I think this is temporary. In the long term, the unambiguity of the fact that any exception in the normal arms will not be handled by the catch arms is worth much more IMO.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">3: Enhanced switch try with an enclosing try intended to catch exceptions thrown from the case arms and any from the catch arms<br></span></div><div><span style="font-family:monospace"><br></span></div><div>
<div><span style="font-family:monospace">try{<br></span></div><div>
<div><span style="font-family:monospace"> switch try (doSomething().thenSomething()) <br></span></div><div>
<div><span style="font-family:monospace"> catch {</span></div><div><span style="font-family:monospace"> IllegalArgumentException e -> handle1(e);</span></div><div><span style="font-family:monospace"> IllegalStateException _ -> handle2();</span></div><div><span style="font-family:monospace"> NoSuchElementException | MissingConfigException lub -> handle3();</span></div><div><span style="font-family:monospace"> default e -> handleDefault(e);<br></span></div><div><span style="font-family:monospace"> } {</span></div><div><span style="font-family:monospace"> case a -> normal1(a);</span></div><div><span style="font-family:monospace"> case b-> normal2(b);<br></span></div><div><span style="font-family:monospace"> }</span></div><div><span style="font-family:monospace">} catch {</span></div><div><span style="font-family:monospace"> UberException u -> x();<br></span></div><div><span style="font-family:monospace"> OtherException o -> y();<br></span></div><div><span style="font-family:monospace"> default e -> z();<br></span></div><div><span style="font-family:monospace">}<br></span></div></div>
</div>
</div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Advantages:</span></div><div>
<div><span style="font-family:monospace">Composes very cleanly with an uber try catch designed to catch exceptions thrown from the case and catch arms.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">4. Enhanced catch with traditional switches:</span></div><div><span style="font-family:monospace"><br></span></div><div>
<div><span style="font-family:monospace">switch try (doSomething().thenSomething()) <br></span></div><div>
<div><span style="font-family:monospace"> catch {</span></div><div><span style="font-family:monospace"> IllegalArgumentException e -> handle1(e);</span></div><div><span style="font-family:monospace"> IllegalStateException _ -> handle2();</span></div><div><span style="font-family:monospace"> NoSuchElementException | MissingConfigException lub -> handle3();</span></div><div><span style="font-family:monospace"> default e -> handleDefault(e);<br></span></div><div><span style="font-family:monospace">} {</span></div><div><span style="font-family:monospace"> case a: </span></div><div><span style="font-family:monospace"> normal1(a);</span></div><div><span style="font-family:monospace"> break;<br></span></div><div><span style="font-family:monospace"> case b:</span></div><div><span style="font-family:monospace"> normal2(b);<br></span></div><div><span style="font-family:monospace"> break;<br></span></div><div><span style="font-family:monospace"> default: </span></div><div><span style="font-family:monospace"> noop();<br></span></div><div><span style="font-family:monospace">}</span></div></div>
</div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Advantages:</span></div><div><span style="font-family:monospace">Allows devs to start doing better exception handling without messing with old switches!<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">5: <<FUTURE>> Try expressions:</span></div><div><span style="font-family:monospace"><br></span></div><div>
<div><span style="font-family:monospace">var value = try (doSomething().thenSomething()) <br></span></div><div>
<div style="margin-left:80px"><span style="font-family:monospace"> catch {</span></div><div style="margin-left:80px"><span style="font-family:monospace"> IllegalArgumentException e -> v1;</span></div><div style="margin-left:80px"><span style="font-family:monospace"> IllegalStateException _ -> </span>
<span style="font-family:monospace">yield v2</span>
<span style="font-family:monospace">;</span></div><div style="margin-left:80px"><span style="font-family:monospace"> NoSuchElementException | MissingConfigException lub -> </span> <span style="font-family:monospace">v3</span><span style="font-family:monospace">;</span></div><div style="margin-left:80px"><span style="font-family:monospace"> default e -> throw e;<br></span></div><div style="margin-left:80px"><span style="font-family:monospace">}<br></span></div></div>
</div>
</div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Advantages: </span></div><div><span style="font-family:monospace">This is exactly similar to the switch try syntax just without the switch and case arms.<br></span></div><div><span style="font-family:monospace">The yielding works exactly like the case arms<br></span></div><div><span style="font-family:monospace">This exact syntax will start working if and when the try monad comes - the expression '</span>
<span style="font-family:monospace">doSomething().thenSomething()' can be replaced by the monad variable 'result' etc.</span>
</div><div><span style="font-family:monospace">Similarly we can extend this to make the switch try itself a 'switch try expression', again same syntax<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Mental model:</span></div><div><span style="font-family:monospace">------------<br></span></div><div><span style="font-family:monospace">Switch switches across values (normal cases)</span></div><div><span style="font-family:monospace">Catch switches across exceptions</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Catch always catches what is in the try</span></div><div><span style="font-family:monospace">Case is for what is in switch</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">'switch try' combines switch and try - giving us 'catch'-ability and 'case'-ability</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Developers have these options:</span></div><div><span style="font-family:monospace">Switch + Case Block</span></div><div><span style="font-family:monospace">Switch + case expression</span></div><div><span style="font-family:monospace">Switch Try + New Catch + enhanced Case block</span></div><div>
<div><span style="font-family:monospace">Switch Try + New Catch + old Case block</span></div><div><span style="font-family:monospace">Switch try + New Catch expression + case expression<br></span></div><div><span style="font-family:monospace">Old Try (with resources)? + Old Catch</span></div><div><span style="font-family:monospace">Old Try (with resources)? + New catch<br></span></div><div><span style="font-family:monospace"><<IN FUTURE>></span></div><div><span style="font-family:monospace">Try monad + new catch</span></div><div><span style="font-family:monospace">Switch + Try Monad + new catch + enhanced switch<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">The right option can be used in different contexts based on use case and readability.<br></span></div>
</div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Final Note:</span></div><div><span style="font-family:monospace">----------<br></span></div><div><span style="font-family:monospace">In general this syntax allows me to do better code reviews "Hey, have all the exceptional scenarios been handled? (checks the catch arms) Done? Good. Now let's go to the case arms". (This is similar in spirit to checking all the preconditions related to method parameters at the beginning of methods)<br></span></div><div><span style="font-family:monospace">It also brings the catch block into the future, along with the switch - and preserves the well learnt pattern - where there is a catch, there is a try above - What is being caught is for what is inside the try.</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">HTH</span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace">Sincerely,</span></div><div><span style="font-family:monospace">Ram Anvesh<br></span></div><div><span style="font-family:monospace"><br></span></div><div><span style="font-family:monospace"><br></span></div></div></div></div></div>