Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

excepts were the only way in CLU to handle control flow. something like

    while read_character { 
        handle_character 
    } except_when end_of_file {
        return string
    }
and it did this primarily because it was typesafe: read_character always returned a character, and the "except" cases could return what they were declared to return. "except" was far from exceptional.

There's nothing ineffecient about that, it's meatly linked up at compile/load time, and exceptions that go up the stack simply unwind the stack just as returning values up the stack do.

What gets users confused is conflating this with the idea of hardware interrupts and system signals, external exceptional events that can occur at any time, interrupting the flow of control and needing registers (cpu state) to be saved so the process can be restored and continue when the interrupt is handled. (and which CLU handled through the same system) Such interrupts and signals do have a superficial resemblance to excepts that are unexceptional, and to not so unusual and recoverable errors like out-of-disk-space on a file write, which is only code you're going to write for an important high availability or headless system, or a nice fat rich word processor that people will sit in all day long.

I'm just explaining this all because when I read discussions like this I'm constantly thinking "but...but... did you think of...?"



Returning an option sumtype is also typesafe.

Exceptions have to walk up the stack until a suitable handler is found, that handler can't know ahead of time where the value is coming from or if it will ever arrive - just what type it will be if it arrives. Code emitting exceptions also have no knowledge who (if anyone) is going to handle their output. It is a nonlocal goto in reverse.

Compared to regular functional returns there are so many unknowns. I'd prefer returning an option value any day of the week.


> Code emitting exceptions also have no knowledge who (if anyone) is going to handle their output.

This is no different from code returning a regular value. When writing `return -EINVAL;` or `return 0, fmt.Errorf("")` or `return Err(something)`, you have no idea who (if anyone) is going to handle your output.

Also, one reasonable way of implementing exceptions could be exactly to translate all functions that can throw exceptions to functions returning a Result type, and translating function calls to such functions to the equivalent of pattern matching on that return. Of course, this mostly forces the language to only support checked exceptions (otherwise this overhead would be added to all function calls, even those that can't actually fail).

Most languages that implement exceptions have chosen a different trade-off though: make exceptions costly to throw, but make sure they have 0 cost on the happy path. Happy-path code becomes more efficient than it is possible to be in a language which uses return values for errors (since there is no need to check the result before using it), but the unhappy path becomes significantly worse.


A normal function return is handled directly by the caller, even if the caller decides to completely ignore it. In the case of exceptions it continues to implicitly pop the stack to attempt to find a handler. The explicit vs implicit nature is quite different.

I'd also point out that in gcc and clang support nodiscard and warn_unused_result giving a function some ability to force callers to handle returns. Go, rust and even java (thanks to errorprone) have similar guardrails.

I think you also need to balance the marginal efficiency wins of not checking for errors on return with the overall robustness of your program. The likelihood that an error condition is properly handled is heavily predicated on your ability to know that it might occur in the first place. In a language where exceptions are common place make this very challenging because they heavily rely on unchecked exceptions. Languages that value error checking have tended to shun exception style in favor of returning option types.


> one reasonable way of implementing exceptions could be exactly to translate all functions that can throw exceptions to functions returning a Result type, and translating function calls to such functions to the equivalent of pattern matching on that return

That's exactly what the lightweight exception proposal for C++ argued for. It additionally used some ABI tricks like storing the discriminant in a flag register when returning from an exception throwing function allowing for very cheap and compact pattern matching.


That's one way to compile exceptions. There are other methods with different tradeoffs for different cases.




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

Search: