24 comments

[ 4.2 ms ] story [ 64.0 ms ] thread
How is this showing python better than go?
And how does this add anything to the discussion that hasn't been said 9000 times?
This article is also from 2012
And is the 3rd time submitted to HN by the same user.
The author (Ubersmekel of the eponymous Uberpython Pythonblog) dislikes that errors are passed as return values from functions, rather than percolated and caught/handled as exceptions. He or she feels that it is verbose and encourages the missing of maybe-important errors. The author also feels that panic and recover are ill-applied, at least in the standard library. These perceived shortcomings are sufficient not to leave their current language (likely Python, given the blog title).
(comment deleted)
This is my biggest gripe with Go as well.

Though initially the diligent programmer will check each and every call with if err != nil {}, there comes a time when laziness kicks in. When that time comes, you'll get hit with unexpected behavior (aka: tracking down nil references).

You might argue the same would happen with exceptions. Yes, they will blow up in your face as well if you fail to catch them. But when they do, you'll hopefully have a well specified restriction that enables to you pinpoint the issue almost immediately.

Basically, with exceptions you know the guy who shot you in the gut because you were being lazy, whereas with Go's error system he's hiding behind the bushes with a dozen or so of his conspirators.

You can argument around that in both directions: Yes, exceptions will blow your program up and you will see it later on and can debug the exception - no silent errors.

But on the other hand what you really want is correct error handling from the beginning (and no blowing up in release builds or SW delivered to customers). Here error return values have the advantage in my opinion that you directly see development that a function can error and that you need to handle that error. While you can't see that a function might throw.

What Go is probably missing is forcing you to look at the returned error values while you can currently accidentally miss them by only assigning the first return value of a function.

Yes, that would be nice, but as lazy programmers we skip some error checks for a reason: sometimes the error is highly unlikely. As such, it might not be worth the cost of writing a bunch of error checking code which costs keystrokes and litters the code with ifs.

If an uncaught exeception does happen, it moves from unlikely to likely, and so gets plugged up in the code. Rinse and repeat. Again, the obvious advantage being errors that are easy to track.

In go this approach would result in a possibly nightmarish debugging session.

Lazy programmers that skip error checks or count keystrokes as a something major are probably at the wrong job.

Either an error can happen then it must be handled - no matter how likely it is. Or it can't happen, then there's no need to handle it. The rare exceptions are functions where errors are indicated in the type signature but in reality won't occur, e.g. because the error return value will only be used in other implementations of an interface or because a precondition that guarantees that there will be no error was checked before. And for this cases you could still do something like: _ = functionThatReturnsError()

This is the approach that you e.g. have to follow in F#.

IMHO, Handling exceptions well (not too course grained, not masking problems, etc.) takes just as much work as simply dealing with each possible error as it turns up in the go fashion. Lazy programmers are going to mishandle exceptions too.
I'm not very familiar with Go, so please correct me if I'm wrong, but this looks like this pattern was chosen due to the async-focused nature of the language; similar to many callback-laden javascript error handling strategies. Is this true?

Also, I see that the error must implement the 'error' interface, and this interface specifies a single "Error" method. Is the typical error-checking practice to do type assertions on the err object, ensuring you know what went really went wrong?

I agree with the article that python's handling is better, if not for the fact that ignored errors within 3rd party libraries could go unnoticed and be hard to debug. Python3's try/except blocks even work in asynchronous coroutines.

Also, this article was written in 2012; this should be mentioned. (previous discussion: https://news.ycombinator.com/item?id=4562211)

I can't tell you why this pattern of error handling was chosen but this is different from the callback pattern in nodejs. In Go, errors are typically returned directly by the called function. Even if such function makes "asynchronous" calls (there is a great article shared on HN that explained why this term is not the best)
> I’m still waiting for that open source, concurrent, bottom left language to come along. Any suggestions are more than welcome.

Could that language be Swift? I haven't been following that too closely, but from my experience with Obj-C (and GCD), Apple has been doing a relatively good job with concurrency. Any thoughts on that?

(comment deleted)
I know i'm being a smartass, but:

"Why I'm not leaving Apples for Bananas (uberapplepie.wordpress.com)"

Apples and oranges. Python is a dynamic language, whereas Go is a systems programming language that tries to offer an alternative to C.

You {would/would not} switch to Go from Python for about the same reasons why you {would/would not} switch to C, Ada, Modula 2, and so on.

A more useful blog would be, "Why I'm not leaving C (or C++, Rust, ...) for Go".

If I were using Go (which I almost certainly won't be, ever), I think would try to use its panic/recover mechanism as much as possible to avoid the tedious multi-level error handling.

> I think would try to use its panic/recover mechanism as much as possible

And that's exactly the reason you should avoid Go. Every error is not exceptional. Most errors are not exception. Hence, most errors should not panic.

> to avoid the tedious multi-level error handling.

It only looks tedious to the uninitiated based on code snippets you've probably seen. Or if you're stuck in an imperative paradigm, i can imagine Go will feel tedious. But since Go has first class functions, it's quite easy to write high-order functions that work better than try/catch and make error handling flow quite nicely. It also provides better error handling than cumbersome, coarse-grained, and verbose try-catch clauses where the catch clause is not directly related to the call that produced the exception (which probably isn't really an exceptional case but rather just a simple error.)

https://gist.github.com/nicerobot/263869942dd5344d9cd6943af5...

Yea, Python can do the same but because Exceptions exist, everyone considers simple errors to be exceptional. Go allows errors to just be errors and error handling becomes related directly to the call that caused the error.

Users of languages that have excellent support for first-class functions and exception handling will probably not agree with you, sorry.

Lower-level code often doesn't "know" how to handle a situation when it is not able to complete its job. The executive decision about how to proceed is somewhere higher up.

Returning an error code that has to be propagated through several levels to the appropriate place is blub programming that belongs to the 1960's.

Higher order functions are useful for error handling, but only when coupled with a dynamic control transfer. The low-level routine invokes a callback, which is some closure belonging to a higher level. For this architecture to be useful to its full potential, that closure must be able to make a jump (for example, a jump to the surrounding code in the function where that closure was created, to effectively abort all the frames between that function and the low level where the error occurred).

I was _only_ pointing out, through _one_ simple example, the flaw in your conclusion of Go errors being tedious. I was not discussing other languages nor how to incorporate extensive and fluid error handling in Go to be as capable as any in other language. And since you've raised several points that weren't pertinent, it seems you simply want to disagree with me, so i'll end my side of this discussion. Have fun.
What is the magic pixie dust in Go which makes returned error values not tedious?

(Besides, of course, what I mentioned in the original posting: panic and recover.)

Call panic() from the callback ;-)
"Beginner programmers have a favorite language. Seasoned programmers have a language they hate the least." - Unknown Author

---

It is about the right tool for the job guys. All languages have something to hate on. Of course it is important to work with technology you like. Nothing wrong with this article, the guy is voicing his opinion. But this isn't a verdict on Go. People like Go for precisely the way it handles errors.

Part of the reason I avoid Python software like a plague.

The flaw of Go is not reporting unhandled errors at compile time (solved by 3rd party checkers), and no convenient mechanism to escalate errors upwards. Both somewhat fixed in Rust by compiler and `try!`. But I don't see how Python is making it any better. It actually makes it much worse.

Errors are not exceptional. They need to be handled with care. Exceptions make people throw them on every error condition, and then maybe sometimes, wrap the whole sections of code into try...catch blocks and not do anything useful about it.

Overall (IMO, from my experience) open source Go projects are light-years ahead of Python projects in terms of stability, usability and reliability.