3 ms·
Man! I actually like half of Golang's error handling practice. Returning errors and dealing with them in a simple case sounds nice, there is no magic required,
by a-saleh 8y ago
Man! I actually like half of Golang's error handling practice. Returning errors and dealing with them in a simple case sounds nice, there is no magic required, no need to think "am I forgetting to catch an exception here?"
On the other hand, the fact that we are returning a tuple of (result, err) where by convention err should be nil if everything worked screams as a terrible design to me.
But I might be influenced by reading too much Haskell/ML inspired languages :D
Once you understand applicative validation [1] or exhaustive pattern matching on row-polymorphic sum types [2], other error handling methods just seem so crude :)
[1] https://leanpub.com/purescript/read#leanpub-auto-applicative-validation https://leanpub.com/purescript/read#leanpub-auto-applicative...
[2] http://keleshev.com/composable-error-handling-in-ocaml http://keleshev.com/composable-error-handling-in-ocaml
- jstimpfle 8y agoWho said (result, err) should be a sum type? Why must the result be invalid when err != nil?
- lalaithion 8y agoIt doesn't have to be, but it can be, and preventing it from being a sum type because you might sometimes want a product type seems overly complex to me.
- dwohnitmok 8y agoIt doesn't. You can use an inclusive sum type (i.e. inclusive or), which would be success, error, or result with accompanying error. You can think of it as success, fatal error, or non-fatal error. The same machinery still works and it's pretty straightforward to convert between the two representations (so you can track which parts of your code bail out at the first error and which parts try to soldier on). See for example http://hackage.haskell.org/package/these http://hackage.haskell.org/package/these or https://typelevel.org/cats/datatypes/ior.html https://typelevel.org/cats/datatypes/ior.html or https://github.com/purescript-contrib/purescript-these https://github.com/purescript-contrib/purescript-these For a lot of FP languages that take this approach to errors you'll have three representations (bail out at first error, continue on nonfatal errors, and bail on any error but first try to accumulate as many errors as possible as a batch) that all have conversions between them depending on what you want the semantics of that section of your code to be.
- merb 8y agobtw haskell also has a concept of exception, so it's not like that you need These or Either for all cases, in some cases using a exception might be good enough.
- dwohnitmok 8y agoExceptions do exist but their usage in Haskell is mainly restricted to code living in IO (as a result of both community customs and the restriction of handlers to IO code). FWIW personally I think of exceptions in Haskell almost exclusively as a tool for thread management and tend to shy away from using it for only error handling (although the two do have some overlap). But that's a more divisive topic in the Haskell community at large than the ban on exceptions in pure code.
- atombender 8y agoAs an aside, there is a notable Go API that a lot of people get wrong: io.Reader's Read() and io.Writer's Write() both return a count when they return io.EOF, and if the count is a positive number, the buffer was filled. If you do a code search for Go projects on Github, a disturbing number of them get this utterly wrong, and simply do something like this: for { c, err := r.Read(buf) if err == io.EOF { break } if err != nil { return err } ... When they should be doing something like: for { c, err := r.Read(buf) if err == io.EOF { if c == 0 { break } } else if err != nil { return err } This is an unfortunate API design, because almost all error-returning functions have disjoint return values, and would could be expressed as sum types in other languages, which teaches developers to not expect both return values to be valid at the same time. It certainly surprised me. The other problem with Go's design is that you have to read the documentation to understand what the semantics of a particular function are. The type system can't tell you.
- a-saleh 8y agoI said that I prefer it :D And even in ML-style language, I wouldn't like a result of (Maybe result, Maybe error) and I would much prefer Result result | Partial (result error) | Error error | Nothing These two things are the same. I still like the spelled-out version better. Especially because this gives me easier way to model different things. Consider changing my example to Result result | Partial (result error) | Error error or Result result | Error error Semantic of every one of these is different. And nicely explicit :)
- quotemstr 8y agoWhy wonder "do I need to catch an exception here?"? If you're doing it right, most of the time, you want to let exceptions propagate.
- a-saleh 8y agoHm, maybe it is the explicit nature I am after? For example I really liked the checked exceptions in Java, even though I understand the reservations of other people.