Search code examples
javaelmchecked-exceptions

How does elm's compilation differ from Java's checked exceptions?


elm's claim of zero-runtime-exceptions is one of its major selling point (see official website),

But if you stop to think about it, nothing stops you from dividing by zero or running out of memory.

What the elm compiler basically does, is forcing you to cover all possible paths that can lead to an exception.

For example:

import String exposing (toInt)
toIntOrZero s = case toInt s of
                          Err e -> 0
                          Ok val -> val

But how does this differ from the infamous "checked-exceptions" feature in java ?

public static Integer toIntOrZero(String s) {
    try { return Integer.valueOf(s); }
    catch (NumberFormatException e) { return 0; }
}

I never heard any claims that java is a zero-runtime-exceptions language.


Solution

  • Please don't get too caught up on what is essentially marketing hyperbole. Of course there are classes of errors that you will never be able to fully rule out with any compiler.

    As such, I've always taken these zero-runtime-exceptions claims with a grain of salt, but I think I understand the proponent's intent. Elm was created as an alternative to developing front-end applications in Javascript, which is a messy world where exceptions abound and are just part of everyday life. Elm makes it much harder to shoot yourself in the foot, and without too much effort, if you run through basic sanity testing on your app, you probably won't ever have runtime exceptions in production.

    Elm drastically reduces the possibility of exceptions in a few ways.

    1. There is no notion of a throwable exception in the language aside from Debug.crash, which, as its name implies, should really only be used for debugging and stubbing out incomplete logic paths.

      Since there are no throwable exceptions, handling problems is most often done through types like Result and Maybe.

      This could be thought of as loosely analagous to Java's checked exceptions but conceptually they feel very different than me. Let's face it. Exceptions have been abused. You mention an example in Java, where Integer.valueOf() says that it is going to return an int but if you pass it anything else, it unrolls the stack and bubbles up until some function hopefully catches it. This feels very messy to me, and sure, checked exceptions can aid in reducing the window for failure propagation, but the underlying fact is that an exception is the wrong tool for business logic.

      An alternative to throwing an exception would be to have classes analagous to the Result and Maybe Elm types, but that would have been nearly impossible in the early days of Java to do cleanly, and even with Generics, writing such types is more tedious and error prone than the simplicity of Elm's types. And because of Elm's closed type system,

    2. Non-exhaustive pattern matches cause a compilation failure

      In Java and Javascript, there is no way to have exhaustive pattern match checking because the type system does not allow it. Sure, Typescript has introduced some functionality but you have to opt into it. In Elm, you have to explicitly handle all cases. Sure, I suppose you could argue that Elm let's you opt out of exhaustive pattern matching by ending all case statements with a catch-all _, but that would just be a silly abuse of the language. Those checks are there to help you, and I feel much safer with the fact that I don't get to opt-in to error checking in Elm - it's there by default!

    3. Immutability

      Immutability avoids loads of potential types of errors, too many to get into here.

    4. The Elm Architecture offers a clean separation between Javascript and Elm

      Elm compiles down to Javascript, but the Elm Architecture offers a nice clean barrier to keep all the nasty bits of Javascript away from the pure code written by Elm. Any exception that may happen in Javascript should be handled by that barrier, such that I/O errors will always be translated into an Elm-friendly, exceptionless type.

    In the end, runtime exceptions are still possible (case in point: the next tagged Elm question dealt with a well-known runtime exception caused by a recursive JSON Decoder definition), and I cringe a little any time I hear someone say it's impossible to get an exception in Elm. The fact of the matter is that exceptions are possible, but nearly every exception you'd run across in day-to-day Javascript development is essentially impossible in Elm.