Please ignore performance issues, I am interesting in data flow, safety, modelling, reasoning.

I wonder what are the limitations of exception approach to error reporting implemented like in Java compared to Haskell-like (OCaml, and Rust use same/similar approach AFAIK).

As far as I know in both cases error is part of the function signature, but in Haskell you have to explicitly "handle" the error even if entire "handling" is just passing it further. I see a difference for the reader, in Java:

  a = foo();
  b = bar();

it is impossible to tell (just by looking at the code) if foo and/or bar can end up with error, also in both cases the error can be silently passed. If I understand correctly in Haskell the the counterpart code would be put into monad:

do a <- foo();
   b = bar();

(I am new to Haskell, so forgive me the errors) and it is clear where the drop occurs and which line can fail.

BUT -- this is for human, compiler knows it in both cases.

So when it comes to, say, reasoning about the data flow (for compiler, not human), what are the limitations of Java approach? In other words, what is not doable in Java that is possible in Haskell? Or what information Haskell has which is not present in Java?

  • 2
    I think this question is too broad - exceptions in Java and in Haskell are completely different things due to the languages being completely different. I don't think it makes sense to enumerate the differences since it would come down to enumerate fundamental differences between the two languages in general. – JacquesB May 22 '16 at 10:26
  • @JacquesB, assuming there are million differences it is sane to say that million has to start somewhere, correct? So what are 3 differences when it comes to reasoning about errors? – greenoldman May 22 '16 at 10:34
  • Java supports both checked and unchecked exceptions. Haskell exceptions are mostly comparable to checked exceptions, while unchecked exceptions are more like Haskell "errors", which are completely different from exceptions. In Java exceptions are baked into the core language but in Haskell they are a library feature, and you could create your own exception framework if you want. – JacquesB May 22 '16 at 10:49
  • @JacquesB, .... and how this what you wrote translate to analysis of the data flow? – greenoldman May 22 '16 at 11:33
  • 2
    Check out also Monad vs Exception. You are comparing two different approachs for the same goal. – Laiv May 22 '16 at 12:42

I think the most interesting difference between the two, certainly from the perspective of a language designer, is that Haskell's error handling is not a language feature but is part of the library, and yet it manages to be just as expressive and easy to work with as Java's. The reason that this works so nicely is due to Haskell's widespread use of Monads, which allow two useful properties:

  • the code implementing the Monad type is passed blocks of code (i.e. functions that operate on the value(s) stored in the Monad and return a new Monad instance, which are conceptually comparible to the notion of either a code block or a statement in most imperative languages), and is allowed to call those one, none, or indeed as many times as it wants. In any order it wants (cf the continuation monad). This means that user level code can effectively implement just about any flow control pattern it needs.

  • the language provides easy-to-use syntax sugar that makes writing code using Monads particularly easy.

Another interesting feature of Haskell's reliance on a user-supplied data type for handling errors is that if you write data-type generic code, then the caller of that code can dictate how the errors are handled. For example, if I write a function with a signature like this:

myUsefulFunction :: Monad m => String -> String -> m String

I can call this function in a variety of ways... for example, using maybe defaultString id (myUsefulFunction str1 str2), which will give the result of the function on success or a default if it failed, or using runError (myUsefulFunction str1 str2) which will return an Either containing the error message or the result. This allows more choice in how to structure my code, allowing me to produce a better result with less boilerplate code (no catching exceptions and substituting a default value, for instance).

  • Thank you very much, I will keep my question open though (at least for a while), because while being useful it does misaddress my question a bit. I am not after differences like "this is in language/framework", but the the limitations of Java which Haskell overcomes when it comes to data flow analysis. Maybe an example from other area -- both languages probably can handle sequences, like counting them, but if one of them can handle infinite sequence and the other cannot, I say one overcame the limitation present in the other one. And the fact it is done via framework is irrelevant. – greenoldman May 23 '16 at 18:45
  • Some background explanation -- Haskell, Ocaml, Rust (and probably many others) share similar way of handling errors. One quote (but I saw more) "Rust’s design allows to mechanically eliminate a nontrivial bug class" (as opposition to Java). Obviously some breakthrough, the problem is I don't see it :-). This is why I asked about limitations, not just differences, I know them, but I don't see where they lead. I hope I put my question in more meaningful perspective. – greenoldman May 23 '16 at 18:51
  • 1
    Ok, having looked up a bit about Rust's error handling, I think I can see where you're coming from. The commonality between Rust and Haskell is that error conditions are (in the end) objects that can either contain a value or some kind of error indicator, and the language forces you to check which one is present before accessing the value. The benefit quite simply is that code that ignores errors (which is a very common problem) can be caught by the compiler and rejected, simply because the data types involved explicitly require that. – Jules May 23 '16 at 19:02
  • Thank you :-) but I still don't see it -- for example here: lucumr.pocoo.org/2014/10/16/on-error-handling, initial code includes error "handling", but entire handling is passing, or you could say leaking. Since checked exceptions in Java are part of function signature you can also compute which exception is handled or leaked. The only difference I see, in Rust the leak is explicit, in Java is implicit, but the effect is the same, you could write profiler for Java which warns you about every implicit leak. – greenoldman May 24 '16 at 11:48
  • 2
    It is possible to perform such analysis, even statically in most cases (case in point: Java 8's Optional class warns you if you use the get() method without first calling isPresent()). But as pointed out by Karl Bielefeldt in this answer to a related question, such an analysis is expensive and you want to avoid doing it too much otherwise you compilation process will take too long. In Haskell and Rust, the requirement to check whether or not the result is present is encoded in the type system, and therefore happens automatically. – Jules May 24 '16 at 12:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.