3 ms·
Error handling has to be one of the worst things about go. Even though IDEs can highlight where you've forgotten to bind an error returned from a function to a
by nprateem 8y ago
Error handling has to be one of the worst things about go. Even though IDEs can highlight where you've forgotten to bind an error returned from a function to a variable they don't prompt you if you then forgot to return that err back up the call stack - it's easy to just log the error and forget to actually return it.
The only way I've found to avoid RSI and make error handling just about tolerable is to use an IDE that supports code autocompletion - e.g. type `err` and hit tab and it should replace it with an `if err != nil {}` block. The other thing is to use pkg/errors[1] and wrap the error so you've actually got a stack trace if you want to log the error further up the call chain.
Error handling is a solved problem - exceptions, or like the article says monads if the language is functional. The fact that Go supports neither out-of-the-box is a frustrating waste of time.
[1] https://github.com/pkg/errors https://github.com/pkg/errors
- YorkshireSeason 8y agomonads if the language is functional Monads are not a functional concept. For example an imperative language like Scala (which, being in the ML family of languages has a powerful and first-class functional sublanguage) has monads that work perfectly fine with imperative computation. Indeed, I find it a good programming style to let trivial, ubiquitous monads, like printing / IO / global state, be handled by the built-in primitives, while higher-level, unusual, application-dependent effects are implemented explicitly using monads. Monads are essentially a form of overloading / re-defining composition operators, e.g. the semicolon operator in traditional imperative languages. In languages like Haskell and Scala, types (and higher kinds) are used for this purpose, hence it is often though that monadic programming is restricted to statically typed languages with powerful types, and don't make sense for dynamically typed languages. Nothing could be further from the truth. All we need is a 'hook' into the sequencing mechanism(s) the language provides, at least in principle. Admittedly, the outcome is not nearly as pretty as in statically typed languages. See [1, 2] for discussions. [1] T. Garnock-Jones, Monads in Dynamically-Typed Languages. https://eighty-twenty.org/2015/01/25/monads-in-dynamically-typed-languages https://eighty-twenty.org/2015/01/25/monads-in-dynamically-t... [2] O. Kiselyov, Monadic Programming in Scheme. http://okmij.org/ftp/Scheme/monad-in-Scheme.html http://okmij.org/ftp/Scheme/monad-in-Scheme.html
- hollerith 8y agoWhat you write is technically correct for a sufficiently relaxed definition of the word "monad", but omits a very important point: when something of type Integer is evaluated in a Haskell program, the evaluation is guaranteed not to have side effects. The language and the standard library have been carefully arranged so that that is the case. If you want the evaluation to have a side effect, you have to change its type to `IO Integer`. Kiselyov's approach to monads in Scheme in contrast make no such guarantee and cannot be modified to make the guarantee. The situation with monads in Haskell is analogous to the situation around data hiding in certain "pure" object-oriented languages which have been carefully arranged so that the only way to mutate the state inside an object is to send a message to the object. Both guarantees make it easier to reason about programs. (Another example of a language that has been carefully arranged to make a guarantee is how Rust guarantees against use-after-free errors.) That is really all I have to say. The rest of this comment is designed only to prevent my being dismissed by a reader because what I wrote above is not precise enough. When I referred above to monads in Haskell it would have been more precise for me to write "the IO monad and if you are into monad transformers, user-defined monads with the IO monad in its implementation". Monads have multiple uses in Haskell. (e.g., the List monad is involved in the implementation of list comprehensions.) It is only the IO monad (and maybe the relatively obscure ST monad, but every use of the ST monad can be transformed into a use of the IO monad with some loss of conciseness and maybe readability) that is involved in the guarantee about side effects. When Haskell and its standard library were being carefully arranged, the designers had to make some choices as to what is considered a side effect and consequently what is in the IO type. For example, a loop does not have to have `IO` in its type signature in Haskell, so looping endlessly is not considered a side effect in Haskell even though maybe it should be, and in fact it is possible to define a language similar to Haskell where a loop must have `IO` in its type signature: specifically, you get rid of tail recursion with the result that the only way to write a loop is to use the fixed-point operator (i.e., the Y combinator, which since you got rid of tail recursion you have to provide as a primitive of the language) and then instead of giving Y the type (a -> a) -> a you give it the type (IO a -> IO a) -> IO a. There is some ambiguousness in other words in what is considered a side effect, but that ambiguity does not undermine my point because any skilled definition of side effect in Haskell will provide some guarantee.
- eadmund 8y ago> Error handling is a solved problem True. > exceptions Nope. Exceptions aren't capable enough; you really want conditions & restarts: http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html http://gigamonkeys.com/book/beyond-exception-handling-condit... Conditions can be signalled, handled & resignalled like exceptions, except that they can be optionally ignored and handlers can optionally choose to restart. An exception unwinds the stack, while a condition is handled prior to unwinding, which means that the available recovery strategies are more numerous.
- nprateem 8y ago> Nope. Exceptions aren't capable enough They've been perfectly fine for the majority of popular languages and are a vast improvement over manually mimicking them the way most people do with Go.
- YorkshireSeason 8y ago"Right tool for the right job" Exceptions are an extremely useful and widely used tool for dealing cleanly with certain classes of errors (through a non-local jump out of a context to a dynamically bound target), let's call them simple errors. Maybe something that can be oversimplified like this: def main ( ... ) try ... catch case e => print "Something's wrong, aborting" (Note that in Go's original main use case (writing low-level servers), such simple error handling is rarely what's needed.) If you need restarts etc, then standard exceptions are not the right tool. In my experience, it's worthwhile to stop thinking about errors in such situations but see the behaviour that you are tempted to classify erroneous as part of the normal algorithm, using control constructs other than exceptions.