←back to thread

Go is still not good

(blog.habets.se)
644 points ustad | 2 comments | | HN request time: 0s | source
Show context
blixt ◴[] No.44983245[source]
I've been using Go more or less in every full-time job I've had since pre-1.0. It's simple for people on the team to pick up the basics, it generally chugs along (I'm rarely worried about updating to latest version of Go), it has most useful things built in, it compiles fast. Concurrency is tricky but if you spend some time with it, it's nice to express data flow in Go. The type system is most of the time very convenient, if sometimes a bit verbose. Just all-around a trusty tool in the belt.

But I can't help but agree with a lot of points in this article. Go was designed by some old-school folks that maybe stuck a bit too hard to their principles, losing sight of the practical conveniences. That said, it's a _feeling_ I have, and maybe Go would be much worse if it had solved all these quirks. To be fair, I see more leniency in fixing quirks in the last few years, like at some point I didn't think we'd ever see generics, or custom iterators, etc.

The points about RAM and portability seem mostly like personal grievances though. If it was better, that would be nice, of course. But the GC in Go is very unlikely to cause issues in most programs even at very large scale, and it's not that hard to debug. And Go runs on most platforms anyone could ever wish to ship their software on.

But yeah the whole error / nil situation still bothers me. I find myself wishing for Result[Ok, Err] and Optional[T] quite often.

replies(18): >>44983384 #>>44983427 #>>44983465 #>>44983479 #>>44983531 #>>44983616 #>>44983802 #>>44983872 #>>44984433 #>>44985251 #>>44985721 #>>44985839 #>>44986166 #>>44987302 #>>44987396 #>>45002271 #>>45002492 #>>45018751 #
kace91 ◴[] No.44983531[source]
My feeling is that in terms of developer ergonomics, it nailed the “very opinionated, very standard, one way of doing things” part. It is a joy to work on a large microservices architecture and not have a different style on each repo, or avoiding formatting discussions because it is included.

The issue is that it was a bit outdated in the choice of _which_ things to choose as the one Go way. People expect a map/filter method rather than a loop with off by one risks, a type system with the smartness of typescript (if less featured and more heavily enforced), error handling is annoying, and so on.

I get that it’s tough to implement some of those features without opening the way to a lot of “creativity” in the bad sense. But I feel like go is sometimes a hard sell for this reason, for young devs whose mother language is JavaScript and not C.

replies(3): >>44983704 #>>44986562 #>>44991271 #
j1elo ◴[] No.44983704[source]
> People expect a map/filter method

Do they? After too many functional battles I started practicing what I'm jokingly calling "Debugging-Driven Development" and just like TDD keeps the design decisions in mind to allow for testability from the get-go, this makes me write code that will be trivially easy to debug (specially printf-guided debugging and step-by-step execution debugging)

Like, adding a printf in the middle of a for loop, without even needing to understand the logic of the loop. Just make a new line and write a printf. I grew tired of all those tight chains of code that iterate beautifully but later when in a hurry at 3am on a Sunday are hell to decompose and debug.

replies(5): >>44983856 #>>44984023 #>>44984735 #>>44985816 #>>44986632 #
williamdclt ◴[] No.44983856[source]
I'll agree that explicit loops are easier to debug, but that comes at the cost of being harder to write _and_ read (need to keep state in my head) _and_ being more bug-prone (because mutability).

I think it's a bad trade-off, most languages out there are moving away from it

replies(1): >>44985443 #
nasretdinov ◴[] No.44985443[source]
There's actually one more interesting plus for the for loops that's not quite obvious in the beginning: the for-loops allow to do perform a single memory pass instead of multiple. If you're processing a large enough list it does make a significant difference because memory accesses are relatively expensive (the difference is not insignificant, the loop can be made e.g. 10x more performant by optimising memory accesses alone).

So for a large loop the code like

for i, value := source { result[i] = value * 2 + 1 }

Would be 2x faster than a loop like

for i, value := source { intermediate[i] = value * 2 }

for i, value := intermediate { result[i] = value + 1 }

replies(2): >>44985804 #>>44985910 #
tuetuopay ◴[] No.44985804[source]
Depending on your iterator implementation (or, lackthere of), the functional boils down to your first example.

For example, Rust iterators are lazily evaluated with early-exits (when filtering data), thus it's your first form but as optimized as possible. OTOH python's map/filter/etc may very well return a full list each time, like with your intermediate. [EDIT] python returns generators, so it's sane.

I would say that any sane language allowing functional-style data manipulation will have them as fast as manual for-loops. (that's why Rust bugs you with .iter()/.collect())

replies(2): >>44985862 #>>44986036 #
1. maleldil ◴[] No.44986036[source]
Python map/filter/zip/etc. return generators, so they're lazily evaluated.
replies(1): >>44987089 #
2. tuetuopay ◴[] No.44987089[source]
Thanks, I was not sure, hence the "may". Comment edited :)