5 ms·
I strongly do not like try/catch. Just to list the limitations of exceptions that come to mind, - try/catch exceptions obscure what things can throw errors. Ju
by jchw 1y ago
I strongly do not like try/catch. Just to list the limitations of exceptions that come to mind,
- try/catch exceptions obscure what things can throw errors. Just looking at a function body, you can't see what parts of the functions could throw errors.
- Relatedly, try/catch exceptions can unwind multiple stack frames at once, sometimes creating tricky, obscure control flow. Stack unwinding can be useful, especially if you really do want to traverse an arbitrary number of stack frames (e.g. to pass an error up in a parser or interpreter, or for error cases you really don't want to think about handling as part of the normal code flow) but it's tricky enough that it's undesirable for ordinary error handling.
- I think most errors, like I/O errors, are fairly normal occurrences, i.e. all code should be written with handling I/O errors in mind; this is not a good use case for this type of error handling mechanism—you might want to pass the error up the stack, but it's useful to be confronted with that decision each time! With exceptions, it might be hard to even know whether a given function call might throw an I/O error. Function calls that are fallible are not distinguishable from function calls that are infallible.
- This is also a downside of Go's current error handling; with try/catch exceptions you can't usually tell what exceptions a function could throw. (Java has checked exceptions, but everyone hates them. The same problem doesn't happen for enum error types in Rust Result, people generally like this.)
(...But that's certainly not all.)
Speaking just in terms of language design, I feel that Rust Result, C++ std::expected, etc. are all going in the right direction. Even Go just having errors be regular values is still better in my opinion.
(Still, traditional exceptions have been proposed too, of course, but it wasn't a mistake to not have exceptions in Go, it was intentional.)
- deleted 1y ago[deleted]
- 9rx 1y ago> but it wasn't a mistake to not have exceptions in Go, it was intentional. It does have them, though, and always has. Use is even found in the standard library (e.g. encoding/json). They are just not commonly used for this because of the inherit problems with using them in this way as you have already mentioned. But you can. It is a perfectly valid approach where the tradeoffs are acceptable. But, who knows what the future holds? Ruby in the early days also held the same preference for error values over exceptions... Until Ruby on Rails came along and shifted the prevailing attitude. Perhaps Go will someday have its "Ruby on Rails" moment too.
- jchw 1y agoDisagree. We could argue what counts as "exceptions", the jargon goes places (e.g. CPU exceptions are nothing to do with "exception handling" for example.) I'd argue that in the modern day programming language exception handling is the type where you have structured control flow dedicated to just the exception handling. Go has stack unwinding with panic and recover, but those are just normal functions, there's no try, no catch, and no throw, and no equivalent to any of those. C also has setjmp/longjmp which can be used in similar ways, but I wouldn't call that exception handling either. But I think we'll have to agree to disagree on that one, since there's little to be gained from a long debate about what jargon either does or should subjectively mean. Just trying to explain where I'm coming from.
- 9rx 1y ago> We could argue what counts as "exceptions" What is there to debate? An exception, by every definition I have ever encountered, is a data structure that contains runtime information (e.g. a stack trace) to stand in for a compiler error where the compiler was not sufficiently capable of determining the fault at compile time. It couldn't possibly mean anything else in reason. Of course, we're really talking about "exception handlers", not "exceptions". > there's no try, no catch, and no throw, and no equivalent to any of those. There can be in name and reasonable equivalency: https://go.dev/play/p/RrO1OrzIPNe https://go.dev/play/p/RrO1OrzIPNe I'm not sure what it buys you, though. You haven't functionally changed anything. For this reason, I'm not convinced by the signifaince of syntax. Think about it. Go could easily provide syntax sugar that replaces `try { throw() } catch (err) {}` with `try(func() { throw() }).catch(func(err) {})`. That would truly satisfy your requirements in every way. But what, specially, in that simple search and replace operation says "exceptions" (meaning exception handlers)? > C also has setjmp/longjmp which can be used in similar ways, but I wouldn't call that exception handling either. Agreed. You could conceivably craft your own exceptions to carry through the use of setjmp/longjmp, but that wouldn't be a language feature. However, Go does have an exception structure as a built-in.
- jchw 1y agoThe Wikipedia article about Exception handling[1] does a better job discussing the history and background IMO. Also, obviously when we say "exceptions" in a programming language, we're definitely talking about "exception handling", the word is omitted because it's obvious on context. I'd argue that one's on you if you thought otherwise. (If we're just talking about an "exception", well the Go error object is an "exception", but it's pretty obvious you're not merely talking about that.) True to my word, I won't argue over the definition itself. [1]: https://en.wikipedia.org/wiki/Exception_handling_(programming) https://en.wikipedia.org/wiki/Exception_handling_(programmin... P.S.: > There can be in name and reasonable equivalency: https://go.dev/play/p/RrO1OrzIPNe https://go.dev/play/p/RrO1OrzIPNe I'm not sure what it buys you, though. You haven't functionally changed anything. For this reason, I'm not convinced by the signifaince of syntax. To me this is no different than implementing "exception handling" with setjmp/longjmp, just less work to do. For example, Go doesn't have pattern matching; implementing an equivalent feature with closures does not make this any less true.