Hacker News new | ask | show | jobs
by fourseventy 682 days ago
I don't like the "range-over-func" addition. I feel like it is adding complexity and syntactic sugar to the language that has thus far been mostly avoided.
6 comments

Hard disagree. A standard way to define iterators was long overdue, and using the existing `range` construct was the right solution.

This change will end up removing some mental load, at an extremely low complexity cost. Basically you define an iterator just by defining a function. No additional keyword, no state to manage, just a function.

The first time I looked at the syntax, it honestly looked like gibberish to me.

It's probably simple, but the control flow just felt convoluted.

Are you talking about

for key, val := range m.Range

I like that a fair bit more than

m.Range(func(key, val any) bool {

But now I have to understand what m.Range does, and I can't assume it's a slice or a map etc. Prior to this change, you could almost always correctly assume what a for loop is doing as it was very primitive.

It's changes like this that will make Go less productive for users over time, IMO. Java-fication.

You put your cursor on the "Range" portion of m.Range, and hit "Jump to Definition".

You can also just not import things that do crazy things with iterators. Based on the history of the community, that will actually be fairly easy. I'm yet to see anything crazy with generics get into a library that I use. I'm abundantly positive there's going to be a dozen "hey let's go do crazy things with rangefunc" libraries in the next couple of weeks, probably some will even make it to HN (with predictable comments bemoaning how complicated Go is getting even though these libraries have an expected use rate in the low dozens of people in the next few years), but the odds of them penetrating into common practice remain low. (Higher than previous attempts at such libraries, because rangefunc fixes some basic issues with them. But still low overall, I think.)

I think you could go many, many years programming Go in the next few years and not encounter any funny iterators in real code. If you just assume the iterator is doing what it looks like it should be doing and isn't doing anything funny, you're going to be 99%+ correct in the Go programming world.

> You put your cursor on the "Range" portion of m.Range, and hit "Jump to Definition".

Well, that's one reason why people usually run away from languages high in magic like Java/JS to something like Go: not needing a fancy IDE to make sense of what's happening with a single line of code.

Even if you don't have any help from an LSP, I don't really see how this change adds any meaningful complexity.

With `for x, y := range m.Range`, you know you are iterating over something, and you are getting an `x` and a `y`. For the purposes of understanding the range block and what it does, it doesn't really matter what `m.Range` is, only that it allows iteration. Just like you don't care in <=1.22 land whether `m.Range` is a slice, map, or channel, because for the purposes of understanding the code, that doesn't matter.

You seem to have forgotten that it's possible to range over channels.
Same. This is one of the first times I'd checked release notes and not really understood most of it at first glance.
Not a fan either, this release feels like it’s shoehorning FP into Go.
The change adds no syntax.
"for key, val := range m.Range" is syntactic sugar for using the m.Range iterator directly "m.Range(func(key, val any) bool".

In a vacuum I think it's fine, but I don't want to see stuff like this getting added to the language routinely. I prefer a small language.

Go grew too big (in popularity) to be small. Popular languages tend to grow as the userbase grows. This is natural — the language gets used across a wider range of industries, and the programs being written in the language become larger, more mature and more diverse.

The language maintainers need to balance a small specification or a small implementation versus the users' demand, but in most cases at least some user demands win routinely. This is especially true when the language has just one major implementation. There is a significant cost for making that implementation more complex, but there is a more significant cost for making millions of programs that rely on a single implementation more complex just to make that implementation simpler.

I think the key difference is that in a loop body you can return from the outer function. In a function passed to m.Range you can't.
Go 1.22 is still right there.