Live data from Hacker News

Away from Exceptions: Errors as Values

humanlytyped.hashnode.dev

51–60 of 145 posts

Re: Away from Exceptions: Errors as Values

#51
post #45
post #40

Earlier quoted context omitted.

It's the correct design only if we assume that the design space didn't allow for a different return type. Kotlin for example offers toIntOrNull ( https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/to-... ) as an alternative.

A null doesn't contain any information about what went wrong and unless you religiously check your objects for null values at every turn you just turned a clear stack trace into a search for waldo at the international waldo impersonators meetup.

Note that in Kotlin the return type is `Int?`, not `Int`. You can't forget to check for null because the compiler enforces it.

To your first point: Another example in the design space would be Rust which works very similarly to Kotlin but returns more information in the failure case.

Re: Away from Exceptions: Errors as Values

#52
post #20
post #19

No, I don't want to wrap every single statement of my program in its own if-block, thank you very much.

Rust solves this issue by having a ? operator to bubble up Errors. Before that there was the try! macro with the same semantics. That cuts the boilerplate to a minimum while having a well defined and explicit control flow. I agree that if you had to write the ifs by hand it would be a pita. Looking at you, Go.

In the end that is equivalent to bubble up exceptions when thy are of the unchecked type.

Re: Away from Exceptions: Errors as Values

#53
post #9

Personally, i really like having multiple return values, since being able to give a function multiple inputs but only being able to return a single thing always felt weird - if your require any metadata in a language like Java, then you'd have to come up with wrapper objects and so on. That said, i really dislike the following from the article: if (error) { // you can handle the error as you see fit // you can add mo…

> 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 edge case?

I get that some people are engineers with rigid requirements. I'm an artist - I sculpt the program to produce output I'm not entirely clear on. I'm trying to make the computer to interesting, unexpected things.

Say I'm making a game. I wanna load a character sprite from an image file and draw it on the screen. Do I really need to handle all the possible ways that file could fail to load right now, before even seeing a preview of what it should look like? Hell no!

It's like having an assistant who refuses to do anything unless you specify everything! Hey assistant, get me a coffee. "I refuse to get you a coffee because you didn't specify what I should do in case the coffee machine is broken." Aargh!

Re: Away from Exceptions: Errors as Values

#54
post #20

Earlier quoted context omitted.

Rust solves this issue by having a ? operator to bubble up Errors. Before that there was the try! macro with the same semantics. That cuts the boilerplate to a minimum while having a well defined and explicit control flow. I agree that if you had to write the ifs by hand it would be a pita. Looking at you, Go.

In the end that is equivalent to bubble up exceptions when thy are of the unchecked type.

I don't think that's true because if I understand it correctly, the return type of functions which can possibly throw unchecked exceptions would not indicate that they can throw or what they can throw. On the other hand, with the "errors as values" approach (including "bubbling up" operators like `?`), you can tell exactly from the function's return type if an error can be returned and if so what the set of possible errors is.

Did you maybe mean "the checked type"? In that case I still think it's not equivalent because at least in Rust you can automatically transform the error while it bubbles up, while I don't know of a language with checked exceptions that lets you transform the exception while unwinding (short of manually catching, transforming, and re-throwing).

Re: Away from Exceptions: Errors as Values

#55
post #5

Interesting that the author used joi as the example, when the io-ts validation library fully embraces the functional world with success/fail return value from validation. Realistically, the advantage throw has is that if you have a single error handler in a function it avoids the "go" problem of tons of lines of error handling. The other side is that error handling becomes "optional".

Personally, I gave up on io-ts due to FP complications, and use suretype instead. In my case, where I was parsing HTTP request body, it just simplifies the code, if I can call a single function, get back validated object of the expected type, or throw an exception if there is a problem. Global request handler takes care of catching validation exception, and returning back user friendly error on what field(s) failed v…

suretype looks interesting, will have to do a bit of review there. One of the things we're now doing is using io-ts for both encoding and decoding types. Internally we're more JSON than gRPC so we're using it to provide a standardized way to move data between components.

Re: Away from Exceptions: Errors as Values

#56
post #9

Personally, i really like having multiple return values, since being able to give a function multiple inputs but only being able to return a single thing always felt weird - if your require any metadata in a language like Java, then you'd have to come up with wrapper objects and so on. That said, i really dislike the following from the article: if (error) { // you can handle the error as you see fit // you can add mo…

> 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…

I don't quite follow. You always have to somehow handle the case the file does not load successfully. In exception languages that handling might be implicit (raise an exception and crash your program) and in "errors as values" languages you at least have to acknowledge that it could go wrong with something like `image.unwrap()` (which turns it into a program aborting panic).

Re: Away from Exceptions: Errors as Values

#57
post #6

It's good to see so much focus on errors. They are essential when trying to build resilient systems. But our approaches are still very immature. First, to make it clear, this article appropriately points out that exceptions are still necessary and relevant. I disagree with some of the use-cases given, but it's important to recognize that exceptions should still exist in programming languages. Joe Duffy's article abou…

We can't forget that "error handling" is a civilization-level hard problem. Do you set up support structures to catch them when shit hits the fan, or do do you preventatively and excruciatingly suppress them?

It's as much a theoretical problem of what errors are as a practical problem in how to represent these intricate models ergonomically, and what will be sacrificed. In some sense it's an almost moral question!

Re: Away from Exceptions: Errors as Values

#58
post #34
post #30

I'm firmly in the camp that believes that exceptions are a false economy. The post links to an "Exception Smells" post that doesn't mention one of my pet peeves: exceptions as control flow. For example, Java's parseInt [1] throws a NumberFormatException if the string can't be parsed. IMHO this is terrible design. As a side note, checked exceptions are terrible design. I wrote C++ with Google's C++ dialect where excep…

> For example, Java's parseInt [1] throws a NumberFormatException if the string can't be parsed. IMHO this is terrible design. It's unergonomical design, but it's the _correct_ design: the method is declared to return an int, and it can't fulfill its promise: throwing an exception is the right thing to do.

But the mechanism is just wrong; Exceptions are heavyweight and should only trigger with unexpected issues, bugs that a developer wants to see a stacktrace for.

I mean in this case you could consider it developer error; a developer tried to parse an integer without first validating the input and checking if it COULD be parsed. But it's normalized to just "let it crash", instead of writing additional pre-check code.

With errors as values - like Go has normalized - instead of a big, expensive, potentially panicky exception, you just get a lightweight error option back. With the more functional approach of Either, you are basically forced to deal with the "but what if I can't parse it" code branch.

Re: Away from Exceptions: Errors as Values

#59
post #6

It's good to see so much focus on errors. They are essential when trying to build resilient systems. But our approaches are still very immature. First, to make it clear, this article appropriately points out that exceptions are still necessary and relevant. I disagree with some of the use-cases given, but it's important to recognize that exceptions should still exist in programming languages. Joe Duffy's article abou…

> when you start introducing other factors like how to report the errors publicly to a non-technical user, maybe in different languages, or whether to log it or send it who knows where, whether to trace or not, how, how to deal with duplicates or similar errors...

I’ve tried searching for articles that talk about people deal with this in the context of web apps but have found it difficult to find content. It’s a tricky topic to google. Most of what I find are (usually content marketing) articles about how to log errors and/or how to send them to some service.

I’ll give you an example that is admittedly a little paranoid. Take a switch case statement where you branch of an enum-like value, meaning there’s a set of known values you expect. What do you do for the default case? In theory you don’t need a default case because you “know” the switch won’t hit it, but it’s weird to me to write code that has no logic to handle a possible scenario even if it’s highly unlikely. What if there’s a bug in the code or the enum-like value changes to contain a new value or some weird edge case? The point being in JavaScript there’s no way to be 100% sure that the switch condition will not contain an unexpected value.

Given how unlikely this scenario is though, how do you deal with it? (I realize some of it depends on where in the application’s code this is happening in). You don’t want to throw an exception and break the app. Or you can but you’d want to catch at some point. Do you log the event in the backend and create an alert so that you know a user ran into a weird edge case or bug? Do you write it out to the console in case you get a customer support call so that you can identify the issue? Is it a bad idea to write out errors like that to the console?

I’m sure these are questions that most mature apps have had to answer, but I haven’t been able to find what people consider best practices for these types of situations. If anyone knows of good resources I’d love to read them.

Re: Away from Exceptions: Errors as Values

#60
post #17

Earlier quoted context omitted.

Tbh, to me the first one is easier to read. There's less jumping. But both are terrible. It should just be a bunch of if (...) { ... } else if (...) { ... } else { ...} etc. with no mutation of variables (what are all those v += 1 for?).

Those v += 1 implement the exact specification given above: 1. Parse an integer N from a string. 2. If N is NaN, fail with an error. Otherwise, increment N by 1. 3. If N is > 3, fail with an error. Otherwise, increment N by 1. 4. If steps 1-3 failed, set N to 3. 5. Increment N by 1. Those are quite strange requirements, and the resulting second code looks strange too, but... it faithfully and obviously correctly repr…

It's the kinda assignment where you should look at a higher level - what does it do? What is N used for? What are the possible inputs?

I mean if you start with a set of tests instead of pseudocode written down in text you could probably write something smarter.

Post reply on HN