Hacker News new | past | comments | ask | show | jobs | submit login
Go is not an easy language (2021) (arp242.net)
71 points by psxuaw on Jan 13, 2024 | hide | past | favorite | 34 comments



I was writing a more involved change in golang for the first time and was surprised to find golang didn't support higher order functions like map and filter. It's not a big deal to have to implement them in a custom for loop but for a language that sells on simplicity, I was surprised to find these missing. I guess I have a different idea of simple than the golang team.


Higher order functions are not easy to grasp for beginners, but once you do, they simplify reasoning about the code a lot.

Go is targeted at system programming, which is a domain in which you need pretty advanced developers. But the language feature seems to favor use by absolute programming beginners. It's a trade-off which seems somehow inverted.


Go was designed to be easy to learn for inexperienced developers.

Is it actually intended for systems programming? Its main (and intended) use seems to be a faster and better at concurrency and parallelism alternative to Python, Ruby etc.


Rob Pike was after C++ programmers initially, turns out that naturally the language isn't appealing to that community.

> Although we expected C++ programmers to see Go as an alternative, instead most Go programmers come from languages like Python and Ruby. Very few come from C++.

https://commandcenter.blogspot.com/2012/06/less-is-exponenti...


I messed around with Go early on but haven’t touched it since (I mostly do numerical work these days) but this tracks with what I expected from the language. It felt reactionary to the C++ monster. It felt like the early goal was to make the language as simple as possible, like C, and add quality of life improvements that don’t increase complexity, especially for parallelization with the go routines.


There’s a talk by Rob Pike where he mentions that it would have been difficult for code using map, filter, etc. to be as fast as the equivalent code using a for loop without a sufficiently smart compiler.

So I think the motivation was more implementation simplicity . But if they had tried to add zero cost iterators it probably would have leaked complexity into the language too.


I can appreciate Go's design philosophy of simplicity and emphasizing fast tooling + one way to do things.

But as someone who has done a ton of functional programming, to me it is just too conservative of a language. There actually has been progress in the PL space the past 50 years, and I personally prefer a language that includes them.

I think OCaml strikes a better balance between power and simplicity, it is probably the closest to Go in the functional programming space (and is also a systems language). It has a blazingly fast compiler, it's easy to understand how it executes things, compiles down to a single binary, the tooling is good. Very similar advantages to Go.


This was my impression coming to Go as well (from Python and PHP) but since then I've changed my mind and somehow find it easier to unpack a small for loop than a HOF with a lambda function argument.

When you introduce lambdas/closures/HOF you immediately have to think about scope rules, non-local references etc.

Meanwhile, a foor loop is just a for loop.


Until you spawn a goroutine from a loop and suddenly you have to think about scope rules, nonlocal references, etc...

(It doesn't bother me, just noting the same complexities do come up pretty often).


BTW this is actually being addressed, specifically the for loop now essentially captures by value instead of by reference. I believe no real working programs were affected by the change, except for maybe fixing bugs in them.

Proof: https://go.dev/blog/loopvar-preview


Map and filter name what they’re doing, while loops look too much alike.

I haven’t checked whether any Go compiler does list fusion between producers and consumers (to avoid leaving a slice in the heap after reading it just once).


This wasn't feasable without generics, and now with generics they're already adding some convenience functions to the stdlib, like in the slices package.

For map, reduce etc it's not in the stdlib yet, but you can use https://pkg.go.dev/github.com/samber/lo


Generic iterators are included in go 1.22 behind an experimental feature flag and will most likely be in 1.23 by default. Once that's done you will probably see some support functions like map and filter in the standard library. Russ Cox already has a proposal for an experimental package xiter that includes Map/Filter/Reduce/Zip etc.


Go is not a functional language.

The most common complaint against Go seems to be "My favourite programming language has X, why doesn't Go has X??"

There are things I would like to see in Go (e.g. real enums and the ? operator for error handling), but I DONT WANT Go to become a copy of C# or Haskell...


Go is so minimalistic that it forces developers to reinvent the same things again and again. Just think about how common certain collection operations are and how easy they are in some languages (e.g. Java, Kotlin, C#, Rust), but in Go you'd write list and for loops like in the last century. This wastes developer time, makes the code harder to understand and adds chances for bugs.

So, this cite applies to Go: “Everything should be made as simple as possible, but no simpler.”


As someone dabbling in Go, still a n00b really, but experienced in many other languages, I find this too, why's there stuff that's just standard in e:g Python, yet missing in Go? Apparently a thought-out, deliberate decision by the language designers. But then, since many people feel this way about Go, I'm mystified that 3rd-party libraries haven't evolved to cover "missing features" of Go. One would think, someone will scratch an itch, release it, then everyone uses that. Seems to have happened in other languages. Weirdly, there are indeed 3rd-party libs for Go that do more sophisticated things, web frameworks etc. Its almost as though 3rd-party devs don't dare touch the lower level stuff cos that's "against the spirit" of Go, but do make reusable libs for higher level things. Well, this comment may reflect my naiveté / ignorance of the language. Maybe someone else will clarify this? ;)


There are some libraries that add the kind of utility functions you're talking about; I'm fond of samber/lo [1] myself. I don't know how widely used they are, though.

[1] https://pkg.go.dev/github.com/samber/lo


Go is simple like C is simple. Add a garbage collector and concurrency and it’s easier to do some things but the simplicity of the language means you have to intimately understand more of the runtime to not shoot yourself in the foot.


People tend to underestimate how much effort it takes to a) learn how to be a good programmer, and b) learn a given programming language. Sometimes they confuse the two.


Yeah. Tons of people claim to be able to pick up languages within a week, or a month. I’m convinced it takes at least a year to get out of beginner status, even for simple languages. I’m including things like knowing the ecosystem and popular libraries (http server and clients, ORMs, testing frameworks, …) in that.


Yeah, I often wonder what threshold people use when they say they picked up a language in x hours/days/weeks. I guess it's largely a matter of opinion, but like you I consider a working understanding of the standard library, popular packages in the ecosystem etc. to be beginner level stuff, and for me it takes at least a few months to start getting a grasp on these things (especially in something like go which has a pretty big standard library).


As you point out there's a lot to cover. There's the language, the libraries, and the tooling. Then there's learning how different people use the language. The stylistic elements, how to play to the strengths of the language, how to deal with the shortcomings etc. All that takes time because you have to gain some experience with all of this. Even the things you figure out aren't very good.

The reason it took me a long time to learn Java is because when I started learning Java there was a certain way to architect Java systems. And to be frank, the way Java was done in the 90s was horrible. Lots of complex boilerplate, lots of structure and none of it seemed to pay off. And on top of that, most people still didn't understand OO all that well so you'd get layer violations and strong coupling all over the place. Making code really hard to maintain. (I used to ask people to read the first part of the GOF book, and forget about the rest of the book. Because at that point, the introduction contained the best explanation of OO we had found. It is probably still worth reading. Though the rest of the book led to a lot of silly dogmatism where some people suddenly behaved as if the patterns were the only allowable design strategies)

Then came the 00s with people adopting huge frameworks that in themselves took a lot of time to learn and, again, didn't produce the results we wanted. Worse yet, applications became hostages to these frameworks. Ever been faced with a few hundred thousand lines of code trapped inside a Spring-dictated architecture. That's really, really expensive to do something about.

It took a really long time to figure out a good way to do software in Java. It also took a very long time to re-train Java developers whenever we got a new hire. To teach them anything from "microdesign" (how you express things in Java) to how you design, plan and architect stuff without ending up with a lot of


Do you think you’re doing Java correctly now?

Do you have reason to believe you won’t be looking back in 10 years’ time, realising you’ve been doing it wrong in 2024 still?


I don't think it is about doing it "correctly". I don't think there is a "correct". There's stuff that works and then there's stuff that is a waste of time. For many years, most Java practitioners wasted time doing things that don't actually work.

It is about finding a practice that works better than the other ways that you have been exposed to and then try to figure out if you can write it down and explain it in a way that makes other people more productive and more capable of producing quality. Of course, that requires you to be exposed to a lot of practices and having the ability to figure out if they work. Not all programmers have that ability.

Some people find plateaus that are better than other plateaus. If you teach other people what you do then sometimes people think of that as the way to do it until someone else improves on it. Or someone finds an entirely new direction to go in that is perhaps even better.

> Do you have reason to believe you won’t be looking back in 10 years’ time, realising you’ve been doing it wrong in 2024 still?

A lot of the thinking that went into how we did Java evolved slowly over a couple of decades and we eventually started seeing people come around to our point of view. We just started a bit earlier than most. And it wasn't just informed by how we did Java. A lot of ideas came from how we did things in other languages as well.

Things like avoiding large frameworks that lock you in and dictate design choices, embedding servers rather than loading the application into the server (which never actually worked), or even making self-contained applications that have no external dependencies, were, to varying degrees, controversial at different points in history. Even the idea of treating servers like cattle rather than pets was controversial at some point.

Some of the things we spent a lot of time thinking about I don't see wide spread awareness of. Like designing for evolvability which is very, very different from over-engineering. Those bits are still hard to explain - and especially when people have short attention spans. But they are just as important today as they were 15 years ago. (I'm still terrible at explaining how we design for evolvability)

(I stopped doing Java around 2016. But most of the practices we developed live on in how we do Go. I think the reason switching to Go was so easy and happened so fast was that Go was very compatible with what we tried to do in Java)


This has been my argument since I started using it. It's definitely simple but the easiest stuff can be such a pain.


It's really the other way around : it's simple, which makes it not easy to use.

The way Arch Linux defines simplicity is "Arch Linux defines simplicity as without unnecessary additions or modifications."[0]

Go is simple because it only really has what you NEED. People complain about wanting this and that feature, but they don't really NEED it, it would just make writing code easier.

Personally I love that because if there's a billion ways to do something, I waste all my time trying to figure out the best way to do it instead of working on the original problem, but I can see why people who are not like me would prefer more features.

* https://wiki.archlinux.org/title/Arch_Linux#Simplicity


But Arch let's me make my system as complicated as I want after providing the minimalist base layer. It gives almost total freedom in that respect.

I think it's more like a Lisp than Go.


I feel like the argument is van to front here. Go is easy, the problem is it's not simple.


"Back to front"?


pop() would have been a much better example, as ruby providing delete_at is a mistake IMO, since it runs in O(n), chances are that you're using the wrong data structure. Go providing append but not pop is inexplicable


There are many, many situations where delete_at is a completely reasonable operation. It may be O(n) but it is a blazing fast O(n) as it is just a memmove. For example if you are writing a TODO app and you want to delete a task it is likely a very acceptable solution. You would need millions of tasks before you couldn't remove it withing a few milliseconds. Most other solutions even though they have better time complexity would be slower in practice. (But benchmarks would be needed to verify this).

I would say that the biggest concern with delete_at is if you are using it inside another O(n) loop, then it becomes O(n^2) which does scale up fast enough to become a performance issue.


There is nothing wrong with O(n). It can be faster than O(1) depending on the size of n and what you're doing (small arrays are usually faster than hashmaps for example).

Big-O just tells you how something scales. It doesn't really tell you how fast something is.


Just a side note, Golang has generics now: https://go.dev/doc/tutorial/generics

Google says that only since Feb 2022, and the article was written in 2021, but I think it was a planned feature and if the author did some research, he'd realize that generics are coming.


I was quite unhappy when they added generics to golang. That seemed like a slippery slope to C++ or Rust like monster.

I much prefer the pre generics Go. Crisp and compact.

Go is like Prostaff 90 upgrade to the great PS 85. It is not the plasticky Babolat APD 100 that beginners love.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: