Live data from Hacker News

Errors and Exceptions

giantfublog.wordpress.com

31–40 of 55 posts

Re: Errors and Exceptions

#31
post #26
post #6

Earlier quoted context omitted.

Go's preference for 3 over 4 is a big chunk of the reason I've never used it in anger. Rust's situation is slightly different; code typically uses a monad (Result ) for error propagation, which is mostly isomorphic to exception throwing, but more verbose. And Rust has macros to cope with some of the verbosity.

It may be isomorphic to exception handling, but it promotes a meaningfully-different human style. The exception-based isomorphism to the usual returned-error handling style would look something like: try: a_single_statement_here() except: if exception.type == "A": #yada yada elsif exception.type == "B": #yada yada else: raise Exceptions by contrast encourage putting a lot of statements together, and much less frequen…

I think it's more a product of application complexity. The deeper the waters below you, the less you can meaningfully do with errors triggered by our actions. The closer you are to the hard boundary with the outside world, the more easily you can enumerate error conditions and be complete.

A server connected to a network socket is very close to the wire protocol, and the potential error states are fairly well known and documented. Head up the abstraction stack deep into business logic and add a dose of third-party libraries and some high-level application structure (i.e. indirection and abstraction boundaries) and the world gets a lot more fuzzy.

I concur re GUI; I think this comes from the complexity of the GUI framework - "don't call us, we'll call you" - GUI code is typically a thin seam between two sets of framework call stacks.

I wouldn't advocate exceptions for a kernel, or a database server. The more code between you and the hardware, and the more code that you use that you don't control, the more I feel the need for exceptions to communicate error information. The higher up the stack you go, it's more likely that human intervention will be required to solve the problem - in a server situation, log the error and tell the user that support has been informed about the problem. Etc.

Re: Errors and Exceptions

#32
post #30

I find anything but a Result ADT to be a poor solution. Errors shouldn't have language-level support (exceptions), and error codes are just a poor implementation of a result ADT - they cannot be enforced by the type system. Result is a monad, so it doesn't require an alternative code path. It's the cleanest and safest. "Exceptions" should always be for fatal, irrecoverable errors (array index out of bounds). I'm okay…

How about using an algebraic effect system? That's sort of a general purpose alternative to monads that also doesn't require an extra code path, and adds a bit of theoretical niceness. For example, if your code can throw two different exceptions, composing two monads is order-sensitive (because the monad interface is in some sense too general and forgets too much), while algebraic effects always commute with each other.

Re: Errors and Exceptions

#33
post #3

Well, that's one position on the subject. The Rust people prefer option 3 over option 4. Go takes the same approach. Python prefers exceptions, and the exception hierarchy puts (almost) all the exceptions which result from external problems under EnvironmentError. Much of the trouble with error returns comes from the strange C convention that functions with return values can be called as if they didn't return a value…

I like to say "Rust has exceptions, you just can't catch them". If you use panic!() in Rust, it will unwind the stack, just like exceptions do. Destructors correspond to finally blocks in Java.

Re: Errors and Exceptions

#35
post #30

I find anything but a Result ADT to be a poor solution. Errors shouldn't have language-level support (exceptions), and error codes are just a poor implementation of a result ADT - they cannot be enforced by the type system. Result is a monad, so it doesn't require an alternative code path. It's the cleanest and safest. "Exceptions" should always be for fatal, irrecoverable errors (array index out of bounds). I'm okay…

How about using an algebraic effect system? That's sort of a general purpose alternative to monads that also doesn't require an extra code path, and adds a bit of theoretical niceness. For example, if your code can throw two different exceptions, composing two monads is order-sensitive (because the monad interface is in some sense too general and forgets too much), while algebraic effects always commute with each oth…

True. I could rephrase as "the possibility of errors is just another type, and is best treated as such"?

Re: Errors and Exceptions

#36
post #27
post #20

Earlier quoted context omitted.

Don't really agree, this seems too general? It's a matter of preference, what level you're working on and of what is possible. For example you're writing C++ which is going to be wrapped in a C-style api then yes you are going to need those error codes. If you are writing an application using some C++ api with well-defined exceptions then using them might lead to much nicer code. Consider some function in main() whic…

Go read one of Herb Sutter's "Exceptional C++" books. Okay, just read the first one. If you don't close the book and reflect, "I am never writing this shit," Herb didn't do his job. Writing exception-safe code in C++ is very hard. Depending on another module's authors to get their C++ exception handling right is the road to madness. In the example above, the FAILED clauses clearly handle all the failures. In the exce…

A "FileNotFound" exception from an open() function is just colossally stupid.

Yes it is, and my example wasn't about such functionality anyway

Re: Errors and Exceptions

#37
post #34

It's a bit sad (and an indictment to "modern" languages) that option 5 "Conditions" (resumable/non-unwinding exceptions) is not mentioned.

How is that different to option 2?

It has nothing in common with 2. It's an extension of 4 where the eventual (dynamically scoped) handler is executed on the non-unwound stack and may unwind the stack (same as an exception), perform an action on the non-unwound system (e.g. resume, resume with a value provided, resume with a restart specified, repeat, etc…) or dynamically opt to resume looking up handlers.

Re: Errors and Exceptions

#38
post #33
post #3

Well, that's one position on the subject. The Rust people prefer option 3 over option 4. Go takes the same approach. Python prefers exceptions, and the exception hierarchy puts (almost) all the exceptions which result from external problems under EnvironmentError. Much of the trouble with error returns comes from the strange C convention that functions with return values can be called as if they didn't return a value…

I like to say "Rust has exceptions, you just can't catch them". If you use panic!() in Rust, it will unwind the stack, just like exceptions do. Destructors correspond to finally blocks in Java.

Also worth noting that "things come unglued" applies to panics in destructors in Rust the way it applies to exceptions in destructors in C++.

Re: Errors and Exceptions

#39
post #27
post #20

Earlier quoted context omitted.

Don't really agree, this seems too general? It's a matter of preference, what level you're working on and of what is possible. For example you're writing C++ which is going to be wrapped in a C-style api then yes you are going to need those error codes. If you are writing an application using some C++ api with well-defined exceptions then using them might lead to much nicer code. Consider some function in main() whic…

Go read one of Herb Sutter's "Exceptional C++" books. Okay, just read the first one. If you don't close the book and reflect, "I am never writing this shit," Herb didn't do his job. Writing exception-safe code in C++ is very hard. Depending on another module's authors to get their C++ exception handling right is the road to madness. In the example above, the FAILED clauses clearly handle all the failures. In the exce…

If you don't close the book and reflect, "I am never writing this shit," Herb didn't do his job.

The same Herb who said Prefer to Use Exceptions to Report Errors? (which makes no sense btw, just like saying to never ever use them)

Depending on another module's authors to get their C++ exception handling right is the road to madness.

Wait, so we should just forget about STL/boost/.. and reinvent all wheels? Or maybe just all code - depending on another module's authors to get their error code handling right, as in not ignoring all of them, is also madness.

You add a new exception that seems reasonable, but you wind up changing a hundred places in your source code (... and your callers! hope you have good customer support).

That is just not how proper code using exceptions gets written, ever. Anyone can make up similar horror stories for error codes (you are checking if HR is FILE_NOT_FOUND or FILE_NOT_ACCESSIBLE but suddenly the author changed the name into FOOFILE_NOT_FOUND and now you wind up changing a hundred places in your source code) Callers know what exceptions they can handle (if they don't the code is crap anyway) and handle those. If a caller wants to provide a strong guarantee the caller catches everything and nothing is forgotten. Everything else is truly exceptional and bubbles up, eventually to the top like all most C++ systems you saw.

Re: Errors and Exceptions

#40
post #31
post #26

Earlier quoted context omitted.

It may be isomorphic to exception handling, but it promotes a meaningfully-different human style. The exception-based isomorphism to the usual returned-error handling style would look something like: try: a_single_statement_here() except: if exception.type == "A": #yada yada elsif exception.type == "B": #yada yada else: raise Exceptions by contrast encourage putting a lot of statements together, and much less frequen…

I think it's more a product of application complexity. The deeper the waters below you, the less you can meaningfully do with errors triggered by our actions. The closer you are to the hard boundary with the outside world, the more easily you can enumerate error conditions and be complete. A server connected to a network socket is very close to the wire protocol, and the potential error states are fairly well known a…

I like this distinction. It makes sense to me based on my experience, and as an extra bonus, explains the passion on both sides of the debate. If you don't have both kinds of experience, obviously one is strictly better than the other, it just varies as to which one that is. Both sides look at the other in horror and ask "Why are you trying to ruin my programs?", with good justification.
Post reply on HN