News for hackers. Stuff that matters.
Re:Error Handling Patterns by gpderetta on Thursday May 04, 2023 @05:44AM (#35813106)
Obviously sum types for errors are vastly superior to exceptions. Compare the following very readable Error variant pseudocode:

    fn doSomethingFallibly() -> SameVal | Error
       SomeVal x = ...;
       if failed:
          return Error
       else 
          return x

    fn doTheThing() -> void
       match doSomethingFallibly():
          SomeVal x: /* use x */ 
          Error: /* log the error */
To the utterly unreadable exception-based implementation:

    fn doSomethingFallibly() -> SameVal, except Error
       SomeVal x = ...;
       if failed:
          raise Error
       else 
          return x

    fn doTheThing() -> void
       try Someval x = doSomethingFallibly()
          /* use x */ 
       except Error: /* log the error */
This is especially egregious if you want to propagate the error. For the error variant case you can use a beautiful monadic solution

    fn doTheThing() -> void|Error
       SomeVal x <- doSomethingFallibly()?
While the exception based variant completely obscures the control flow:

    fn doTheThing() -> void, raises Error
       SomeVal x = try doSomethingFallibly()
Re:Error Handling Patterns by golergka on Thursday May 04, 2023 @06:31AM (#35813385)
Depends on where do you want to handle a particular error.

For errors that you can handle right in the calling function, this is ideal.

For errors that should kill your whole aapp, may be do some housekeeping (like abort the whole transaction that wraps your logic to reply to this http request) and get reported to sentry, catching exceptions at some highest level is far more usable.

I prefer to use both depending on meaning of the error.

Re:Error Handling Patterns by AnimalMuppet on Thursday May 04, 2023 @08:37AM (#35814306)
I don't like your approach. In general, I'm a fan of "use the right tool for the job". But for error handling, I prefer one approach for the whole application. If you mix approaches (exceptions here and sum types there), you tend to get bugs at the seams between the regions. If you mix them more uniformly in the code, that tends to be uniformly harder to reason about.
Re:Error Handling Patterns by golergka on Thursday May 04, 2023 @12:29PM (#35818050)
Interesting point. What would you consider to be the seam between regions in this case?
Re:Error Handling Patterns by HelloNurse on Thursday May 04, 2023 @06:43AM (#35813463)
This is the kind of sarcasm I'd like to see more often on Hacker News.
Re:Error Handling Patterns by xnoreq on Thursday May 04, 2023 @06:47AM (#35813498)
The issue is that in many programming languages that offer exceptions, exceptions are misused to handle common and unexceptional non-success cases. I think that is because it is hard to draw a sharp line between the two.

Java already went in the right direction with distinguishing errors from exceptions and having "checked" (that you cannot ignore) exceptions, but the implementation of those concepts sucks. Also, generics, which allow the implementation of "sum types", came later and so sum types were never established as the way to do error handling.

Re:Error Handling Patterns by tialaramex on Thursday May 04, 2023 @09:36AM (#35815098)
> exceptions are misused to handle common and unexceptional non-success cases.

This is my insurmountable objection to Exceptions.

What people will tell you is that you should only use Exceptions for "real" error conditions which should be rare, but the problem is that the people deciding whether to use Exceptions are writing some library, whereas the people who know whether this is a rare condition are the application writers, using that library only second or third hand, or maybe via some third party component. You might as well provide a language where the primitive integer types don't have a specified size so programmers don't know if numbers will fit in them...

... oh right yeah. That checks out.

Re:Error Handling Patterns by masklinn on Thursday May 04, 2023 @12:41PM (#35818267)
It's also an interesting objection because it means that when you have exceptions... you still need an error handling method for non-exceptional errors.
Re:Error Handling Patterns by mananaysiempre on Thursday May 04, 2023 @07:24AM (#35813776)
Jokes aside, I can think of a couple of differences:

- For the longest time, languages with typed (“checked”) exceptions lacked any parametric polymorphism worth a damn. That made the typing... less than optimally useful, especially with fallible callbacks. Sum types can borrow all that from the existing language, and you’d feel intensely awkward if you tried to go with untyped errors there (it’s of course still possible, through Dynamic or interface{} or whatever Rust’s thing is).

Though looking at the boilerplate fest that is the latest Rust (sum-types-based) error handling article posted here[1], I’m not entirely convinced the situation has improved. Perhaps open union types of some sort really are needed? That’s where the algebraic-effects languages seem to be going, anyway.

- Exceptions are more strict about what you can do with them: you can return or you can bind, monadically speaking. If an exception was raised, everything stops until it’s caught. With sum types (concrete ones, not an abstract error monad you can’t run), you are allowed to have several propagation chains at once and manipulate them as data. The lack of that additional flexibility is what makes exceptions (more cheaply?) implementable in terms of stack unwinding.

(Traditionally, stack unwinding is thought of as expensive, but I don’t really see why. Sure SJLJ-induced register spills are bad, and sure table-based unwinding is slow as hell, but a linked list of frames as in Win32 SEH[2] has to be cheaper than the same thing implemented as conditional jumps in every unwinding path, right? Right? I must be missing something here, expired Borland patent aside.)

- Concrete sum types cannot implement resumable exceptions[3], as far as I can tell. I’m almost sure an abstract error monad could, but for some reason I have never seen it done. Besides, working with that is going to require more syntactic support than what you’ve shown. (Yes, I know, such syntactic support is possible to implement and has in fact been implemented.)

[1] https://sabrinajewson.org/blog/errors, discussed at https://news.ycombinator.com/item?id=35502874

[2] http://bytepointer.com/resources/pietrek_crash_course_depths...

[3] https://gigamonkeys.com/book/beyond-exception-handling-condi...

Re:Error Handling Patterns by gpderetta on Thursday May 04, 2023 @07:43AM (#35813887)
1) Agree, the issues with (checked) exceptions and error monads mostly stem from expressive limitations of a specific language than the model itself.

2) The next step up is making the exception continuation first class, then I don't think there is any difference. I think you can recover the efficiency of the simple non-local goto by optimizing after CPS transform, but I'm not a compiler writer.

3) As you point out, I think the continuation monad should be able to do resumable exceptions.

You mention algebraic effects, which are probably the right way to implement Error types/Exceptions/Resumable Error handlers.

Re:Error Handling Patterns by zactato on Thursday May 04, 2023 @05:51PM (#35822583)
I agree, Monads don't solve everything, but if we're being pedantic then I would argue that in a functional context you would almost never write

fn doTheThing() -> void, raises Error SomeVal x = try doSomethingFallibly()

Functions don't do things, they process and return things. Side effects are handled higher up at the root of a program. Generally any time you'd be "doing a thing" you'd also be in a position to handle the error.

source: I don't do any functional programming but have seen some videos on youtube