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.
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.
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.
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.
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.