Hacker News new | past | comments | ask | show | jobs | submit login

Lack of parameterisation of exception signature is not the reason why checked exceptions are a bad idea, though. Failure is a function of implementation details; runtime errors are fundamentally an abstraction violation. There's no escape.



> Lack of parameterisation of exception signature is not the reason why checked exceptions are a bad idea

It absolutely is one of the reasons why they are a bad idea, and why reified results are so much more useable and useful.

> Failure is a function of implementation details

In the original intention, that's what unchecked exceptions were for, with checked exceptions for the reporting of errors rather than failure. That's why java didn't have only checked exceptions.


Unchecked exceptions were for programmer errors, errors that could be avoided with a sufficiently careful programmer: null pointer check, a divisor not zero check, a list bounds check, etc.

Checked exceptions were for unavoidable errors. Errors that are fundamental to the operation being attempted. They usually occur because the operation interacts with the world outside the program, which means the program is subject to violations of expectations. Network errors. File not found when you try to open it - you cannot test for file existence first without a race.

The reality is that the latter kind of errors are better off as overloaded call signatures: one call variant when you care about the error and want to catch it, another variant when you don't care about specifics of the error and want the whole stack to unwind when the expectation is violated.

Neither of these approaches require checked exception signatures throughout the stack (or Result types for that matter - you can assume I also mean those, due to the isomorphism).

There's a reason .net modeled number parsing with Parse() and TryParse() instead of throwing NumberFormatException like parseInt(). It's because sometimes you care - input came from user and you need to handle it - and sometimes you don't - input came from configuration file and the stack needs to be torn down if you can't parse it.

Picture a stack that looks like this:

    0: <operation that may throw exception of the checked variety>
    1: <code that may be interested in handling error>
    2: <code that doesn't know about implementation details>
       .... could be 10, 100, 200+ methods in this stack dump
    N-1: <code that doesn't know about implementation details>
    N: <request handler or event loop that catches all exceptions>
The great problem with checked exceptions is the methods for stack frames 2 to N-1. Either exceptions are handled at stack entry 1, or at stack entry N. The only job of all the code between is to pass exceptions unmolested back to N.

Those calls may be dynamically bound (whether via vtables or function pointers, objects or closures) and / or dynamically linked (so unavailable to a type system at compile time). In large production programs, control flow will be dynamically determined. It's a fact of life; if it's not for testing purposes, it'll be for deployment flexibility.

I think there's value in the IO monad; in marking functions as the kind of functions that may interact with the outside world. And checked exceptions can work this way. But not unless the error type has a polymorphic storage location, and unwinding the stack is syntactically weightless. I don't ever want to have to change the signature on a dozens of methods just because there's a new implementation detail deep inside a dynamically linked abstraction.

And exception / error wrapping isn't the answer either - it's almost always a bad idea.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: