<html><body><div style="font-family: arial, helvetica, sans-serif; font-size: 12pt; color: #000000"><div><br></div><div><br></div><hr id="zwchr" data-marker="__DIVIDER__"><div data-marker="__HEADERS__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><b>From: </b>"Brian Goetz" <brian.goetz@oracle.com><br><b>To: </b>"Tom L" <tom_l64@hotmail.com>, "amber-dev" <amber-dev@openjdk.org><br><b>Sent: </b>Sunday, March 5, 2023 3:24:38 AM<br><b>Subject: </b>Re: Language feature to improve checked exceptions<br></blockquote></div><div><style>@font-face
        {font-family:"Cambria Math";
        panose-1:2 4 5 3 5 4 6 3 2 4;}@font-face
        {font-family:Calibri;
        panose-1:2 15 5 2 2 2 4 3 2 4;}p.MsoNormal, li.MsoNormal, div.MsoNormal
        {margin:0cm;
        font-size:11.0pt;
        font-family:"Calibri",sans-serif;}.MsoChpDefault
        {mso-style-type:export-only;}div.WordSection1
        {page:WordSection1;}</style></div><div data-marker="__QUOTED_TEXT__"><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><font size="4"><font face="monospace">I have some sympathy for the
        desire to optimize catch-and-wrap; doing this the regular way is
        indeed </font></font><font size="4"><font face="monospace"><font size="4"><font face="monospace">syntactically </font></font>painful
        when the actual business logic is small (which it often is.) 
        Catch-and-wrap works well as an abstraction idiom when turning a
        low-level exception (e.g., IOException) into a higher-level one
        (e.g., XMLException); as higher-level library-code delegates to
        lower-level library code, it will want to remap low-level errors
        to higher-level ones.  (Particularly interesting is
        catch-and-wrap at the method declaration level, with some sort
        of `throws X as Y`, as it represents the catch-and-wrap rules
        declaratively rather than imperatively.)<br><br>
        However, the examples you give of catch-and-wrap are not really
        catch-and-wrap; they are catch-and-pretend-they-didn't-happen
        (by turning checked exceptions into unchecked ones.)  While this
        would surely be popular among the "checked exceptions suck"
        crowd, this is not making error handling more reliable, it is
        just making it easier to ignore errors.</font></font></blockquote><div><br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>A remark, a catch-and-pretend-they-didn't-happen is just a catch-and-wrap without the enclosing context.</div><div><br data-mce-bogus="1"></div><div>For example, you can see<br data-mce-bogus="1"></div><div>  List<String> foo() {<br data-mce-bogus="1"></div><div>    ...<br data-mce-bogus="1"></div><div>    List<String> list = stream.map(it -> {</div><div>        try {<br data-mce-bogus="1"></div><div>          return IO.something(it);<br data-mce-bogus="1"></div><div>        } catch(IOException e) {<br data-mce-bogus="1"></div><div>          throw new UncheckedIOException(e);<br data-mce-bogus="1"></div><div>        }<br data-mce-bogus="1"></div><div>      }).toList(),<br data-mce-bogus="1"></div><div>    return list;  <br data-mce-bogus="1"></div><div>  }<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>but foo() is called like this<br data-mce-bogus="1"></div><div>  List<String> list;<br data-mce-bogus="1"></div><div>  try {<br data-mce-bogus="1"></div><div>    list = foo()<br data-mce-bogus="1"></div><div>  } catch(UncheckedIOException e) {<br data-mce-bogus="1"></div><div>    throw e.getCause();<br data-mce-bogus="1"></div><div> }<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><font size="4"><font face="monospace"><br>
        The Result approach is both safe and honest (and can be
        implemented today without language help), but is more foreign to
        Java developers.  It operates by moving the side-channel result
        into the main channel, which enables monadic composition
        (turning a partial operation into a total one returning a
        Success|Fail union).  With pattern matching in the language,
        this gets even more attractive.  Still, I suspect that most of
        the "checked exceptions suck" crowd wouldn't thank us for
        kicking off the massive, decade-long migration from checked
        exceptions to the Either monad.  </font></font></blockquote><div><br></div><div>From Java the language POV, returning Either or using a checked exception both side of the same coin, there are semantically equivalent.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>As a proud member of the "checked exception suck in Java" crowd. The "in Java" is important. Checked exceptions do not suck in the vacuum, it suck in Java because on a higher level is goes against the freedom of composition and because on a lower level the Java type system does not handle them well, no union type (only precise rethrow), no proper way to bundle exceptions into a type parameter.</div><div><br data-mce-bogus="1"></div><div>It's the colored function problem [1].  Checked exceptions aka the Result monad does not suck in Rust. Because inherently Rust is a language that consider library composition less important than precise lifetime tracking or asynchronous calls tracking, the whole language relies on colored functions. But Java is not Rust, freedom of composition, freedom to use any libraries whenever it was written, is an important part of the language. That's why Java has a GC, lightweight threads, lambdas, erased generics or the concept of binary backward compatibility. All those features enable free easy composition.</div><div><br data-mce-bogus="1"></div><div>Sadly checked exception in Java colors functions, they create an unnecessary artificial barrier to library compositions. That's why they suck in Java.<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>regards,<br data-mce-bogus="1"></div><div>RĂ©mi<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><div>[1] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/<br data-mce-bogus="1"></div><div><br data-mce-bogus="1"></div><blockquote style="border-left:2px solid #1010FF;margin-left:5px;padding-left:5px;color:#000;font-weight:normal;font-style:normal;text-decoration:none;font-family:Helvetica,Arial,sans-serif;font-size:12pt;"><font size="4"><font face="monospace"><br><br><br></font></font><br>
    <div class="moz-cite-prefix">On 3/4/2023 6:49 PM, Tom L wrote:<br>
    </div>
    <blockquote cite="mid:AM7PR04MB70932A45D29F52A4435206B088B09@AM7PR04MB7093.eurprd04.prod.outlook.com">
      
      
      
      <div class="WordSection1">
        <p class="MsoNormal">Hello, it's the first time I send an email
          here, I have no idea how things work, so I hope I am doing
          things somewhat correctly.</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">I wanted to make a suggestion about
          exceptions :</p>
        <p class="MsoNormal">Checked exceptions are used as a meant of a
          second return type, which shouldn't happen in most cases, and
          then, depending of what the caller wants to do, it will handle
          it in a certain way. So in some sort, a checked exception is
          like part of the return type, like you would have a
          Result<Success, Failure>, compared to unchecked
          exception in java which are just supposed to be bugs.</p>
        <p class="MsoNormal">But this feature causes some burden, so
          much so that some languages, even languages compiling to java
          (ie kotlin) got rid of checked exceptions, and other languages
          like Rust use a Result<Success, Failure> which, which,
          with enough language constructs, can be quite good.</p>
        <p class="MsoNormal">While I agree that not having checked
          exceptions nor Result but instead only unchecked ones would be
          a bad idea, because it would mean that a part of the return
          type is unknown, which we wouldn't want in a strongly typed
          language, I believe some action needs to be taken.</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">In my opinion, one of its burdens is caused
          by the try-catch language feature :</p>
        <p class="MsoNormal">String text;</p>
        <p class="MsoNormal">try {</p>
        <p class="MsoNormal">                text =
          Files.readString(somePath);</p>
        <p class="MsoNormal">} catch(IOException ex) {</p>
        <p class="MsoNormal">                throw new
          UncheckedIOException(ex);</p>
        <p class="MsoNormal">}</p>
        <p class="MsoNormal">//do something with text</p>
        <p class="MsoNormal">This is a common example, of how you would
          use it : you want to read a string, and an IO error shouldn't
          happen, so you fail-fast</p>
        <p class="MsoNormal">The reason why I didn't use text inside the
          catch, is because the catch should only be for this specific
          exception, and also it would add another level of nesting,
          which would start to hurt when other ifs or try-catches
          appear.</p>
        <p class="MsoNormal">This code is boilerplate and is
          error-prone, since you always have to repeat the same lines,
          it is also weird for any non java programmer.</p>
        <p class="MsoNormal">And this is far from being the worst,
          because another common example is with streams, since you
          can't throw checked exception, you have to handle each time,
          in each stream operation, and even if you wrap this code in
          methods and use method references, it's still far less
          readable than having a short lambda where you see exactly what
          the stream is doing.</p>
        <p class="MsoNormal">If checked exceptions were instead a Result
          type, a solution to this problem would be to make a unwrap()
          method (like in Rust, or like with Java's
          Optional#orElseThrow())</p>
        <p class="MsoNormal">String text =
          Files.readString(somePath).unwrap();</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">So my suggestion is that, since catching is
          a language feature, it can only be dealt with another language
          feature :</p>
        <p class="MsoNormal">String text = Files.readString(somePath)
          throw IOException ex as new UncheckedIOException(ex);</p>
        <p class="MsoNormal">If this method throws an IOException, it
          will rethrow it as an UncheckedIOException.</p>
        <p class="MsoNormal">About the syntax, I used "throw" since it's
          an already used keyword, but depending of the meaning, it
          could be "catch" or whatever, and "as" could be "->" if
          using a contextual keyword is too much.</p>
        <p class="MsoNormal">This code could even be simplied as the
          following with new methods or language features, in the future
          :</p>
        <p class="MsoNormal">String text = Files.readString(somePath)
          throw IOException ex as ex.unchecked();</p>
        <p class="MsoNormal">String text = Files.readString(somePath)
          throw IOException as unchecked;</p>
        <p class="MsoNormal">etc.</p>
        <p class="MsoNormal">Unchecked part could either be the same
          exception except the compiler ignores it (I know this is
          possible since it's possible to throw a checked exception as
          an unchecked), the idea is that since you are telling the
          compiler that if an exception happens, then it should
          fail-fast, then the compiler should be able to say "ok, I
          trust you".</p>
        <p class="MsoNormal">An alternative would be that the unchecked
          simply wrap it in a RuntimeException or a specific unchecked
          exception.</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">So, what's the point of this, does it only
          serve this use case ?</p>
        <p class="MsoNormal">So first, is it important to note that it
          isn't just for checked -> fail-fast, but also checked ->
          some other checked, when exception conversion is needed, which
          can be useful, and also unchecked -> checked/unchecked, for
          example if an API only provides an unchecked like
          Integer#parseInt.</p>
        <p class="MsoNormal">Now about the use case : this syntax, isn't
          just a compressed try-catch, it's also an expression, which is
          very important, because not only you can very clearly handle
          this kind of cases, but it can provide easy to read, concise
          code in lambdas  and assignments, for example :</p>
        <p class="MsoNormal">try (var files = Files.list(path) {</p>
        <p class="MsoNormal">                return
          files.filter(this::matcher)</p>
        <p class="MsoNormal">                                              
          .map(p -> Files.readString(p) throw IOException as
          unchecked)//Possible syntax Files::readString throw
          IOException as unchecked ?</p>
        <p class="MsoNormal">                                              
          .toList();</p>
        <p class="MsoNormal">}</p>
        <p class="MsoNormal">And in the future, a new method could be
          added for streams called .continueOnFailure(IOExcepion.class)
          (or UncheckedIOException if the unchecked wraps the exception
          instead of marking it as unchecked) for example, which would
          continue even if there is an exception.</p>
        <p class="MsoNormal">An additional syntax could also be provided
          for cases where catching is used as an if else :</p>
        <p class="MsoNormal">OptionalInt parseInt(String s) {</p>
        <p class="MsoNormal">                return
          OptionalInt.of(Integer.parseInt(s)) catch
          NumberFormatException -> OptionalInt.empty();            //
          Using -> syntax here instead of "as" to show that it can
          also make sense</p>
        <p class="MsoNormal">}</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">I hope it can fix this unholy war of
          checked exceptions which never seem to advance.</p>
        <p class="MsoNormal"> </p>
        <p class="MsoNormal">Sincerely.</p>
      </div>
    </blockquote>
    <br><br></blockquote></div></div></body></html>