Live data from Hacker News

Errors vs. exceptions in Go and C++ in 2020

dr-knz.net

61–70 of 73 posts

Re: Errors vs. exceptions in Go and C++ in 2020

#61
post #36
post #17

Earlier quoted context omitted.

> It's designed to force people to handle the damn error as near to the call as possible. If they wanted to "force people" they could use Optionals and really force them. This no more forcing than mandating checked exceptions -- the user can just return the err immediately, like in Java they can just add a throws and propagate for others to handle, or an empty try/catch and ignore it...

You have go-lint or other linters to enforce it. It’s a per-project choice.

It's either a "per-project/leave it to the linter" choice, or a "they did this in the language to force people to handle errors close to the source".

Can't have it both ways!

Re: Errors vs. exceptions in Go and C++ in 2020

#62
post #19

Earlier quoted context omitted.

What if we designed a system where there are two kinds of failures that can be returned? One where the caller is forced to address it by the compiler, and one that is transparent to the caller, but can be caught and addressed by anyone in the call stack (probably the top level)? And one could convert one type of failure to the other. So if you call a library function and it returns the force-you-to-address kind of er…

I think everyone has had this distinction on their mind when designing error handling in the past thirty years or so, it’s just that figuring out an ergonomic way to express it is quite hard. In some languages the distinction is between logic errors and runtime errors. In Java, checked and unchecked exceptions. In Go, err and panic. Rust also has Err() and panic!(). If you look at, say, the evolution of the “if” stat…

For sure. I didn't mean to imply that this isn't a legitimately hard problem for language designers. Just being a smart ass.

I do think that checked and unchecked exceptions are the right way. The issues that people have with Java's checked exceptions are mostly centered around Java's particular implementation of the concept. The biggest failure of which, IMO, is that you can't write an interface that is generic over the exception type. Also, wrapping in try {} catch {} finally {} is cumbersome. But Java is just cumbersome. In some expression-oriented language, it could be smooth. `try` could become an expression that returns a value. Or you could have syntax help like something Rust-ish: `val thing = fallible().finally { cleanUp() }?`.

That's the main reason, IMO, people don't complain quite as much about Rust's Result, which is very much like a checked exception mechanism in spirit. The only problem with the Rust approach is that you have an extra if-statement on every single call to a fallible function, to unwrap the success/failure. If it used exceptions, the happy paths would (sometimes) be more optimized, if I understand correctly.

But some things just can't be fixed at the language level. You have to craft good error types and messages. You have to think through your happy paths as well as your sad paths. I like when languages force you to think about failure. I don't like when languages only have unchecked exceptions for all kinds of failures.

Re: Errors vs. exceptions in Go and C++ in 2020

#64

I think they missed the point of Go's convention. It's designed to force people to handle the damn error as near to the call as possible. I've seen way too many programs with a single exception handler right at the base of the program, that just goes "whoops, something bad happened, bye!". I've even seen this anti-pattern used with Go's panic-recover mechanism. It's an interesting find though, that the actual perform…

The point of Go's convention isn't really relevant to the question of its relative cost compared to exceptions, is it? I don't see that they so much missed it as didn't evaluate it.

How dare you remain on topic.

Re: Errors vs. exceptions in Go and C++ in 2020

#65

Earlier quoted context omitted.

Well there is a language that lets you decide how to handle errors separately from the code that actually handles them (eg separating the code that says “please retry” from the code that retries). That language is Common Lisp. But error handling in it is still a pain. The one advantage it has over most exception systems in my opinion is that the equivalent of try-finally is much more common than try-catch. With excep…

Please stop typing. I don't need another reason to learn LISP! I've been successfully stopping myself from entering that rabbit hole for years in order to prevent yet another "hey I should rewrite all my projects in LISP!". Also looking at you, Rust.

Well I was suggesting that the feature doesn’t really turn out that well in CL, so it’s not a great reason. That said, I’ll happily argue that your language should have something like unwind-protect as an easy to use concept before getting something like exceptions.

A big issue with exceptions lately is that they integrate terribly with async style code because they can’t simply unwind past where a promise was created and a promise can be raised to multiple times. The other issue is that they are so pervasively nonlocal that typical code can’t know what might be raised (or what restarts might be available)

Re: Errors vs. exceptions in Go and C++ in 2020

#66

Earlier quoted context omitted.

> And having the power to ignore the convention is good, too. Mistakenly not handling errors is not "the power to ignore the convention", it's "the language is half assed". "The power to ignore conventions" is being allowed but having to explicitly ignore the error, aka that the second and third cases trigger errors, and that you'd have to write: y, _ := fmt.Println("Bar") println(y) _, _ = fmt.Println("Qux") (also n…

The language allows you to ignore the convention. There is a plethora of tools available to allow you to detect if you did that when you didn't mean to. It's just that the compiler doesn't enforce it.

Given that Go also supports binary packages, those tools won't help much there.

Re: Errors vs. exceptions in Go and C++ in 2020

#67
post #61
post #36

Earlier quoted context omitted.

You have go-lint or other linters to enforce it. It’s a per-project choice.

It's either a "per-project/leave it to the linter" choice, or a "they did this in the language to force people to handle errors close to the source". Can't have it both ways!

The “force” is the part that is incorrect, or at least misleading. The go approach is to encourage people to handle it at the call site, and you can use extra tooling to enforce it.

Once you get that point, there is no contradiction.

Re: Errors vs. exceptions in Go and C++ in 2020

#68
post #29

The one item I'd really contend is where it says it "makes it easier to ... maintain over time". That might be true for smaller code bases (tracking down exceptions generated from libraries called from libraries, fun!), or code bases where you don't use closed external libraries (that can generate unknowable exceptions), or you use only synchronous code (because asynchronous exceptions wind up jumping to fishkill, we…

This was clarified in the conf talk [1] [2]: error returns should be used at API boundaries, and panic-driven handling only "within" a component. [1] https://www.youtube.com/watch?v=inrqE0Grgk0&t=15126s [2] https://docs.google.com/presentation/d/1WVu4O-ax7punUC2V_XgT...

For simplicities sake I just use error returns everywhere and then you have a consistent error handling abstraction (and one that scales to boot).

Re: Errors vs. exceptions in Go and C++ in 2020

#69

Earlier quoted context omitted.

Please stop typing. I don't need another reason to learn LISP! I've been successfully stopping myself from entering that rabbit hole for years in order to prevent yet another "hey I should rewrite all my projects in LISP!". Also looking at you, Rust.

Well I was suggesting that the feature doesn’t really turn out that well in CL, so it’s not a great reason. That said, I’ll happily argue that your language should have something like unwind-protect as an easy to use concept before getting something like exceptions. A big issue with exceptions lately is that they integrate terribly with async style code because they can’t simply unwind past where a promise was create…

I agree. I've been implementing the Event Stream architecture recently in Go and dealing within unwinding errors is a pain in the arse. Do I just repeat the event and hope it works next time? Is there something wrong with the event that means it'll never work? Do I restart the database server because that's what's wrong?

There's this saying that the AA has: "wherever you go, there you are". It's about "doing a geographic" - thinking that moving city/country/continent will change your circumstances and therefore change you. It's false, because no matter where we move we're still the same person so we'll still face the same problems. Wherever we go, there we are.

I think programmers have the same dynamic - if I change my language, I won't make the same errors as I'm making here. Somehow this new language will make me a better programmer because x or y.

I find this difficult to resist. But I also realise its falsehood - I will not be a better programmer in Rust or Lisp than I am in Go. In fact, I have a much better chance of being a better programmer if I drill down in Go and unlearn some problems and relearn some patterns and generally stop learning syntax and start learning deep shit.

Go has a convention on error handling. It may not be ideal. But it's there for a reason, and while we can argue with the reason, it's a valid reason. As a Go programmer, I can fight it and basically reject the language, or I can adopt it and get deeper. I choose that.

But that doesn't mean I don't dream of how much better my life would be if I chose Rust or LISP instead. And yes, I know that all languages have their problems, and a year after learning LISP I wouldn't be writing some infuriated blog post on how EMACS does this weird shit that takes 30s to resolve on a remote server. But don't we all dream of that promised land?

Re: Errors vs. exceptions in Go and C++ in 2020

#70
post #19
post #10

Earlier quoted context omitted.

>It's designed to force people to handle the damn error as near to the call as possible. But that is sometimes the wrong design. If you have functions A() --> call B() --> call C() ... and C() has an error because of a memory allocation failure or a network connection being down, sometimes the best context to handle that error is the outermost function A() and not C(). That's why some programmers don't like copypasti…

What if we designed a system where there are two kinds of failures that can be returned? One where the caller is forced to address it by the compiler, and one that is transparent to the caller, but can be caught and addressed by anyone in the call stack (probably the top level)? And one could convert one type of failure to the other. So if you call a library function and it returns the force-you-to-address kind of er…

IMO the only way for this to actually work is to have the language/compiler force "exceptions" to be part of function signature. I am not aware of any mainstream language which does this.

Without compiler support ANY call can end up throwing an exception and thus ANY call can end up not returning (jumping straight to handler higher up in the call stack).

Post reply on HN