Live data from Hacker News

Away from Exceptions: Errors as Values

humanlytyped.hashnode.dev

101–110 of 145 posts

Re: Away from Exceptions: Errors as Values

#101
post #31

> Some errors are unexpected and should stop the program; you want to use exceptions for those. Precisely the opposite: exceptions are a fail-fast mechanism that gives you an alternative to terminating the program. Now, as slx26 mentioned, it's only half of the story. Most APIs (including .NET) document exceptions badly, they're not discoverable, and if you try to use them to _recover_ from a condition, you're in for…

There's no need for even that complexity. If base exception type had a single property, something like "CanRetry", all exception handling would be simple.

Because when it comes to exceptions there are really only 2 things you can do: abort the current operation or retry it.

The code generating the exception will know which of these is appropriate and the try/catch handler is what would restart or cancel the operation.

The exact details of the exception are for debugging not for program flow.

Re: Away from Exceptions: Errors as Values

#102

Earlier quoted context omitted.

> the return type of functions which can possibly throw unchecked exceptions would not indicate that they can throw or what they can throw As far as I know, that's how Java's "throws" method signature works, which has been widely regarded as a mistake.

Throws is for checked exceptions. Unchecked are not listed in the throws list.

It's actually even worse: you can include both checked and unchecked exceptions in the `throws` clause. The compiler will only enforce handling of the checked exceptions included. Unchecked exceptions listed in the `throws` clause serve as an optional hint to others. Note that you can erroneously include any exceptions you like in the `throws` clause, even ones that are never thrown from the method. These quirks are often covered by static analysis.

Re: Away from Exceptions: Errors as Values

#103
In my opinion, the difference between errors as return values and checked exceptions is merely one of syntactic sugar. Both are conceptually a sum type together with the regular return value, and the syntactic sugar for handling and/or propagating the error or exception is really a spectrum, not a dichotomy. I believe it would be useful to focus on the possible design choices within that spectrum, regardless of the underlying implementation mechanism.

Of course, the implementation mechanism matters at the machine code level or in the runtime. However, that is mostly a question of performance trade-offs and interoperability, but otherwise just an implementation detail, and doesn’t have to be a question of expressiveness and code-level semantics. You can implement exceptions as subroutine return values, and you can implement error return values with exception-like mechanisms behind the scenes. That should be a different concern from how it looks like at the source-code level.

Re: Away from Exceptions: Errors as Values

#104
post #103

In my opinion, the difference between errors as return values and checked exceptions is merely one of syntactic sugar. Both are conceptually a sum type together with the regular return value, and the syntactic sugar for handling and/or propagating the error or exception is really a spectrum, not a dichotomy. I believe it would be useful to focus on the possible design choices within that spectrum, regardless of the u…

The main caveat is that oftentimes, checked exception handling doesn't compose well - see what kind of trouble Java gives you, for example.

Recent articles I've read on effect modeling languages seems to give a more uniform construct for bringing checked exceptions in line with other control constructs.

Re: Away from Exceptions: Errors as Values

#105
post #103

In my opinion, the difference between errors as return values and checked exceptions is merely one of syntactic sugar. Both are conceptually a sum type together with the regular return value, and the syntactic sugar for handling and/or propagating the error or exception is really a spectrum, not a dichotomy. I believe it would be useful to focus on the possible design choices within that spectrum, regardless of the u…

The main caveat is that oftentimes, checked exception handling doesn't compose well - see what kind of trouble Java gives you, for example. Recent articles I've read on effect modeling languages seems to give a more uniform construct for bringing checked exceptions in line with other control constructs.

> see what kind of trouble Java gives you, for example.

I program a lot in Java, and the only trouble there is the lack of support for sum types and/or variadic type parameters in generics (i.e. to express functional interfaces that can throw an arbitrary number of checked exceptions, as a type parameter). That’s the only pain point for me and is something that could be fixed.

In fact, the interplay with control structures is exactly what I was referring to by syntactic sugar. Indeed it also concerns the type system.

But let’s talk about that, not about exceptions good/bad or error values good/bad. That’s too simplisitic.

Re: Away from Exceptions: Errors as Values

#106

>Only throw exceptions when something really bad has happened and the program must stop. For example: > the program cannot connect to its database; > the program cannot write output to disk because the disk is full; > the program was not started with valid configuration. I'd prefer Result over exceptions even in these cases. The only case where I think exceptions should be used is when the type system of the language…

Another thing to call out is that you also need to be precise in what the error is. Division by zero is indeed a grave error, but only when your code is not logically thought out - there should never have been any codepaths that divide by zero in the first place. So the error that your should report is whatever cause the denominator to be zero, not division by zero. That's almost entirely useless, and misleading.

Re: Away from Exceptions: Errors as Values

#107
post #68

Earlier quoted context omitted.

> You always have to somehow handle the case the file does not load successfully. In exception languages that handling might be implicit I.e. you don't have to handle it. Until you're polishing the program for a stable release, that is.

Right, in both approaches you can choose to handle the error by ignoring it and crashing. In "errors as values" languages you have to make that choice explicit by marking the line with `unwrap`. Saying that this requirement is "the single most unproductive mis-feature a language could have" is extreme hyperbole, no? Adding `unwrap`s during development to imitate implicit exceptions for fast prototyping takes no time…

Okay, if there's a simple way to mark some code as "compile this even if it's wrong", it's only a minor annoyance.

But the commenter I responded to seemed to me to be wishing for a language that explicitly disallows that. Maybe I misunderstood?

Re: Away from Exceptions: Errors as Values

#108

Earlier quoted context omitted.

> The compiler should force you to handle every exception in some way, or to check for it. This is the single most unproductive mis-feature a language could have for me. Programming is already a tedious excercise of wrangling your thoughts into an alien form the computer can understand. You want, on top of everything else, the computer to refuse to run your program at all, unless you explicitly handle every possible…

> You want, on top of everything else, the computer to refuse to run your program at all, unless you explicitly handle every possible edge case? Precisely! Even better - let the IDE suggest to you all of the possible exceptions and when you're feeling lazy or are hacking away at a prototype, either let it add a "throws SomeException" to the method signature and make it someone else's problem up the call chain, or jus…

If I had to write that kind of boilerplate every time I had an artistic inspiration, I'd never ship anything!

We are on far apart sides of a wide industry. I couldn't work productively in your dream language but hey, I'm happy we can have our different tools for our different needs. More power to us! :)

> let the IDE suggest to you all of the possible exceptions

So, programming without an IDE becomes untenable. I use a text editor. It feels like you're shifting language features into the IDE. What's the difference between the compiler doing it automatically vs the IDE doing it automatically?

Re: Away from Exceptions: Errors as Values

#109
post #93

Earlier quoted context omitted.

> Haskell, in many of its uses, cleans up the tediousness so thoroughly that the code written using errors as values can be almost indistinguishable from code written using exceptions, and yet, nevertheless, the errors are values and no exception machinery is being deployed. I don't have experience with Haskell, but I have mixed feelings about monadic error handling in Scala for precisely this reason. It goes to grea…

> This hasn't turned me off of monadic error handling, but it has made me think of it as FP's version of exceptions, rather than an upgrade. This reminds me a lot of Java's checked exceptions just with different window dressing. You move the failure mode type information from the exceptions list ("throws" clause) into the return type. Typed error return values is definitely an improvement in ergonomics over C-style e…

I've started writing a blog post that covers this topic, and once I started thinking clearly about it, I've found errors to be a really hard problem.

To even get out of static vs. dynamic typing, I've expressed the problem as "Given this piece of code, how can I know what types of errors will come out of it?", where I'm using a human, loosey-goosey sense of the word "type" here, rather than necessary a strict type. (If your language wants to answer that in terms of strict types, great, but I'm trying to answer it very generally across programming languages.) It turns out that from what I can see, the underlying problem is that "errors don't compose"; given a function f that returns errors X, Y, and Z and accepts another function f2 to call that may return other errors, it is really difficult to characterize f(f2) in practice. For a concrete, static f2 we can mostly at least imagine taking the union of the two (although even that can be an oversimplification; what if f calls f2 in such a way that one of the types of errors that f2 can produce is guaranteed not to happen, e.g., what if f2 could throw a null pointer exception but it can be easily statically proved that f never passes it one?), but once you let enough polymorphism into the mix to let f2 be an arbitrary closure of some type, all bets are off in terms of what f(f2) can produce in most languages.

This hurts statically-typed languages in that they can't create very strong types for these sorts of situations, but more generally, translating the theory up to practical experience regardless of the language being used, A: it's hard to program in an environment where you have enough polymorphism of some sort (OO, accepting closures, whatever) to have errors mix like this in your code and know what sort of errors may occur where and B: that's nearly everything because programming in an environment that lacks that polymorphism is not something we generally do voluntarily. (Embedded code not allowed to even allocate on the stack is this static, but we can't build everything that way.)

Amusingly, I think what saves us in the end is that to a first approximation, there is no such thing as error handling. All there is is logging something for a human and giving up. Obviously, to a second approximation there is a such thing as error handling. I've got plenty of it. But honestly, it's pretty rare by percentage. Most errors result in a log message and some level of failed task. Fortunately, with some work, we can usually get our systems fed enough good data that we can do things without errors, such that the ones that do occur end up almost always being essentially correctly handled by screaming and dying. If programming actually required us to handle errors, like, in some intelligent manner all the time, we'd have a lot fewer programs in the world!

Re: Away from Exceptions: Errors as Values

#110

>Only throw exceptions when something really bad has happened and the program must stop. For example: > the program cannot connect to its database; > the program cannot write output to disk because the disk is full; > the program was not started with valid configuration. I'd prefer Result over exceptions even in these cases. The only case where I think exceptions should be used is when the type system of the language…

Another thing to call out is that you also need to be precise in what the error is. Division by zero is indeed a grave error, but only when your code is not logically thought out - there should never have been any codepaths that divide by zero in the first place. So the error that your should report is whatever cause the denominator to be zero, not division by zero. That's almost entirely useless, and misleading.

Yes, this. Parse all input at the application boundaries and reject invalid input. For division by zero, the code path that leads to that should encode the input as a number greater than zero. But this may be clunky to do, depending on the language you're working with.
Post reply on HN