I also strongly suspect that in the general case, you're not going to see more than one handler at the top of the function. It will be a rare function that has a dozen of them in there, and either A: it needs to be refactored, and while I'm sorta sympathetic to the idea that we shouldn't hand programmers too much rope to hang themselves with, that argument has the problem that you end up removing pretty much everything from the language or B: it's the core inner complicated loop of a program and it just needs that much complexity, which still doesn't mean it's going to show up in every function.
It's not that dissimilar from one function getting nested four layers deep in try/catch statements; yes, it would be a bit confusing to disentangle what that would actually do (moreso, since the catch statements are conditional but the handlers are not), but the answer is simply not to do that unless you really need to, and thwack any coworker that tries it on the wrist during code review. It is impossible to make a Turing-complete language in which I can't express something incomprehensible in.
(especially in Rust now that is has been made convenient with the ? operator and the failure crate which provides backtraces and wrapping).
I would prefer an extension of the `check` syntax instead of the standalone handle blocks.
For example:
check someFn()
|> if err : = someFn(); err != nil { return err; }
check someFn() handle {
panic(err)
}
|> if err := someFn(); err != nil { panic(err) }
So handle blocks are only for an individual expression.This way the control flow is still linear and explicit (ignoring defer...) but the syntax is much more convenient.
The point of 'handle' is that it applies to ALL FUTURE calls to check in the function body. If we're going to consider alternatives we need to at least address the authors perspective (even if just to deny it).
I guess the proposal's central idea is that once you see an error, error handling is going to be focused on rolling back various stages of partial progress. It's the complement to how 'defer' incrementally accumulates guaranteed execution in the face of either a return or unwind.
That said, how common this issues is and if it's worth optimizing for is more up to debate. It would be nice to see a lexicon of well handled errors; for 90% most of what I do log and unwind is sufficient.
What do I know -- I actually like Java style checked/unchecked exceptions and think they'd work well for Go as the parent said. For what I'm up to it's often sufficient to just not leak partial progress via side effects. All the partial progress just evaporates into the GC. 'finally' or 'defer' takes care of most of the .close() or .unlock() calls.
The change I'd make to Java-style exceptions if I had a time machine is to make all exceptions types unchecked. Instead of looking at exception's types at all, enforce checks only if a function has 'throws' declared.
Calls to 'void close() throws IOException' would still need to be checked but a function containing 'throws new IOException()' isn't forced to declare throws or catch it. This lets API's be explicit and force due diligence but lets user code intelligently allow unwinding until it's caught at the appropriate level. Some sugar for code to acknowledge the checked error and unwind could be nice I suppose.
I think fattening out Go's err's to full exceptions with stacktraces is more pressing personally.
CLU never went beyond university projects, and Modula-3 research died after the labs went through several acquisitions, DEC => Compaq => HP.
As for C++, checked exceptions never gained much support, because their semantics are different from what Java adopted. Throwing an exception that isn't part of the list terminates the application and there were some other issues with them, so now as of C++17 they have been dropped from the standard after being deprecated in the previous ones.
In general there are two big communities in C++, the ones that turn everything on (RTTI, exceptions, STL, Boost...) and those that rather use C but get to use a C++ compiler instead.
So no we don't hate exceptions, it just depends on which side of the fence one is.
I think just using try/finally/except would be a step up over what has been proposed.
This could make grasping the control flow in a function very cumbersome.
Everything else looks like a very good change to me and would draw me back in to Go.