4 ms·
> This is almost always unacceptable in production, This is not true for almost all software outside of embedded domains. In fact responding to an error near w
by barrkel 9y ago
> This is almost always unacceptable in production,
This is not true for almost all software outside of embedded domains. In fact responding to an error near where the error occurred is almost always a mistake. The rare exceptions to this are when "error" is unavoidable due to concurrency (e.g. I/O), and even then many errors shouldn't be handled (misconfiguration, incomplete system initialization, etc.) or should be wrapped and transformed for human consumption (e.g. user tried to access a resource that couldn't be found - we still bubble up).
> you need to catch all the errors and think critically about what should be done when that error is encountered.
If you support this position, you'd support checked exceptions like Java - something that's widely considered a mistake.
There's lots of reasons why it's a mistake. A more important reason has to do with dynamic construction of control flow. If you use any monad design patterns in the construction of your application (easy to do because the monad design pattern is so powerful), you quickly create boundaries that errors need to cross unmolested because they're generic and cannot possibly encode policy. The same criticisms follow through into dependency injection and other forms of modular decomposition that use dynamic recomposition. Java style has you doing utterly pointless things, like wrapping all the implementation errors in a generic module-level error - obscuring the very thing that might theoretically let user code react to the error state!
More philosophically, abstractions fail in implementation-dependent ways. When you encode policy about failure outside the abstraction boundary, you're encoding implementation-dependent behaviour; but you can't put the policy inside the abstraction boundary either, because then the abstraction isn't reusable - different applications have different policies. Error handling is inherently opposed to abstraction. That's why microscopic error-handling close to the cause of the error only flies in small closed systems, like kernels, databases, embedded.
- masklinn 9y ago> If you support this position, you'd support checked exceptions like Java - something that's widely considered a mistake. The primary problem of checked exceptions in java is not that they exist, it's that they are very badly integrated with the rest of the language e.g. it's impossible to be generic or transparent over checked exceptions, their hierarchy is messy and it's often unclear why specific exceptions are checked and others unchecked. Haskell's error handling (reified through Either error values) has similar effective semantics but nowhere near the dislike because it's well integrated in the language and you can be generic over the error, the value, and the result itself.
- barrkel 9y agoHaskell doesn't encourage runtime polymorphic module boundaries. That's the key difference with Java, not hierarchies. Abstractions invoked through runtime polymorphic interfaces compose poorly with statically typed exception specifications. Generic composition that lets error types flow through abstractions also lets implementation details flow through, so it is an architectural choice, make no mistake. You don't dodge the tradeoff between abstraction and error handling.