5 ms·
Strongly disagree with your conclusions. There are definitely arguments against exceptions in cases where you always want to understand and handle failure condi
by scaredginger 4y ago
Strongly disagree with your conclusions. There are definitely arguments against exceptions in cases where you always want to understand and handle failure conditions, but Go has not got it right. The need to manually handle returned errors adds too much noise to the code, and in my subjective opinion, the code looks far cleaner when you skip the error checking. Additionally, if the manual error handling code isn't written, failures will generally be silent.
My example of error handling done right would be Rust's `Result<T, E>`. There's the ? operator for syntactic sugar to propagate errors upwards. Otherwise, to skip error handling, you're generally forced to unwrap() the result, which is a noticeable smell, and it will crash your program if the operation failed (as opposed to continuing despite the error). Finally, it maintains the error code advantage of keeping the control flow explicit and visible.
We've had decades of erroneous error handling from C codebases to learn from. Frankly, it's insane that we have a modern language that still uses error codes in essentially the same way.
- grawp 4y agoThis!
- kortex 4y agoRust absolutely nails the error handling department. Python style exceptions, pros: - rapidly drop through layers when needed to get to a stable entry point - easy to add errors deep in the stack Cons: - basically a limited GOTO, therefore side-effect, therefore not composable - no way to know if what you are calling is gonna throw on you Java pros: - have to declare exception types, so at least you know what you are dealing with Cons: - you have to always declare exception types, which can be tedious - same non-composable GOTO-lite behavior - adding an exception deep in the stack is tricky Go pros: - errors are values, therefore typed and technically composable - no surprises Cons: - tedious, verbose - can't rapidly unwind unless you panic - rarely composable in practice, requires manual unwrapping in most cases - adding an exception deep in the stack is tricky Rust Result pros: - strongly, statically typed - no surprises - higly composable - can map over it - rapidly unwind in a composable way with ? operator Cons: - adding an exception deep in the stack is tricky (but often amenable to programmatic refactoring) It's no wonder Rust is the SO most loved language 7 years running. Python actually has a Result type library which I really like, but it's been hard selling my team, and you really need buy in. But I'd give it a swing. https://pypi.org/project/result/ https://pypi.org/project/result/
- valenterry 4y agoSorry, but Rust's error handling is pretty clumsy compared to what you can do in some languages. Example: You write a function that calls 2 thirdparty libraries, both of which can fail. The typesystem in Rust is unable to express that the resulting error with be libAError or libBError. It is lacking anonymous union-types. Even if union-types have been added, you'd have to define one first and you'd have to use unsafe (at least from my understanding). This also impacts user-defined error-types of course, but it makes errorhandling when using other libraries very annoying. You always have to define new types and do wrapping. Another example: You have a function that calls 2 thirdparty libraries, both of which can fail. The two libraries have decided to use a customized error-type (and not the built-in one) because they need to carry around some extra context or need some other feature, but they still support everything that Result does but in a slightly. Now you need to manually convert those or learn the interface of this specific error-type, because there is no way to abstract over different "Result"-types. Why? Because Rust has no support for Higher Kinded Types, which would be needed to do so. There are more examples, but these are two that are immediately relevant to pretty everyone using Rust. And yes, Rust has done a lot of things right and especially better than C or C++. But when looking at it as someone who has used other high-level-languages I can say it definitely did not "absolutely nail" it.
- scaredginger 4y agoI agree with your first point, but it's worth noting that if someone is willing to pay a small runtime cost, they can just return a Box<dyn Error> or anyhow::Error in these cases ergonomically. Additionally, there is the thiserror crate which makes defining the union type you described trivial. Your second point is dubious; I've never seen any crate use anything other than Result. Afaik, it works everywhere and is so idiomatic that if somebody suggested reimplementing it, I'd immediately question their credentials.
- valenterry 4y ago> Box<dyn Error> or anyhow::Error in these cases ergonomically This comes at the cost of losing the precise information what error-types could occur and makes it harder to read the code compared to a language that can use its typesystem to model that. > Your second point is dubious; I've never seen any crate use anything other than Result. Maybe. And you also barely see something like Result being used in Python. Is that because Result is bad? No. It is because it is _hard_ to use Result in Python, both of the lack of ergonomy compared to e.g. exceptions and also because it's not standard and people will look at you funny. So why is pretty much everyone in Rust using Result? Because using your own error-type causes exactly the problems that I mentioned (and more). So no matter how you look at it - this is a shortcoming of Rust and hopefully something that the Rust team will improve in the future. (E.g. https://github.com/rust-lang/rfcs/issues/324 https://github.com/rust-lang/rfcs/issues/324)
- lenkite 4y agoAs the joke goes: Master Gorad asks his apprentice: "How many statements does it take to call a function in Go ? Apprentice replies puzzled "I can just call foo();" and I am done ? Master Gorad beats his student with a cane. No, you silly, it takes at-least 3 statements to make a function call in Go: r, err := foo(); if err != nil { return nil, err; }