6 ms·
Explicit error handling is a choice and implicit error handling through exceptions is not necessarily a feature. Both have advantages and disadvantages, I’d sa
by devjab 2y ago
Explicit error handling is a choice and implicit error handling through exceptions is not necessarily a feature.
Both have advantages and disadvantages, I’d say the more “modern” approach actually the opposite to what you state here, and is in my opinion the way to Go (pun int intended), though it’s also how Haskell does it. You’ll find the same philosophy in Rust, Zig, Swift and others which all build on the previous decades of throwing exceptions and how terribly that scales in terms of maintainability. Even in the “old world” like with Java you have Kotlin which does both.
- saurik 2y agoI feel like you might be claiming that Haskell does something like Go does, but it actually doesn't: it supports monads, and so uses a monad to hide the error semantics entirely, providing exception-like syntax with automatic propagation. (I might misunderstand your use of "though", though? It could be that you were just noting in passing how Haskell disagrees with all of these supposedly-"modern" languages, and instead leaned into the sane happy path semantics, thanks to monads.) (edit: to be clear, though... I do not think exceptions solve this. I wrote a comment elsewhere on this thread about the semantics issue, but a few other people also wrote similar things while I was trying to type my overly-verbose reply ;P.)
- foldr 2y agoIt's worth noting that using Either style monadic error handling in IO code in Haskell is arguably an antipattern, as the Haskell runtime has its own built in exception handling (which actually works in a pretty conventional way). Some more info on this under 'Exceptions best practices ' here: https://tech.fpcomplete.com/haskell/tutorial/exceptions/ https://tech.fpcomplete.com/haskell/tutorial/exceptions/
- xg15 2y ago> Explicit error handling is a choice Yeah, it is - a bad choice IMO. I know the "if err != nil" pattern is spoken up as some sort of cultural idiosyncrasy of Go, similar to the whitespace formatting in python. But so far, I haven't seen any actual data (or even arguments) why it is superior to exceptions, or which inherent problems of exceptions it solves. (The classical example of "it makes control flow more obvious and makes it easier to ensure that mandatory finalizers are not skipped" was just disproven by this very article) So if there is more substantial criticism against exceptions than just FUD, I'd like to know it.
- randomdata 2y agoWell, let's assume Go did commonly make use of exception handlers for error cases: defer func() { if r := recover(); r != nil { fmt.Println("Failed to write file", r) } }() f := os.Create("file") defer f.Close() io.WriteString(f, "Hello, World!") Cool. You've solved one problem layer, perhaps. But if you look closely you'll notice that code still has a bug! So clearly exception handlers aren't enough. There might be some good ideas in using exception handling, but if you strictly limit its use to errors you end up half-assing it. Why not go all the way and find a solution that works in all cases?
- matt_craig 2y agoIs it that the defer() that recovers is called before the defer() that closes (and potentially fails)?
- randomdata 2y agoNo. No rearranging of the code would fix the problem.
- matt_craig 2y agoHmm. Checked the docs, looks like os.Create returns *File and err, but the sample doesn’t check that error.
- randomdata 2y agoos.Create, per the topic of discussion, is said to throw an error using the exception handling mechanism rather than return error. The exception handler checks the error. If you focus on errors, you’re going to never get it. The whole idea here is that the problem is bigger than errors.
- xg15 2y agoMaybe I'm too dumb too see the bug here. Do you mean the issue that if both WriteString() and Close() throw an exception, the one from WriteString() will be swallowed? That used to be a problem, but has been solved in more modern implementations using suppressed exception tracking.
- pjmlp 2y agoThe modern approach of Rust and Swift is basically checked exceptions done with a different painting.
- kaba0 2y agoGo is doing the worst thing possible. It is neither expressive enough for proper sum types, nor does it have expressions (that are analogous to sum types with good defaults and syntactic sugar). It is literally C’s shitty `errno` with syntactic sugar.
- kbolino 2y agoThere is a lot of space between the spooky magic quasi-global errno integer and proper sum types. Go's errors are not magic globals nor are they mere integers, even if they aren't sum types either.
- kaba0 2y agoHow is it not just errno? Especially that POSIX mandates errno to be thread-local, so not even that is a difference. Just because there is some syntactic sugar that converts it to a slightly more descriptive type than an int, doesn’t make it different.
- kbolino 2y agoThread locals are not lexically scoped, they are not stored as part of the function call stack, and mutating them is not expressed anywhere in the function signature. They are global variables with thread-local storage, not local variables. Go's error returns are not sum types, but they are product types. The return signature (T, error) indicates that two values will be returned essentially as a tuple by the function: one of type T and one of type error. Error-returning functions are pure functions (though they typically perform other, impure operations). There is no syntactic sugar (both for good and for ill). The type of errors is an ordinary interface, with a single method. Any type can implement that interface, including strings and structs and slices. Errors can have as many contextual details as needed, including nested/wrapped error messages, specific parameters of loop iterations, multiple errors rolled up from multiple operations, etc. Go's error return is just an ordinary but common use of its multiple-value returns. You can write a function/method that returns three ints and no errors: func (v Vector3) Splay() (x int, y int, z int) You can even write a function that returns multiple errors: func DoThisAndThat() (thisError error, thatError error) Try that with errno!