4 ms·
They only give the illusion of providing a better development experience if you forgo dealing with errors. Indeed, there is a class of programming, known as sc
by randomdata 3y ago
They only give the illusion of providing a better development experience if you forgo dealing with errors.
Indeed, there is a class of programming, known as scripting, where errors actually don't matter. If something bad happens you simply fail, deal with the problem through human intervention, and try again later. It is not unreasonable to carry errors up the stack using exception handlers in this type of problem domain. Arguably communicating an error state this way is the best solution we know of to that problem.
However, there is another class of programming, known as systems, where error handling is the most important code you will write. This is the domain where exception handlers are just plain not suited to the problem. The idiomatic Go solution is not pleasant, but a huge improvement over dealing with catching exceptions everywhere.
As before, because catching exceptions is so painful, many applications that probably should be systems are pushed into being scripts. Which may very well be a good engineering solution to the problem of dealing with a quirky language, but idealistically a programming language will not shape your application requirements like that.
But, yes, Go is not at all designed to be a scripting language. It has been quite explicit about that since day one.
- JodieBenitez 3y agoI don't think the scripting/systems distinction matters. After all, Go dragged a lot of Python developers that create more or less the same kind of applications and banckends. They just enjoy added benefits such as easy deployment, faster execution, lower memory requirements, saner dependency management, and more. But I'm pretty sure error handling is not what they like about Go. I sure don't, I just cope with it.
- randomdata 3y agoWith respect to Python, what is the practical difference between: try: something() except FooError as err: raise BazError("something bad happened", err) except BarError: return 1 and err := something() switch { case errors.Is(err, FooError): return 0, NewBazError("something bad happened", err) case errors.Is(err, BarError): return 1, nil } ? I see no meaningful difference at all. The only thing that stands out to me is that Go version gets you thinking about errors more (although not as much as some other languages), not being sourced from something bolted onto the side. In Python, you are more likely to lazily call something() blissfully unaware that an error may occur. Is the problem actually not with Go per se, but that people don't want to be reminded that errors are a thing, presumably because they don't want to put in the hard work to properly deal with them? It is quite true that properly dealing with errors is hard work. Really hard work. But it is also the most important code in your application, so I posit that it is work worth doing.
- JodieBenitez 3y ago> In Python, you are more likely to lazily call something() blissfully unaware that an error may occur. It's not about being lazy or unaware (frankly, that's condescending). No matter the language, IDEs are pretty clear about what errors a something() could trigger. It's not about terseness of the code either. Again, IDEs will just fold all the "if err != nil" blocks so I don't care. It's about separation of concerns. Example: If I develop an app which rely on external resources such as an API or database, there may not be any reason to manage all of their possible failures upfront, right in the middle of the so-called "happy path" of the current business logic. Just let the connection error bubble up and do right thing (ie. rollback whatever you had, log the error and display a nice message to the user, or whatever is needed) elsewhere where this error dealing is centralized along with all http transport shenanigans. That's kinda like the Java checked exception thing: annoying with no real benefit. These days I port an app to Go. And I really like some of the things it brings to the table (mostly unrelated to the language semantics though). But it also feels incredibly archaic at times, error handling not being the only culprit here.
- randomdata 3y ago> there may not be any reason to manage all of their possible failures upfront You still need to handle the errors you receive to ensure that implementation details do not leak to the next caller. That, at very least, means returning (or raising/throwing) a new error that is relevant to the function returning it - not what was relevant to any functions it happened to call. To continue with your example: If your database error bubbles up, callers will come to rely on those error values. Then, when changing requirements sees you need to switch your implementation to a web service API – with a whole different set of errors – now everything they wrote is broken. Which is especially problematic in languages like Go and Python where possible error values are not well communicated to the caller via language constructs. Your code will compile just fine, and seemingly run fine, until that SQL error you were expecting becomes a 500 HTTP error that you didn't know to handle, and now you're in a difficult place. Maybe you can get away with that practice if you have checked exceptions, as at least the compiler will blow up when the possible set of errors change, but it's still a pretty awful API design to leave the caller breaking like that just because you changed an insignificant (to the caller) implementation detail underneath the abstraction they are using. > (frankly, that's condescending) Flawed logic. There is no emotional component to a discussion.