The worst problem with almost all popular programming languages are null and shitty error handling, such that, when you call foo() where foo is String foo(){ blabla } you can get a String, null OR an exception/hidden error response, but most compilers happily let you treat it as if it only ever returns a String. (What!!!?)
I hope some day null is no longer a thing, and that Functional Programming types like Option, Either, Try etc in the native libraries is the new default. (BTW, there's really nothing "Functional Programming" about them, they're just objects like any other in "Object Oriented" languages)
This article is a poor choice for submission. He did a companion article that is the exact opposite, and a follow-up article a year later. The follow-up would be a much better choice. It gives links to both prior articles and his thoughts after a year of reflection - https://debugged.it/blog/20000-lines-under-the-go-cean/ .
I’ve never written a line of Go, though I’ve read a fair bit. I was expecting to object to the criticism of its error handling but I ultimately agree. If you can return and ignore an error, that’s another way of saying throw/raise. If error isn’t typed it’s no different from “anything can throw anything anywhere in the stack”. I had thought, not paying much attention to Go in particular, it must deal with this with something like an Either type. Even some language facilities to automatically unwrap Either and rewrap for callers if unhandled would be a great answer to the checked exceptions debate. But what I take away from this is errors in Go are just as amorphous as in JS but harder to find. That’s quite a feat!
> If error isn’t typed it’s no different from “anything can throw anything anywhere in the stack”.
Errors aren't untyped. It's an interface type. Not everything returns errors anyway, they definitely can't be returned from anywhere in the stack.
Errors in Go are definitely nowhere near as amorphous as those in JS. In JS, you can take an Error object and stick additional properties on it, and then throw that. In fact, Node does that, and by extension, thousands of JS libraries generate exceptions like that.
With Go, you use errors.As(), or errors.Is(), or a little bit of dynamic casting. The type of error which a function returns may not be part of its type signature, but decent libraries usually have only one or two error types they return. Yes, I hit F12 to see the library's source to see what errors it returns... but that's just because the IDE experience with Go is so fast and nice to use.
My experience with Go is that decent libraries only have a small number of error return types, and then forward or wrap errors from libraries that they use. You can use errors.Is(), errors.As(), errors.Unwrap() to figure things out as necessary.
It's a pain because it forces users to consider and code against error paths.
Just like putting on a seat belt is a pain.
Both are pains that are worth having because they help you create robust software.
The ways in which a function can fail should absolutely be part of its signature, and if these are recoverable, the compiler should refuse to build until you either handle these errors or pass them on to the caller.
This post should not have been flagged. It is a completely valid criticism of Go. Author's tone could have been better but I'd rather hear straightforward opinions and not polished speech to offend those that cannot defend Go. Personally, I am a huge fan of Go but these are some valid points.
Anyone supporting such flagging behavior is completely against the spirit of what made this place great. Please reconsider your actions and what flagging does on HN.
Thanks for sharing this. It's very hard to lend credence to this series after reading this follow-up. Quoting from the link:
"There are certainly many reasons to hate Go, from the unusual syntax to the half-arsed way they decided to implement the programming model. On the other hand you can go all fanboy over how cool Go is because it makes it so simple to write microservices. (No, it doesn’t. It really, really doesn’t if you want to do it properly.)
If you jumped on either of these bandwagons, I would recommend getting off as soon as possible. Neither of these approaches are very professional and are telling signs of inexperience in software development."
There is an upsetting level of hypocrisy in patronizing your readers for consuming the clickbait-titled articles _you authored_.
> However, the larger problem is that functions don’t declare what kinds of errors they return.
They don’t need to. Static analysis (guru) can answer the question “what kinds of error can this error be?” There’s no need to go source diving as the author suggests.
IMO this is a common failure in discussions about programming languages today. You really must consider the capabilities of "external" language tools (linters, static analysis, etc).
Not having all that in the core language can be a reasonable design choice.
I am an amateur dev (for 25 years), using mostly Python. I was blackmailed by a friend to try Go.
Boilerplate, no exceptions, slices, ... all felt terrible.
And then I started to appreciate the typing and the fact that I had to give some thought about where to actually handle errors.
I know kinda like Go, except for the weird way maps are handled, and the lack of types in errors (compared to Python where it is fantastic).
There are certainly pros and cons (I am not a good enough developer to have advanced thoughts on that) but the cross compilation makes a lot of things easier when deploying self-contained executables.
There is one more reason. When you write something that works in a loop, e.g. web server, exceptions are a huge potential issue. I was working on a C++ project with a very complex state machine. One day our service started using all 128 CPU cores just for stack unwinding. It was an ordinary exception in 3thrd party library raised on every request in the loop. We have tried to make a lot of optimisations in different unwind libraries. But it was far away from the performance of a simple Optional-like errors container as a return value. Exceptions are really Zero-cost if they occur only once when application should be terminated.
The language is rough but the performance, tools, and deployment is top notch. That dichotomy was quite surprising to me.
If someone could write a "coffeescript" for it, perhaps a python+pascal love-child, while keeping the fantastic lower-level parts of go, I'd be all over it.
The last two companies I have been at switches from other languages to Go. At this point I don't see what people like about Go. This article touches on problems, but it is only the tip of the iceberg.
I'm not a Go fan but the language to me looks very readable and clean. However in my opinion syntax is usually not that important, you can and will get used to anything
No, it doesn't get any better when the codebase is bigger. The way to figure out WTF went wrong in a large code base written in Go is:
1. Take the line number where the error was logged, likely somewhere very close to e.g. the main event handling loop, and completely disconnected from the place where the error was initially thrown. There is no stack trace.
2. Try to figure out which parts of the error string are constant (i.e. searchable), and pray for the developer to have been familiar with the issue so that the string is globally unique (corollary: When writing Go, make sure your error strings are unique, introduce some form of variation if you're throwing similar errors, even if it's just punctuation or synonyms, to make sure the throw site is identifiable).
3. Try to guess what the flow between those two sites could have been.
The lack of proper stack traces (or even the line number where the error was thrown) on errors is one of the most insane effects of this error handling approach. (The other is hard to read code because the actual logic is hidden in 3x as much error handling boilerplate).
I’m not sure if I buy that. Did you read the article? The author is expressing explicitly what’s wrong in many areas and why they’re useful. I don’t like rebuttals like this because it then becomes a self-fulfilling prophecy of Go-lovers avoiding criticism from outside because “They won’t get it due to their background”. Great, now we’ve just created an echo-chamber. It’s good to get outside perspective and debate. I’m sure many Go experts would like to understand where they could improve - without losing Go’s core minimalism.
Author’s tone could be better but they’re just being straightforward that they don’t like Go and specifically why. Totally ok in my view.
I think this criticism is very unfair, if there is one thing java is (in)famous for, its large codebases. Kafka is written in it. I myself don't use it, but you need to substantiate your argument.
Is there any reason you believe python and java are 'very problematic"? Isnt every language promlematic in its own way? Are they more problematic than C, which breeds foodguns, or what are we comparing to?
[+] [-] bedobi|4 years ago|reply
I hope some day null is no longer a thing, and that Functional Programming types like Option, Either, Try etc in the native libraries is the new default. (BTW, there's really nothing "Functional Programming" about them, they're just objects like any other in "Object Oriented" languages)
[+] [-] angelzen|4 years ago|reply
[+] [-] mftb|4 years ago|reply
[+] [-] eyelidlessness|4 years ago|reply
[+] [-] klodolph|4 years ago|reply
> If error isn’t typed it’s no different from “anything can throw anything anywhere in the stack”.
Errors aren't untyped. It's an interface type. Not everything returns errors anyway, they definitely can't be returned from anywhere in the stack.
Errors in Go are definitely nowhere near as amorphous as those in JS. In JS, you can take an Error object and stick additional properties on it, and then throw that. In fact, Node does that, and by extension, thousands of JS libraries generate exceptions like that.
With Go, you use errors.As(), or errors.Is(), or a little bit of dynamic casting. The type of error which a function returns may not be part of its type signature, but decent libraries usually have only one or two error types they return. Yes, I hit F12 to see the library's source to see what errors it returns... but that's just because the IDE experience with Go is so fast and nice to use.
[+] [-] bigdict|4 years ago|reply
[+] [-] klodolph|4 years ago|reply
We've gone down this route with Java and my conclusion is that declaring the kind of errors a function returns is, for most users, just a pain.
https://www.artima.com/articles/the-trouble-with-checked-exc...
My experience with Go is that decent libraries only have a small number of error return types, and then forward or wrap errors from libraries that they use. You can use errors.Is(), errors.As(), errors.Unwrap() to figure things out as necessary.
[+] [-] hota_mazi|4 years ago|reply
Just like putting on a seat belt is a pain.
Both are pains that are worth having because they help you create robust software.
The ways in which a function can fail should absolutely be part of its signature, and if these are recoverable, the compiler should refuse to build until you either handle these errors or pass them on to the caller.
[+] [-] systemvoltage|4 years ago|reply
Anyone supporting such flagging behavior is completely against the spirit of what made this place great. Please reconsider your actions and what flagging does on HN.
[+] [-] nassimsoftware|4 years ago|reply
[+] [-] unknown|4 years ago|reply
[deleted]
[+] [-] kvnhn|4 years ago|reply
"There are certainly many reasons to hate Go, from the unusual syntax to the half-arsed way they decided to implement the programming model. On the other hand you can go all fanboy over how cool Go is because it makes it so simple to write microservices. (No, it doesn’t. It really, really doesn’t if you want to do it properly.)
If you jumped on either of these bandwagons, I would recommend getting off as soon as possible. Neither of these approaches are very professional and are telling signs of inexperience in software development."
There is an upsetting level of hypocrisy in patronizing your readers for consuming the clickbait-titled articles _you authored_.
[+] [-] jgrant27|4 years ago|reply
[+] [-] teeray|4 years ago|reply
They don’t need to. Static analysis (guru) can answer the question “what kinds of error can this error be?” There’s no need to go source diving as the author suggests.
[+] [-] lokar|4 years ago|reply
Not having all that in the core language can be a reasonable design choice.
[+] [-] ed_elliott_asc|4 years ago|reply
[+] [-] aranchelk|4 years ago|reply
Also I’m guessing the section on immutability isn’t referencing something special in Java, dunno.
[+] [-] deepsun|4 years ago|reply
[+] [-] animex|4 years ago|reply
[+] [-] bfung|4 years ago|reply
https://news.ycombinator.com/formatdoc
[+] [-] eyelidlessness|4 years ago|reply
[+] [-] BrandoElFollito|4 years ago|reply
Boilerplate, no exceptions, slices, ... all felt terrible.
And then I started to appreciate the typing and the fact that I had to give some thought about where to actually handle errors.
I know kinda like Go, except for the weird way maps are handled, and the lack of types in errors (compared to Python where it is fantastic).
There are certainly pros and cons (I am not a good enough developer to have advanced thoughts on that) but the cross compilation makes a lot of things easier when deploying self-contained executables.
[+] [-] nunez|4 years ago|reply
(I actually quite like Go, despite it being more difficult to write tests for than other languages, and error messages usually being very cryptic.)
[+] [-] NelsonMinar|4 years ago|reply
https://golang.org/doc/faq#exceptions https://go.dev/blog/errors-are-values
[+] [-] vasilia|4 years ago|reply
[+] [-] mixmastamyk|4 years ago|reply
If someone could write a "coffeescript" for it, perhaps a python+pascal love-child, while keeping the fantastic lower-level parts of go, I'd be all over it.
[+] [-] ohCh6zos|4 years ago|reply
[+] [-] nassimsoftware|4 years ago|reply
[+] [-] rtoway|4 years ago|reply
[+] [-] ronnier|4 years ago|reply
[+] [-] yannoninator|4 years ago|reply
[+] [-] nobody0|4 years ago|reply
[+] [-] deltasixeight|4 years ago|reply
[+] [-] didip|4 years ago|reply
It makes PR review job so much easier, and to me, that's beautiful.
[+] [-] jgrant27|4 years ago|reply
Maybe it's not Go that is a terrible language but just that the author is not a systems programmer who has worked with large code bases ?
[+] [-] tgsovlerkhgsel|4 years ago|reply
1. Take the line number where the error was logged, likely somewhere very close to e.g. the main event handling loop, and completely disconnected from the place where the error was initially thrown. There is no stack trace.
2. Try to figure out which parts of the error string are constant (i.e. searchable), and pray for the developer to have been familiar with the issue so that the string is globally unique (corollary: When writing Go, make sure your error strings are unique, introduce some form of variation if you're throwing similar errors, even if it's just punctuation or synonyms, to make sure the throw site is identifiable).
3. Try to guess what the flow between those two sites could have been.
The lack of proper stack traces (or even the line number where the error was thrown) on errors is one of the most insane effects of this error handling approach. (The other is hard to read code because the actual logic is hidden in 3x as much error handling boilerplate).
[+] [-] systemvoltage|4 years ago|reply
Author’s tone could be better but they’re just being straightforward that they don’t like Go and specifically why. Totally ok in my view.
[+] [-] ClumsyPilot|4 years ago|reply
Is there any reason you believe python and java are 'very problematic"? Isnt every language promlematic in its own way? Are they more problematic than C, which breeds foodguns, or what are we comparing to?