17 ms·
Errors in Go: From denial to acceptance
- pilif 8y ago> Nothing explodes, the code keeps running in my book, this is not a good thing but a very bad thing. If I'm a library function somewhere all the way down the stack and I'm at a point where something about the state I'm working on isn't up to the specification I expect them to be, then I'd prefer the safety of blowing up. I don't want to be responsible for my callers eventually ignoring my error code and happily churning along thinking that I have fulfilled my promise and enacted the side-effect I promised to have. If something else, far removed from me depends on me to having had the side effect, it might blow up. Or it might write corrupt data. Suddenly a thing that went wrong at one place blows up at a totally different place which makes it incredibly hard to debug. And if the world is burning, "hard to debug" is about the least wanted property a problem could have. Oh no. If the world is burning for me, then I absolutely do not want to continue. But I also do not want to abort the daemon process I'm being hosted in because that would mean affecting other threads of execution (be it threads, coroutines or whatever else) where the world might be in a totally acceptable state. Calling `exit` in a library function is outright rude. If I get to `throw`, I can make absolutely sure that I made it clear to my caller or my caller's caller that I panicked while there's still a chance for my parent daemon to not die but handle my panic cleanly. Or, my caller, or its caller was prepared for me failing and can chose another way out. But by throwing I can raise a very strong signal that something is wrong and I have the guarantee that if I'm being ignored, then I will kill my parent which is totally their fault for ignoring me and it will be very obvious what happened, why it happened, and, above all, where it happened. If I'm an engineer wearing my devops hat, I'd much rather debug an uncaught exception causing a stack trace to be logged in sentry.io (or whatever else you use. I'm a very happy sentry customer, but your might have your own tool) rather than corrupted data that might have been corrupted months ago.
- navd 8y agoI think this is where custom error types become useful. They can provide you more context about what went wrong so that you can handle that issue properly
- pilif 8y agoNot if my caller forgets to handle my error at all
- NateDad 8y agoIf you care, you can't realistically forget. There are linters that will find your missed error checks.
- hu3 8y agoIn Go, if a function returns an error you have to explicitly handle or suppress it. Otherwise code just doesn't compile. There's no margin to "forget".
- insertnickname 8y agoThat's not quite true. For instance, the error value returned from `fmt.Printf`[0] is usually ignored silently. [0] https://godoc.org/fmt#Printf https://godoc.org/fmt#Printf
- jerf 8y agoIt's true that functions that return (result, error) tuples are hard to miss the errors from, because you have to explicitly either at least accept it, or ignore it. It's both visually obvious that you've dropped the error and there are linters to catch this for you (which I highly recommend integrating into pre-commit hooks). However, if a function just returns an error, but no result value, it is indeed easy to silently drop that error without realizing it by simply invoking the function and not catching the result at all. To which, again, I'd highly recommend using linters at commit time or even code save time.
- jcelerier 8y ago> In Go, if a function returns an error you have to explicitly handle or suppress it. Otherwise code just doesn't compile. ah, yeah, great idea. in practice this just means that most Go projects are minefields of if err != nil { panic(err) } or if err != nil { return err } ie a manual & repetitive implementation of assert for the first case and manual & repetitive implementation of an exception call stack for the second. thank you very much, I'll take the automated version known as exceptions instead.
- cle 8y ago> in my book, this is not a good thing but a very bad thing. Your whole reply is a straw man. The author is claiming that nothing explodes b/c the error is correctly handled, not b/c it was silently swallowed. I've seen so many bugs caused by exceptions being silently swallowed in other threads. Exceptions are not a silver bullet here, and you end up having to pass errors as values across call stacks anyway.
- TimJYoung 8y ago"Your whole reply is a straw man. The author is claiming that nothing explodes b/c the error is correctly handled, not b/c it was silently swallowed." Yes, but I think that this is exactly what pilif is objecting to: the fact that there is no way for the callee to force the caller to deal with the error condition (I'm not sure that this is entirely correct, given that Go has the panic/recover keywords that are later discussed in the article, but then you might as well use exceptions). IMO, this is very similar to what happens with use-after-free and other types of memory reference errors: they are extremely hard to debug because the symptoms/results of the error can be totally disconnected from the original source of the error, in some cases by minutes or longer. You simply don't want to have a situation where improper state is allowed to linger and further pollute/corrupt any subsequent computations.
- zzzcpan 8y agoAnd yet forcing the caller to deal with errors still doesn't prevent improper state in any way.
- jrobn 8y agoThis is why Erlang's crash early and restart philosophy was so eye opening when I became more and more familiar with the language and design. The whole language is about managing state and managing it well in a dynamic realtime environment (versus Rust's explicit state encoding for example).
- verdagon 8y agoCan you tell me more about Erlang's approach? Quite interested in learning of new ways to deal with state and errors.
- zzzcpan 8y agoTried to explain it here: https://news.ycombinator.com/item?id=18534441 https://news.ycombinator.com/item?id=18534441
- dnautics 8y agoIt's called the "let it crash" philosophy. The idea is that things are going to go horribly wrong no matter how well your code and everyone else's code is written. If you program for resilience, that is the ability to recover from a horribly bad event, then you can get high service availability and faster development (you're not obsessed with checking for errors, you only handle the most common ones and program to capture enough errors to achieve the service level you are comfortable with or have agreed to).
- jlouis 8y agoIsolate all memory so a failure is compartamentalized Discriminate between the error-kernel of the program and the rest. The kernel is the part which is not allowed to fail. Goal: keep it small. Discriminate between errors you have seen happen, and everything else. Handle the errors you've seen. It looks a bit like Go's handling, but only for things where you know you have an error flow. There are also exceptions, but they are somewhat rarer in typical code. On an unhandled error, crash the isolated process. This produces, a state of the memory, a stack trace and eventually the last few messages sent to that process. The system has a recovery model which will restart failed processes from a last known good state. The programming language is functional, and quite much so, so there are orders of magnitude fewer errors relating to state manipulation. One typical approach is to use the crash as a way to learn about errors that can happen, then patch those errors by handling them. Because you have a strategy for reactively handling errors, it is far less dangerous to have them in th e program, as long as they are under a noise floor.
- ansible 8y agoThe error handling story in Go had started to bother me more and more over the years. Not that it was that bad, it just felt cumbersome. And then I started learning Rust for embedded development. I basically love the entire language, and there are very, very few design decisions I disagree with. The error handling story is much better there, and if they're making breaking changes in Go 2.0, I'd suggest they look there for a better path forward. > Panic-Driven Error Handling I'd insert a gif of Vader at this point, from the end of Episode 3. I strongly disagree with this, using 'panic' should be a last resort in most situations, when the program has no reasonable way to recover from an error condition. File not found, user error, etc. are all common occurrences which should be handled by the normal mechanisms. And this goes double for library code.
- weberc2 8y agoI mostly agree with this (especially that panic should not be used for error handling, but only for exceptional circumstances). I think checked enum style errors are the way to go, but I also think that Go's approach suffices. A popular criticism of Go's approach is that the compiler doesn't force you to handle your errors, which is true, but I find that Go's culture suffices here--the biggest complaint I have about Go's error handling is that you have to use a library to get stack traces (which is also true for Rust) and stack traces are simply very practical. The other popular criticism of Go's error handling is that it's verbose, which I wholly disagree with--the verbosity is such a negligible cost that I would much prefer verbose error handling to the language complexity required to elide them (especially if the proposed sugar required baking "errors" into the language as opposed to treating them like any other data).
- ansible 8y ago> A popular criticism of Go's approach is that the compiler doesn't force you to handle your errors, which is true, but I find that Go's culture suffices here... The Go culture does suffice, but the Rust culture is even stronger here. Everyone is expected to use Result for something that can fail, which I like a lot for consistency's sake. > The other popular criticism of Go's error handling is that it's verbose, which I wholly disagree with--the verbosity is such a negligible cost that I would much prefer verbose error handling to the language complexity required to elide them (especially if the proposed sugar required baking "errors" into the language as opposed to treating them like any other data). Six months ago I would have agreed with you. And then I learned about error propagation using the question mark operator in Rust: https://doc.rust-lang.org/book/second-edition/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator https://doc.rust-lang.org/book/second-edition/ch09-02-recove... Arguments can be made about creating custom error messages at each call level, but is that really necessary most of the time?
- lloeki 8y agoThe author seems to generalise his use case into a description of how to use panic/recover as a bespoke exception mechanism. In that case this is still bargaining. > In imgproxy, I use this approach to stop image processing if the timeout is reached (see here, and here). The goal is not to bother about returning timeout errors from each function (emphasis mine) While I understand the pain, I do not like the train of thought. It is our duty as responsible developers to bother with error handling. Luckily this is not a library but a "standalone application", not a "library", which is a slightly different use case (e.g negroni has to recover from panics to log and continue serving http requests). The accepted solution looks like† a world of pain waiting to blow up in a myriad of subtle corner cases (do you remember what happens exactly when there's a panic in a defer that was called because of a panic, and in which order deferred functions are called?) and is barely more readable. I've seen much more interesting and obviously robust Go 1 patterns (that I can't find right now) that e.g pass error handler functions around. † At first sight. Maybe it's not when digging deeper, but that's not the point: the most glorious thing to me when I read idiomatic Go code is that basically everything is boringly obvious. This is a clever hack and crosses a threshold I'm not willing to go past in production-class code.
- jrockway 8y agoI completely agree. I think people are shocked, at first, at how everything can go wrong in ways they've never considered. Exceptions hide the complexity by pretending that errors are exceptional things that will never happen to YOU. The opposite is what's true, though, random errors will happen all the time; networks drop packets, remote machines are slow, database transactions fail because of other writers, etc. If you are aware of all the things that can go wrong, you can handle them. If you treat them as "exceptions" rather than the rule, then you will just write flaky software that needs constant manual attention. Some people like this, I guess, but I'm personally not a fan. The syntax could be better (I do have err, !=, and nil keys on my keyboard, I really do) and it would be nice to annotate errors and not lose the semantic information (fmt.Errorf("trying foo: %v", err) throws away the specifics of err beyond the result of err.Error()), but really... explicitly handling errors after every function, even if it's just "return err" and punt to the code one level up... is something you have to do in every language. Go just front-loads it. As for timeouts, I am not sure why you wouldn't just pass in a context, which already has provisions for a deadline, explicit cancellation, and cancelling further library calls. If you can cancel your operation when <-ctx.Done() returns, then you can ensure that all the related work is cancelled. You don't need a clever panic/recover for that. Just select { case <-workDone: ...; case <-ctx.Done(): cancelWork(); return ctx.Err() }. I am not sure why people rely on hacks when there's already a standard method built into the language.
- Insanity 8y agoWhen learning Go, I actually liked how it dealt with errors. I like that you need to aknowledge that an error could happen, but I also like that it's a normal return value. It's not valid for _everything_ so I don't agree with it entirely. But, some things that are exceptions in Java would make more sense as just returns because it is _valid_ output for a function. I think it's the distinction between "the method can't perform what it's supposed to perform due to the input it received" vs "something unexpected happened and that's why the method couldn't perform what it was supposed to". I'll try to give an example: Imagine you have a method in Java for parsing a credit card, which follows a certain pattern for verifying CSV. If the pattern wasn't satisfied, you could have a: Throw new IllegalArgumentException("Input does not match CSV string ([0-9])") But to me _exception_ would signify something unexpected happened. Whereas here I'd like if I could just 'return' that the input was not correct, and therefore the method could not check the CSV. Whereas, for example, if you have a method that writes to a File and the file you provided does not have the right priviledge -> this is an Exception because the user could not expect this. I'm having a hard time wording this as consisely as possible, I'm sorry if it seems a bit incomprehensive but I'll try to TL;DR: Exceptions: Unexpected behaviour from system Error returns: Wrong input from user If that makes sense :)
- masklinn 8y ago> If that makes sense :) You're just trying to justify/rationalise that you like go's arbitrary positioning on the result/fault continuum (well not continuum, it's not really 1-dimensional), there are few positions which can't be justified there. > But to me _exception_ would signify something unexpected happened. Being provided random garbage when prompted for a CVV can reasonably be unexpected. > Whereas, for example, if you have a method that writes to a File and the file you provided does not have the right priviledge -> this is an Exception because the user could not expect this. Why would the user not expect ACL issues on something well known for involving ACLs?
- NateDad 8y agoGah, my eyes! Why is the font so huge? And why doesn't zoom change it? I have to like step 5' back from my screen to read it.... Otherwise... I have been writing Go for about 6 years... errors make so much sense to me. Why have some external codepath for "file not found"? Why is that different than "name == bob"? They're just data that is in one state or another. You check for them the same way, with an if statement. There's nothing magical.
- erik_seaberg 8y agoIf a function can't succeed there's no reason to continue executing it. Stopping and reporting the error to your caller is the common case and should not be discouraged by adding friction and obscuring the useful parts.
- jstimpfle 8y agoThinking about control flow and organizing for clean structure is crucial for developping code bases of non-trivial size, and that should not be discouraged by encouraging superficial efficiencies.
- weberc2 8y agoAnd yet no one is deterred from proper error handling in Go. On the other hand, I see people fail to properly handle errors all the time in our Python/JS shop. EDIT: This isn't some language partisanship; it's my observations from years of experience with the aforementioned languages (I work in a Python/JS shop). I'm guessing there are no formal data about this, so I'd be curious to hear other anecdotes whether they agree or conflict with mine.
- smbullet 8y agoYeah, I'm not sure why you're being downvoted. We also use Python at work and reviewing code is an absolute nightmare because you end up needing to dive deep into each function call to see if there are any exceptions being ignored. We're trying to enforce adding all possible exceptions in the docstrings but it's difficult because third party libraries (and even the standard library!) sometimes don't document them. Typically we have a catch-all Exception in our main loop and recover from there in case there's something we missed in dev and staging. I'd much prefer being able to know which functions have the ability to fail and why.
- kccqzy 8y agoThe combination of panic and recover is exactly like raise/throw and catch. One could even reasonably argue that both are both just a single specialized call/cc (You pass call/cc a function to execute, which receives an argument that allows immediate exit to the enclosing scope.) It's basically a kind of control flow operator. So what's the novelty here?
- recursive 8y agoI think fancy custom ligatures are actively un-helpful when trying to demonstrate something about code to a wide audience. Is it a custom operator? Is it a fancy codepoint like APL or Julia? Is it a pre-processor macro? Personally, I don't like them in my editor either, but if you want to use one that's fine. But they're pretty confusing when encountering in code online.
- Matthias247 8y ago100% ack. If I wouldn't have known ligatures, I would be totally confused about the syntax in this code examples. Am I expected to type this character? Where is this thing on my keyboard, etc.
- runn1ng 8y agoEven go authors themselves know the current error handling is not ideal, that's why they want to change it in go 2... see https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md https://go.googlesource.com/proposal/+/master/design/go2draf...
- shaklee3 8y agoHe talks about this in the article.
- jayd16 8y agoSo how is this better, cleaner syntax than checked exceptions in Java, for example? I thought checked exceptions were considered a failed experiment. The convention in go is to always handle the errors so isn't that the same thing? Is it the lack of forced stacktrace that makes flow control through exception more palatable? Do people just like convention over compiler enforced correctness? Or is my understanding incorrect and devs don't really like go's error handling?
- erik_seaberg 8y agoThere's a difference between "errors should alter the flow of control" and "error types should be transitively declared". The latter failed in Java because the language didn't have enough support for handling the layering violation. E.g., if the X ctor throws suddenly you can't implement Supplier<X> without dirty hacks like writing an explicit handler that does nothing but rethrow an unchecked type.
- jayd16 8y agoThrowing in constructors is an anti-pattern imo but assuming you need it, how does go solve this? Would you not return nil and an error that you would immediately check? Seems like defensively checking return values in go isn't that much better than catching every exception. Maybe I don't understand the correct go implementation.
- skybrian 8y agoGo's error handling has flaws, but one thing it got right is having a single error type that everyone uses. Most of the time you don't think about what subtype it is. In Java, declaring a method to throw any Exception (or should it be Throwable?) is considered an antipattern and most people use subtypes. I think if the exception hierarchy were simpler so that most of the time, there are only two kinds of methods (those that always succeed and those that can fail), checked exceptions would have worked out.
- jayd16 8y agoThis doesn't seem like a significant difference to me. You can catch all throwables and handle them the same way. Ironically, it feels worse to ignore the added exception type information so it's rare to see errors handled generically this way.
- josteink 8y agoI’m sorry but to me this just looks like Stockholm syndrome all the way. The approach suggested for Go 2 under bargaining is basically a reverse try-catch at best and a Visual Basic “on error goto” at worst. Even in their own attempt to explain why try catch is bad they effectively admit it serves a common use-case: A common use-case not handled by Go no less. This lacking also puts the burden on the programmer which will have to manually handle each and every error, by himself, everywhere, one by one. Even worse are the priorities given in the language: checking for errors is cumbersome, while ignoring errors is easy. I’m willing to be proven wrong, but my immediate guess is that this in general leads to more buggy code, not less.
- sephware 8y agoWithout taking sides on the issue, these are purposeful trade-offs that the Go team makes. They are trying to avoid bloating the language and making it too difficult for them to add optimizations or other features. And I think they would argue that it makes ignoring errors harder, since you have to intentionally discard the error value when it was given to you, which takes conscious effort, rather than omitting it from the code completely like other languages let you.
- nomel 8y agoI find this perspective curious. Maybe it's the non-web field that I work in, but I've never written a program where I could unintentionally ignore errors and have things go as expected after. What are some cases where errors can be ignored? And if they can be, are these neccesarily some sort of "status" rather than "error"?
- sephware 8y agoIf you're writing a command line utility that tries to load a config file and falls back on defaults if it's not found, you may try to load it and just ignore any error, only caring that it either returns a path if found or null if you should use default values.
- a-saleh 8y agoMan! 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.
- H1Supreme 8y agoWhat's up with the "equal sign with a slash" being used in place of the literal "!="? I realize it's reference, but why abstract the code away like that? Seems unnecessary, and potentially confusing for new programmers.
- a9entroy 8y agohttps://github.com/tonsky/FiraCode https://github.com/tonsky/FiraCode
- markbnj 8y ago>> Relying on documentation is not a silver bullet either: poor documentation is still very much a thing. >> It makes sense always to check the documentation to find out whether an error type is a part of the function signature. O_o. So on the one hand you don't know if a function will throw an exception and can't rely on the docs, while on the other you don't know if it will return an error code so you should check the docs. I don't write Go yet, although I look at a fair bit of it. A friend of mine who works a lot in it tells me he was put off at first but basically decided to trust the language's idioms for now. Until I have more personal experience my outlook is: "man, I think I would really miss exception handling" with a smattering of "boy that sure looks more error prone to me."
- AdeptusAquinas 8y agoReminds me of result types and ROP in functional languages, especially F#. Except in F# callers must handle all conditions. Maybe they can add that check to golint.
- rauhl 8y agoFor some time now, I’ve been thinking that it’d be funny to add a Lisp-style condition system to Go by abusing panic, defer & recover. I think that it’d be possible (since Lisp’s conditions are really just built atop THROW & CATCH). I don’t really think it’s a good idea: Go’s lack of macros imply rather a lot of anonymous functions to get it to work, which would imply loads of parentheses & curly brackets. Not to mention that the lack of first-class type literals would make actually using it close to insane. But it’d certainly be interesting. It’d definitely not be idiomatic Go. Still … interesting.
- masklinn 8y ago> For some time now, I’ve been thinking that it’d be funny to add a Lisp-style condition system to Go by abusing panic, defer & recover. I think that it’d be possible (since Lisp’s conditions are really just built atop THROW & CATCH). How would you resume execution at the panic point? A conditions system requires that you don't unwind, afaik Go's panic do in fact unwind.
- kazinator 8y agoLisp-style conditions are implementable even over C setjmp and longjmp style control. The non-unwinding part is done by searching the chain of frames and calling functions, without taking any non-local control transfer. The frame linkage is assisted by maintaining a global or thread-specific stack top variable. A frame is declared on the stack, then hooked into the list using the global variable. When a control transfer does take place, the value of that variable is properly maintained to discard the frames that are in functions being abandoned. Source: been there, implemented that. I'm not that familiar with go but from what I remember of the descriptions of panic/recover, something similar should be doable.
- rauhl 8y ago> How would you resume execution at the panic point? You invert it: rather than panicking, then continuing from the panic point if things recover, you proceed, only panicking to perform a transfer of control. Interestingly, this is what Lisp does: SIGNAL[0] (the most primitive function) just looks for applicable handlers and calls them from most- to least-recent; this means that if a handler transfers control (panics, in Go terms) then the condition has been handled; if not, SIGNAL just returns NIL; ERROR[1] does the same thing, but calls INVOKE-DEBUGGER if no handler transfers control; CERROR[2] is like ERROR, but it adds a CONTINUE restart, which just lets control resume. It’s possible to implement this same structure in Go: conditions.Signal() would search for applicable handlers and execute them in order; if control transfers then it’d never return; conditions.Error() would invoke one or more handlers, or the debugger, and would never return; conditions.ContinuableError() would invoke one or more handlers, or the debugger, but might return. The key is that it’s possible to execute a restart in the dynamic context of the signalling function (via use of Go’s context.Context), and only rewind the stack when you don’t need to go back down it. I think — I’ve not yet implemented it! 0: http://www.lispworks.com/documentation/lw71/CLHS/Body/f_signal.htm http://www.lispworks.com/documentation/lw71/CLHS/Body/f_sign... 1: http://www.lispworks.com/documentation/lw71/CLHS/Body/f_error.htm http://www.lispworks.com/documentation/lw71/CLHS/Body/f_erro... 2: http://www.lispworks.com/documentation/lw71/CLHS/Body/f_cerror.htm http://www.lispworks.com/documentation/lw71/CLHS/Body/f_cerr...
- deleted 8y ago[deleted]
- pkulak 8y agoGo's error handling is exactly the equivalent of only having checked exceptions. I've gone the other way, personally, the last few years: I prefer languages that only have unchecked exceptions.
- erik_seaberg 8y agoIf you call methods that throw IOException, you can just declare IOException, you don't have to litter every statement of every method with catch clauses that hurt readability. Edit: where this breaks down in Java is trying to implement interfaces that don't declare the exceptions you're then forced to smuggle out.
- makecheck 8y agoIt’s actually kind of amazing that programming languages basically make it as hard as possible to figure out what’s going on when you have the temerity to check for errors, and it is easier to read code that doesn’t care. Programming books love to exclude error handling “for brevity”, when the real problem is that the mechanisms for doing so are insane to begin with. Conditionals are frustrating because they tend to be paired with indentation, which doesn’t "diff" well in a lot of tools and makes simple changes seem more extensive than they really are. In a sense, I shouldn’t have to re-indent half a function just because I happen to be changing the “preferred/happy path” that is buried inside several error checks. On the other hand, early returns are frustrating because they enable lazy programmers to make quick hacks at the expense of complicating later maintenance (e.g. instead of having one “normal” path to consider, the next programmer has to find and understand all the short-cuts that someone has introduced throughout the code and make sure they will all work). I suppose the most “compatible” change to existing languages would be something like a keyword or syntax to identify code that is only handling errors. That way, at least IDEs/editors/etc. could offer ways to intelligently hide code that apparently isn’t part of the normal flow of a function. Fortunately languages are now more likely to have good mechanisms for declaring local blocks of reusable code (not banished to functions on far-away lines) so it is now more practical to write code in logical chunks that aren’t quite as indented. You can write a sequence of operations, maintain it without ugly "diffs", and still have indentation and error-checking, etc. at point of use.
- peterashford 8y agoLooking at that pile of vomit code and claiming to have reached some kind of nirvana is just ridiculous. That article is an argument against go's error handling if I ever saw one. One shouldn't have to go to that much effort just to make the handling of errors in Go, sane. To come up with that monstrosity one one hand while calling try ... catch "verbose" on the other hand is hypocrisy of the highest order.
- luord 8y agoThe article is useful, detailing different approaches to... deal with the ugliness; but it still lost me because of the ridiculous implication that Go's error handling is in any way right or even better than the try catch flow. It isn't, it so much isn't that even its designers are tacitly considering they're wrong with check and handle (Go's own try catch, which the OP himself mentioned). Go is not perfect, no language is, and one of its biggest flaws is its odious error handling. And that is good, because nothing can be perfect. Apropos of nothing, it's pretty amusing that the go faq entry criticizing exceptions has a typo.
- purple_ducks 8y agoIsn't it weird that there's no language which nailed error handling and patterns 100% and which people can point to confidently and say "Do what they did - it's bulletproof"
- zzzcpan 8y agoThat language is called Erlang, people always point it out, nobody listens though.
- tomohawk 8y agoI would not ever even think about reusing any package that used the panic/recover mechanism for error handling. It's that bad.
- diebir 8y agoGo away and come back with exceptions. It is not 1990 anymore and I am not going to use C, unless it's Linux kernel driver. Bye.
- dang 8y agoPlease don't post flamewar comments to HN.
- leshow 8y agoThere are other ways to handle errors besides Go's overly pedantic way and exceptions. Either/Result types are in a few languages and are wonderful.
- lobo_tuerto 8y agoVery annoying and frustrating that you cannot zoom in/out to adjust the font size in this @%$#!& site. Why take that freedom from your users? I see a waaaay big font on my monitor (3440x1440).