Live data from Hacker News

Away from Exceptions: Errors as Values

humanlytyped.hashnode.dev

11–20 of 145 posts

Re: Away from Exceptions: Errors as Values

#11

It looks like GO developers never heard about "not repeat yourself", because after almost each call in GO, you write boilerplate error checking code... So you end up writing at least 2 times more lines of code.

You need all those error check code if you want to recover from error -- regardless of whether the error is coming from exception or value.

If you don't want to recover, just throw it down the stack that will exit, then can you do that without exception too. Just call error() function on error which will print the error and exit(-1). No need for per-line error checking in this case too.

Re: Away from Exceptions: Errors as Values

#12
post #8

"Which program is easier to read?" For me it was the second. Am I the only one ?

The first one reads like somebody just found out about functional programming and functors and tried to "improve" a straightforward bunch of if statements. I can read the second program without having read the plain English description, but I definitely would prefer to have the comment for the first one. I think even in languages like Haskell I would prefer (just sometimes) to read just a straight if-elseif-elseif-else.

The whole thing about the way it's written with throwing/catching is a red herring anyway, you should just replace those with a different choice of if's. If you're feeling super adventurous, you can instead replace them with goto's, which is kinda funny; it would actually simplify the code, how often do you see that?

Re: Away from Exceptions: Errors as Values

#13
post #8

"Which program is easier to read?" For me it was the second. Am I the only one ?

The second by far, especially if you just remove the else statements and let program flow continue naturally.

There are very good reasons to prefer Result over exceptions, but this example is not one.

Re: Away from Exceptions: Errors as Values

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

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

MRV is nice and useful, and “error as value” languages usually have ways to return multiple values (usually in the form of tuple), but it’s not proper and correct for error signalling, because the error and non-error are almost always exclusive.

In that case, using MRV means you have to synthesise values for the other case (which makes no sense and loses type safety), and that you can still access the “wrong” value of the pair.

> To me, that's an example of "opt in" error handling, which in my eyes should never be the case. The compiler should force you to handle every exception in some way, or to check for it.

That is what Rust does (including a clear warning if you drop a `Result` without interacting with it at all), although for convenience reasons (because it doesn’t have anonymous enums and / or polymorphic variants) the errors you get tend to be a superset of the effectively possible error set.

Though that’s also a factor of the underlying APIs, when you call into libc it can return pretty much any errno, the documentation may not be exhaustive, and the error set can change from system to system. Plus the error set varies depending on the request’s details (a dependency which again may or may not be well documented and evolving).

So when you call `open(2)`, you might assume a set of possible errors which is not “everything listed in errno(3) and then some”, but a wrapper probably can not outside of one that’s highly controlled and restricted (and even then it’s probably making assumptions it should not).

Re: Away from Exceptions: Errors as Values

#15
post #11

It looks like GO developers never heard about "not repeat yourself", because after almost each call in GO, you write boilerplate error checking code... So you end up writing at least 2 times more lines of code.

You need all those error check code if you want to recover from error -- regardless of whether the error is coming from exception or value. If you don't want to recover, just throw it down the stack that will exit, then can you do that without exception too. Just call error() function on error which will print the error and exit(-1). No need for per-line error checking in this case too.

You don't need it everywhere. With both exceptions and error monads, you can have a happy-flow path that mostly leaves out the boilerplate, and handle errors at a reasonable point.

Go forces you to add even if you want to defer handling to a later point.

Re: Away from Exceptions: Errors as Values

#16
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 validation.

Re: Away from Exceptions: Errors as Values

#17
post #8

"Which program is easier to read?" For me it was the second. Am I the only one ?

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?).

Re: Away from Exceptions: Errors as Values

#18
post #11

It looks like GO developers never heard about "not repeat yourself", because after almost each call in GO, you write boilerplate error checking code... So you end up writing at least 2 times more lines of code.

You need all those error check code if you want to recover from error -- regardless of whether the error is coming from exception or value. If you don't want to recover, just throw it down the stack that will exit, then can you do that without exception too. Just call error() function on error which will print the error and exit(-1). No need for per-line error checking in this case too.

Realistic options are not limited to "handle at every point of the call stack where an error is encountered" like Go and "end program execution as soon as an error is encountered". Most programs handle most errors by bubbling them up to some top-level event loop and presenting some variant of abort/retry/fail to users. Exceptions are tailor-made to cover this use-case.

Re: Away from Exceptions: Errors as Values

#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.

Post reply on HN