I'm also not seeing why this is necessary. Range over int is pure syntax sugar, and while range over func could be useful, iterators can be easily implemented with current Go features.<p>I love Go because of its simplicity, and because it strictly adheres to the "there's only one way to do it" mantra. Adding syntax sugar muddles this, and introducing another keyword and construct makes the code less readable.<p>I hope that this proposal is rejected, and that it's not a sign of Go becoming yet another kitchen sink language.
This approach feels quite invasive compared to adding a standard interface that `range` accepts. Some discussions have happened before, eg <a href="https://github.com/golang/go/discussions/54245">https://github.com/golang/go/discussions/54245</a><p>A standard interface feels a cleaner than these nested callback functions.
This is a weird approach. They are essentially manually implementing the “yield return” functionality from c#.<p>C# defines an interface IEnumerable<T> with a “next” method, and originally you’d need to implement this interface which was a bit of boiler plate.<p>They added “yield return” which would create compiler generated syntax sugar to return a generator function like this go proposal, but your code looked identical to a non iterative version.<p>The go proposal feels like the worst of both worlds and like the team misunderstood the purpose.
I'm guessing this is following the same / highly similar motivation as rsc's coroutines post about a month ago: <a href="https://research.swtch.com/coro" rel="nofollow noreferrer">https://research.swtch.com/coro</a><p>I welcome people embracing function closures more, as I feel they're a drastically under-used language tool (particularly in Go where everyone crams everything into single-func interfaces which are <i>almost</i> exclusively far worse). More exposure to it helps people become comfortable with it, and now that we have generics it's finally not a pile of pain and agony at all times. And <i>just using functions</i> makes it very flexible / doesn't repeat the "X for me but not for thee" issues of past Go versions.<p>I'm not entirely sure how I feel about this proposal though. It kinda feels like it's trying to put a round peg into a square hole. If you try hard enough, everything can go in the square hole! But that doesn't mean it's a good fit.
Iterators are great, I really loved simplicity of `yield` in c# and python.<p>Go's proposed syntax is god awful. Why they are doing that to devs and themselves? Should we expect it to improve in like 15-20 versions, like it happened with any and generic mix/max functions?
This looks like a very complicated way of solving something for which there is already a solution: channels and contexts. Here's the pattern I often use:<p><a href="https://go.dev/play/p/mrQFLAc11QU" rel="nofollow noreferrer">https://go.dev/play/p/mrQFLAc11QU</a><p>It should be doable to express this somewhat simpler using a (genericized) helper function to take out some of the work of writing the ListItems function.<p>Since there is already a way to do this I don't think the language should be cluttered with more ways to essentially do the same thing. It is better to push people to develop a vocabulary of idioms that uses fewer features rather than introduce "more stuff".
Not sure how I feel about this particular approach to iterators. I'd love for there to be <i>some</i> generate way to `range` over a generic collection, but idk about this.
Wait, so you return `false` to terminate the iterator? Stupid!! Now I can’t have an iterator that returns Booleans.<p>UPDATE: I misread the article. This isn’t what happens. My bad.<p>Honestly, I feel like Go focuses so much on superficial simplicity that you wind up with bad/leaky abstractions all over the place as well as bad primitive obsession.<p>/rant
I have no opinion on this specific feature, but I have a general feeling that by far the hardest thing for language developers is to know when to stop.<p>I feel this might be the case because language developers are themselves the worst reference for a typical user. They’re experts and no matter how big a language gets, they easily wield it. Even if they recognize that complexity is going up, they can’t truly grok the cost of that.<p>I think Bob Ross talked about this often. That it can be tricky to know when you’re done. And it can be easy to clutter the canvas.
This design avoids the inefficiencies and most of the problems of having an?_iterator_, like the classical Java `hasNext`/`moveNext` or C# `MoveNext`/`Current` interfaces.
Those iterators require you to keep the state between elements as values, effectively CPS transforming and defunctionalizing the control flow of actually iterating the elements.<p>This iterator function here is really just a `forEach`/`Each` method where the callback can return a boolean. You can't easily use that to split iteration, say iterate the first five elements, then go do something else, and come back to iterate the rest, doing something completely different.<p>But that also means it doesn't have to worry, as much, about the iterator going stale because the underlying structure changes (`ConcurrentModificationError`), or not knowing when to clean up, because somebody might call `hasNext`at any later time.<p>So this is basically just a nicer `forEach` method.
I’ll cop to not having paid much attention to Go generics, but handwaving over my dubious syntax, what’s wrong with:<p>```<p>type Iter[T] interface {
More() bool
It() T
}<p>type mapOver[T] struct { Iter[T]; func (T) T f }<p>func (m mapOver[T]) It() T {
return m.f(m.Iter.It())
}<p>func Map[T] (it Iter[T], f func(T) T) Iter[T] {
return mapOver{it, f}
}<p>type Range { to, at int }<p>func (r Range) More() bool {
return r.at < r.to
}<p>func (r Range) It() (is int) {
is = r.at;
r.at += 1
}<p>func DoEach[T] (it Iter[T], f func(T) T) {
for it.More() {
f(it.It())
}
}<p>// etc<p>iter.DoEach(iter.Range(10), fx.Stdout(fns.MultBy(2)))<p>// or whatever<p>```<p>Translating to English:<p>If you can do it just fine with the syntax you have, why do you need to add it to the core language?
one of the reasons I love Go is because of its simplicity. It just works and it works well.<p>You can even use with without GC if your code has to be fast. I don't see what the fuss is about with other new statically typed languages.<p>So I oppose almost anything that will make Go less simple. Probably the only exception I would make is enums.<p>I don't want my brain to have to work any harder reading code.<p>Edit adding:<p>the whole point of Go is simplicity and speed. That's why it's called Go in the first place.<p>Let's keep it that way.
This proposal seems pretty bad to me.<p>I think Go lacks the features to make this ergonomic and flexible.<p>Before adding iterators the language should add tuples so that you won't need to special case 0,1 and 2 artity functions.<p>And shorter syntax for anonymous functions to make writing this less verbose, I believe this has been proposed before.<p>Also the special case for "0..N" ranges seems very unnecessary.
I have used some iterator libraries for go and they do make for much more manageable code, especially if you start nesting iterators, like iterating over JSON which reads from an iterator over a blob from blob storage. And it also makes it simpler to apply filters at various points. Using callbacks also works but makes for nastier code.
Hmm, it looks interesting but it seems to lack a precise need that's not covered atm imo.<p>I also wonder how defer would be handled. After the last yield? After each yield? Should defer be executed when an iterator is not fully consumed?
I much preferred the iterate by function approach described by one of the blogger guys a while ago (Muratori? Bendersky? Cassettieri? I can't quite recall the name atm).
> But there are some disadvantages to this way of doing it. First, we have to wait until the whole slice has been generated before we can start to process the first element of it.<p>Uh, range over channel ? The author seems to be very bad at go<p><pre><code> func Items() (items chan Item)
</code></pre>
will return channel that then can be just<p><pre><code> for a := range Items() {}</code></pre>
a) one item at a time means slow because when you are iterating maps/slice/array you have it all in memory so it is fast. with one item you have to constantly rewrite memory, move data and whatnot<p>b) yo can always use for loop in a traditional for way with standard arguments(init, stop, next)<p>c) yield is poor way of doing channels, i do not see why we should be degrading our code<p>d) this entire premise is stupid :D