4 ms·
Enforced Error Handling
- greenyoda 11y ago"I wonder whether there is an imperative language that enforces explicit handling of errors..." Java does that. If a method can throw an exception, you need to either handle the exception in a 'catch' block or throw it up the call stack for another method to handle. If you do neither, your code won't compile.
- lost_name 11y agoBasically. You need to handle all checked exceptions (Exception class), but not unchecked exceptions (RuntimeException class).
- ufo 11y agoThe problem with checked exceptions a-la Java is that the type system is not polymorphic. You need to list the exceptions you raise explicitly and there is no way to say "I raise the same exceptions that this object I receive can raise"
- placeybordeaux 11y agoIn the second paragraph the author seems to be only talking about errors as C and go have then, specifically as errors as values, not as exceptions. The important difference being that exceptions can occur anywhere and can lead to non explicit bailing out of a function. Exceptions complect failure and control flow. Judging from the docs (and the comments on the blog) it looks like rust might have largely what the author is looking for. Can anyone speak to that?
- snuxoll 11y agoRust handles errors in a functional-style way with std::result<T, E>. This is the same technique I use in F# code with a identically named discriminated union.
- Chris_Newton 11y agoI’m not convinced by the fundamental premises the article opens with, i.e., that exceptions are only useful if you can tolerate a program crashing now and then, and that if you care about reliability you must use C-style error handling instead, checking everything at every level of the stack. To my knowledge, there is no known system in mainstream use that reliably enforces that check-at-every-stage policy. Moreover, it is not immediately obvious how one would create such a system in a practically useful way, at least not without immediately running into the same sorts of issues commonly used to criticise exception handling in terms of keeping track of every possible failure mode that lower level code could indicate. One thing we do know is that if programmers are not absolutely compelled to handle errors then sometimes they won’t. The article itself cites an example from a very common library function widely called in C code among many other languages. Another thing we know is that if programmers are compelled to handle errors then they will cheat. Specifically, sometimes they will write useless code to satisfy the technical requirements of the language while not in fact doing anything useful to recover from the error. Java’s checked exception mechanism is infamous for unintentionally promoting this sort of programming, and the paper cited by the post we’re discussing specifically calls this out as a real world problem. A third thing we do know is that sometimes there isn’t anything useful you can do in response to some failures. Maybe your program simply won’t be able to complete its task successfully under those circumstances and the best you can do is fail as gracefully as possible. Given these constraints, systematic error handling at an architectural level seems to be the more promising strategy, and there seems little reason to prefer fine-grained, manual error checking code everywhere instead. Exceptions are one way of implementing error handling at an architectural level, and to me they have always seemed a natural fit for sound functional decomposition in a software design. As a base case, you can always reduce to a situation where an operation either completes successfully or it fails and you don’t distinguish between different types of failure. This is just the universal catch block or its equivalent in your language of choice. Anything more specific than that can be added as necessary. The one thing you can’t do is have higher level code accidentally continue as if there is nothing wrong once lower level code has explicitly indicated that a failure has occurred. Another architecture-level strategy is an Erlang idiom, where a failed process is effectively allowed to crash completely, with a separate supervisor process then detecting this and perhaps restarting the failed process to recover. This is somewhat analogous to the base case with exceptions and universal catch, in that normal execution can’t inadvertently continue once a failure resulting in a crash happens. It seems to me that either of these strategies, or any other systematic mechanism that imposes detection and recovery at an architectural scale, is likely to be more robust in the real world than a system that relies on manually checking everything. The latter, in the event of any oversight, can allow higher level code to continue to operate as if nothing is wrong even after code at a lower level has explicitly determined that a failure has occurred, presumably carrying around an error that may subsequently manifest as a different kind of failure and ultimately a system fault. This seems to be exactly what the article was trying to argue against, yet the proposed strategy for dealing with errors, one manual level at a time, seems to be just about the worst way we know to avoid it.
- jallmann 11y agoCompiler-enforced discipline is the best kind of discipline. What the author wants is ML. Specifically, he wants: 1. A statically typed language 2. Exhaustive handling on the type of returned values 3. To discourage impure functions or mutable state Other languages (eg Rust, Haskell) fit these criteria as well. C might be forgiven given its age, but Go really has no excuse for such a weak type system.