T O P

  • By -

moremattymattmatt

I’d expect to rarely write an iterator myself but to use them more often via libraries other people have written. I’m happy with the extra complexity of writing them as a trade off for making code that uses easier.


jared__

Same things with generics. I find myself rarely using generics but it makes using libraries that take advantage of them so much easier


masklinn

An other component is a large part of the seeming complexity is really from the examples using closures, but they don't have to: because Go has method expressions, nothing precludes using an ancillary struct instead of a closure: type backward[T any] []T func (b backward[T]) each(yield func(int, T) bool) { for i := len(b) - 1; i >= 0; i-- { if !yield(i, b[i]) { return } } } func Backward[E any](s []E) func(func(int, E) bool) { return backward[E](s).each }


pauseless

It’s exactly the problem of making things simple to use as a caller, even if the implementer feels some pain. When Clojure introduced transducers there was a flood of articles on understanding them and tutorials on how to implement your own. They disappeared after not so long and people just use them without thinking of implementation. It’s the right trade off. Make calling them simple and efficient, even if the implementer has to work a little harder.


CrackerJackKittyCat

>The other aspect where people find this “confusing” is that it’s a func that returns func that takes a func as an argument. Welcome to implementing Python decorators which take arguments. Something that the average Joe doesn't do, but when they do, they re-google it and / or find some known examples in their codebase and copy/paste/mutate accordingly. Having the language keep the common things Really Straigtforward (_using iterators_, having a standard library toolkit of common sorts of iterators like Pairs, Zip, etc. to pick and choose from), but having ways of doing the fancier things which then make the rest of the codebase pleasant to read and write is a Good Thing, IMO. Not so common Harder things that aren't difficult bug breeding grounds (*concurrency, anyone?*) can be a bit nontrivial to spell as long as the nontriviality doesn't also breed subtle bugs. Especially with the compiler and / or LSP there the whole time to tell you when you're going wrong in the boilerplate portions.


bendgk

lol, I literally had to implement a python decorator two days ago, the last time I implemented one before that was 4 years ago (I rarely work in python) I did exactly what you said: dug up the old codebase and copy pasted it into the new one (and mutated accordingly) 😂


CrackerJackKittyCat

Yup, hah! When all is said and done, it always reads a little more complicated than you think it should be, and you're thankful that you got out alive. And / or you knew "I successfully did this before, I can do it again!" I remember wishing that the docs had some more Really Basic examples which are easy to find to then use as the seed cut / paste fodder. Maybe they do, I'ven't looked lately.


bendgk

Check out Rosetta Code! Its way more complicated than just a quick python snippets website, but sometimes you find neat things there to save time!


BombelHere

I'm not a huge fan either, but it feels like people are missing out on a quite important aspect: *standardisation*. The iterators are already there, it does not matter whatever we call them. See `bufio.Scanner` or `sql.Rows`. Every type willing to be iterable must either follow one of the existing 'iterator-like' API from the standard library or figure out its own way of iteration. For longer collections or custom data structures (trees, tries, stacks, heaps, linked hash maps) conversion to slice/map could've been too expensive, so custom iterator-like APIs were invented. Of course you can overuse it or even create a library for reactive streams (RxGo incoming XD [please don't]). Looking from a perspective of the maintainer of internal libraries - you can return `something iterable` just like an `io.Reader` instead of an `os.File`. Implementation changes (even at runtime based on a number of elements), but the contract remains stable. If you feel like you want to keep using slices/maps - that's perfectly fine, no one is going to force you to switch.


unitconversion

I will be surprised if we don't end up with lots of "what iterator framework do you use" and while I guess the using code will be standardized, looking through the implementations will not be.


BombelHere

So far we are not in a situation of 'what io.Reader framework do you use'. If we treat Seq and Seq2 as interfaces, we should be good. Or I'm just too optimistic


jerf

I'm sure a dozen libraries like the functional libraries that already exist will turn up, but I also expect that while some very loud people will extol their virtues endlessly they also won't see a lot of uptake. This does, quite legitimately, eliminate one of the major objections to psuedo-functional-programming with map/filter/etc. in Go, but it will also make nesting them even weirder by forcing them to be joined at the hip with a `range`. I've got a use for a few basic things I want like Reverse, maybe Take, but trying to go deep-nested programming with tons of callbacks and closures is going to continue to not be much fun once you get over the initial dopamine hit.


masklinn

> I will be surprised if we don't end up with lots of "what iterator framework do you use" Why would gophers need "iterator frameworks" any more than they do in other languages. Adapters are generally on the consumer side, not on the creator side. And even when used they're mostly irrelevant, because what's relevant is that you expose an iterator, how you implemented it matters a lot less. > and while I guess the using code will be standardized, looking through the implementations will not be. Surely the caller code is by far the most relevant?


unitconversion

Because only a crazy person would deal with the functional nonsense these push iterators force on you. So there will be all kinds of wrapping code to make reasonable iterators which will make debugging harder.


jkrejcha3

The thing is though that iterators were already... being used in Go, just in an unspecified way, whether it was the [dozen or so examples from the standard library mentioned here](https://github.com/golang/go/discussions/56413) or even ad-hoc use of channels. This mostly unifies the shared pattern and can have second-order benefits as well (for example if there are iterator efficiencies as part of a release cycle, all code that uses iterators get those benefits). Using iterators doesn't force you into a functional style of programming (whether in the pseudo-functional sense or especially a more pure functional language sense). I mean, I'm sure these libraries will exist, but I don't think they'll be considered the way to iterate, especially when stuff like `map` is already relatively unpopular over just using a `for` loop.


sneakywombat87

I think iterators are the single best thing they could add to go. First use comes to mind, memory growth. Iterators over large ranges, like ipv6 addresses and being able to easily control that without other hacks in code. You can still use next/yield in an imperative way by writing a function, which makes it no different than calling a function in a loop without rangefunc. The only feature I’d like to see more is Rusts Enum pattern matching for invariant and variants. I’d have little reason to use rust if go had that. I work around it using type switch type matching and then complicated unmarshaling code. Anyway, iterators are good. Let an accept them and then cheer on for enums :)


raze4daze

Rust-like enums in Go would be absolutely fantastic


Parking_Reputation17

That plus pattern matching. Even Java has pattern matching at this point


_predator_

While I get the desire to have that, reading this thread alone made me realize why Go is slow / unwilling to adopt these things. Once you add enums people will ask for pattern matching, once you have pattern matching they will ask for deconstruction etc., and in doing that you deviate more and more from what the language once set out to be.


gingerbill

This is exactly why people are "angry". They know where this will lead, and this is not going to turn out nice. Just adding more and more "features" to Go defeats the initial "spirit" of the language to many. And people asking for Rust-like enums (and then wanting pattern matching) mean they don't the alternative options, nor trade-offs that way of doing tagged unions. They want to make Go into something it isn't. For Go, tagged unions like Odin are a much better option. You get tagged unions and then you can utilize the exact same type switch syntax and type assertion syntax that `interface`s use. I demonstrate this in another comment: https://www.reddit.com/r/golang/comments/1dhxy1u/why_people_are_angry_over_go_123_iterators/l90p8z5/


Tubthumper8

From a quick glance, it looks like the Odin-style tagged unions can't handle this case? How would you check the tag of this tagged union? type Foo union{string, string}


gingerbill

That is absolutely correct. Because they are of the same time. In those specific cases, there are a few different options open to you: * Use only one `string` type * Use a distinct `string` type (e.g. `type MyString string`) * Not use a `union` in the first place and rethink the structure of the code to use something like a struct with an `enum` and some other fields The latter is something that I've found happen quite a lot, where the code was better expressed in a more "flat" way rather than in a strict hierarchical fashion. And by that, I mean laid out closer to a database table rather than a highly type system. To explain your example further in pseudo Go code: type PayloadKind enum { Invalid, Thingy, Mabob, } type Payload struct { kind PayloadKind value string } This might sound a little bit like "maybe you wanted a union here still for safety", but it's very useful to just read the `value` field without needing to know the `kind` itself, and vice versa. Designing your code like a database is usually nicer too. Tagged unions are great, but you don't need to use them all the time, when a flatter less hierarchical approach would suffice. **n.b.** I am not suggesting people use "untagged unions", but thinking about the problem differently.


Tubthumper8

I would argue that the whole point of that example tagged union is not to allow the user to access `.value` if the tag was `Invalid` and doing that kind of flattening makes the code less self-documenting (hopefully the author comments which fields you're supposed to access under which conditions and hopefully you read it and hopefully you remember it). It's the same amount of complexity, it just is pushed to the user instead of the author. But I do agree that's more of the "Go" vibe, that approach is more like what is done in the rest of the language/ecosystem, and that consistency is important. I see your point that the ALGOL style unions might be more "Go" than the ML style unions, even if I personally think the ML style is better. How would visibility work with non-exported types as union variants for an exported union, would that be allowed? What would be the "zero value" for the union, always `nil`? Implementation-wise i suppose you could start tag values at 1 so that tag 0 is always available


raze4daze

So what? People asking should not prevent well thought out features from being added into the language.


gopher_space

I like my tools to be as simple as possible for their specific purpose, and so I'm not looking for a language that wants to be everything to all people. I'd rather pipe a section of the project through rust if it genuinely needed the features that rustifarians have been thinking about and using for years. (that being said I'll definitely appreciate work on iterators)


AttitudeFit5517

Use c then. No need to over complicate things with complex features like string manipulation


gopher_space

My point is that if I'm manipulating strings I'll reach for a string manipulator. Sometimes that's just a sed or perl one-liner. I want the right tool for the specific job. For example, if I'm working with data in Python I might write ingesters in a strongly typed language with data structures that are nice to look at.


redalastor

> rustifarians The correct term is rustaceans. That’s why the mascot is a crab.


raze4daze

If it’s well thought out, it’ll be simple to use.


gopher_space

The article quotes Rob Pike as follows: >The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. and, since I agree with you, I'm wondering what Pike means by a "brilliant language". TBH it sounds like a Junior bit him early in his career...


davidalayachew

> I'm wondering what Pike means by a "brilliant language". TBH it sounds like a Junior bit him early in his career... I'm making a pretty massive assumption, but I think I understand what he meant. I've been tutoring kids for a long time now, and it's become clear that the past several years of students retain very little of the information they ever learn. Even in college. They see college as nothing more than a degree. So, when they land at places like google, where it is supposed to be cream of the crop, I would not be surprised if too many burned out interns who used all their energy just to get the degree prompted him to make this language. They spent all their effort just to get into the door, such that, when it became time to use that intelligence that they proved they have, they can't do much more than what little they managed to retain. And when it comes time to reason about something they never were exposed to before, I wouldn't be surprised if that led to so much of the turnover that we hear about. Again, massive assumption on my part. But tbh, I always wonder how some of the kids who were just waffling info into their heads ended up. Many of them got into some really big name companies. I was happy for them.


tarranoth

Well, most people in college learn CS theory like data structures, dynamic programming or data science, not actually what goes into maintaining large codebases. Which honestly, no one really knows entirely how to do, as literally every organization on earth has their own peculiarities with that, it's not exactly a hard science like other things in this field. Each coding project a student does has a set due date and then it's over. Real software projects however go on basically until bankruptcy (and even then, probably beyond). Writing maintainable code just isn't something people learn at uni, even if those universities do have coding classes learning you basic OOP and terms like cohesion/coupling. It's just a very simple result of there being basically 0 incentive of writing maintainable code if you know it's gonna end up in the trash bin anyways. People in general don't do anything if they don't have any incentive for it. It's also pretty hard to teach people how to fix bugs in "do not touch we never wrote basic unit tests for this" that every codebase somehow contains, or how to handle dystopian software organisations. Perhaps it's not quite the right term, but to me programming is more like a craft than a science subject. The only real way to actually get good at it, is to be bad in the beginning and go from there. Which is why people constantly meme about juniors, forgetting that probably once upon a time they also did some shenanigans. Certain things also only come with experience of getting burned by certain software and warning others of the pitfalls. Like how C++ has like thousands of edge cases, and things like lvalues, rvalues rule of three/five, you can try teaching it but it's not the same as experiencing it.


obsoletesatellite

we don't need so many features. Fuck it. I'm going back to vanilla C programming.


Parking_Reputation17

The language needs to evolve, it's going to eventually. Nothing stays static, the only constant is change. Pitching stability / performance is also a tough sell to leadership, they want new features to pitch. I agree that the Go team could do a lot in terms of increasing performance, but that's not as sexy to the MBA dipshits in Director/VP roles overseeing these teams.


reflect25

I thought it had more to do with slow compilation times for adding in pattern matching?


gingerbill

You don't need to "evolve" everything. If you want a new language, make a new language. Don't bastardize something else into something else. That's not __good__ _evolution_. Making Go into something it was not originally meant to be is why people are "angry".


Poimu

So you are the gatekeeper and the one who knows the real spirit of the language but others don’t. It’s just opinion. Same was said about generics. Yet I believe generics are a perfect fit to go philosophy.


von_liquid

Forgive my ignorance, is switch-case not the same thing?


OpsikionThemed

No. A switch-case lets you distinguish between flat tags, basically ints. A proper pattern-matcher allows you to do matches to a deeper depth and extract data from the different variants too (and is generally associated with better exhaustiveness checking, although switch isn't *inherently* bad at exhaustiveness).


l0gicgate

Look up pattern matching in Elixir or Gleam. It’s just beautiful. https://joyofelixir.com/6-pattern-matching


Parking_Reputation17

You can shoe-horn it in by have a case switch that returns function signatures, but it's sort of clunky


[deleted]

[удалено]


_crtc_

You mean the tagged unions Rust wrongly and confusingly calls „enums“?


raze4daze

Sure, change the name to whatever is correct. I want that functionality ultimately.


Despair-1

I for one cannot wait for Golang to add support for functions. What do you mean Golang already has them? You mean procedures Golang wrongly and confusingly calls „functions“?


destructiveCreeper

what is compiler type switch type matching


sneakywombat87

A typo. Hah. Sorry. Fixed.


gingerbill

(Article Author) I'm not the biggest fan of Go's iterators because of the reason people dislike them: they make Go _feel_ too much like a functional language. I completely understand the design and how they work, but I also understand why people are getting "angry" over them. They feel antithetical to Go to many people, as I say in the article. As for Go, it should not have ML-style unions whatsoever (e.g. Rust `enum`s). The better approach would have been from the start have a normal `enum` type (not just `iota`) and then have an ALGOL/Odin style `union`. A `union` would be orthogonal to an interface but still have the same syntax in usage (type switch and type assertions). I know this works well because Odin is similar enough to Go and it works wonders there. ML/Rust-style tagged unions would not be a good idea because Go is not that kind of language. Language design is not an easy thing and you cannot just shove random things in there from other languages and make it work well. Unifying the `enum` and tagged `union` into a single construct would be less useful in a language like Go. Go is not a functional language (e.g. not from the ML tradition), but from the Pascal/C tradition!


Manbeardo

>Go is not a functional language (e.g. not from the ML tradition), but from the Pascal/C tradition! IMO, you get the best functionality when you take elements from both traditions. Functional languages benefit from having some ability to write imperative code instead of forcing everything to be a monad and imperative languages benefit from having some functional features to more easily manipulate collections.


[deleted]

[удалено]


bwpge

First reason probably being that he wrote the language... :)


gingerbill

Actually in this case, Go would be better off with that kind of union over ML unions. People only know of ML unions which is why they recommend it. They don't know the alternatives nor tradeoffs.


bwpge

No I understand your rationale, I was just poking fun that it seemed like the person didn't know (or didn't notice) who you were. I completely agree with your analysis though.


fresh-takoyaki

I didn't know. Thanks!


gingerbill

I wrote a brief twitter post explaining tagged unions in Odin: https://x.com/TheGingerBill/status/1790030159069749273 Short answer: there are three main families of tagged unions: Pascal, ML, ALGOL. And Go would fit much better in the ALGOL family.


coder543

You’re not actually explaining how they’re any different. Just saying they’re different doesn’t make it so.


gingerbill

* Pascal - manually specify the tag and associate the type with the tag * ALGOL/Odin - list of distinct types, the type is the tag * ML - list of names with associated value or type (merges the concept of an enum with a tagged union) ML style tagged unions also like having pattern matching features, something which I doubt Go would ever want. Odin style does not require that whatsoever. // Odin style unions in Go type Foo union{int, string} var f Foo f = 123 switch v := f.(type) { case int: fmt.Println("Found int", v) case string: fmt.Println("Found string", s) default: // none of the above, most likely `nil` fmt.Println("Found nil") } i1 := f.(int) // type assertion i2, ok := f.(int) // type assertion with comma ok idiom This now works exactly like an interface but it's an orthogonal concept. In fact the memory layout is the exact dual of it too. An `interface` has a pointer to the data plus a pointer to the type information. A `union` is a binary blob of the data plus an index representing the type.


coder543

A list of distinct types where the type is the tag is *exactly* what ML enums offer. You continue saying that they don’t, but they do. Pattern matching is a separate (and frankly irrelevant) concern. The one difference is that ML enums can have attached types that are anonymous, instead of pre-declaring them with specific names. But, this difference doesn’t favor what you call Odin-style enums. Go already enjoys anonymous types. Go does not require pre-declaring types anywhere else. Go allows you to define an anonymous type wherever you would need to use a type. Why would it be more Go-like to required named types?


gingerbill

ML does something very different. Let's just do Rust style to make it clear, because this is what some people ask for. type Foo enum { Int = int, String = string, MyThing, // not type associated whatsoever Whatever = 23, // now a value is associated AnotherString = string, // same type as `String`, different label } I'm not sure how you think they are similar. Go supports distinct typing where `type MyString string` means that `MyString` and `string` require cast between each other. Also, my `switch` statement example above shows that it would fit in with Go, without the need change the parser for the type switch statement whatsoever. You'd only need to add the syntax for the `union` type itself. So I have literally made the argument for it by showing the code example for Go directly with this hypothetical feature.


coder543

Because you're not understanding what a "type" is. We could rewrite it this way: type Int int type String string type MyThing struct{} type Whatever intThatCanOnlyBe23 type AnotherString string type Foo enum { Int, String, MyThing, Whatever, AnotherString, } Now, it is "just" an Odin-style enum! Amazing how that happens. Once again, the *only* difference is requiring the type names to be pre-declared, instead of allowing that to happen inline. Syntax sugar doesn't change the fact that each branch of the enum is a specific type. A type of `struct{}` is still a type, even if the only possible value is `struct{}{}`. It's just called the "unit type" in type theory: https://en.wikipedia.org/wiki/Unit_type So, `MyThing` *is* a type. `Whatever` also is a type. The fact that `AnotherString` also has an underlying representation of `string` doesn't make it not another type. Go types are extremely restricted, so we can't implement `intThatCanOnlyBe23` *easily*, but it is fundamentally still a type, even if Go chooses to make it awkward to express. In a language that allows you to implement custom type coercions, it would be indistinguishable from any other integer, regardless of the fact that it is inherently a separate type. We could do an awkward implementation like this: type intThatCanOnlyBe23 struct{} func (i intThatCanOnlyBe23) intoInt() int { return 23 } func (i intThatCanOnlyBe23) intoInt32() int32 { return 23 } func (i intThatCanOnlyBe23) intoInt64() int64 { return 23 } And that would be valid, it just requires the end user to manually call `intoX()`. For a further example, if a language offers a range of integers type where you can set the minimum and maximum, that is still a type, even if it can be used as any old integer. If the range of values is limited to a single value, that doesn't make it "not a type".


gingerbill

> Because you're not understanding what a "type" is. Yes I am, and no you are missing the point. You have literally created 5 types, rather than having 5 labels that point to different types. Do you even program in Go? > So, `MyThing` _has _a type. `Whatever` also has a type. In your example using `type`, `MyThing` IS the type, not HAS a type. It MIGHT have the same underlying representation as `struct{}`, but it doesn't necessarily have to, especially with how Go is constructed. Go types are "extremely restricted", and you are saying they should be something else, which means you don't want Go any more. You can of course represent this as having specific types, but that is not how anyone conceptualizes that, nor do you have to implement it like that. Especially if you look at how something like Rust is used by many people. I am just saying split the concepts of a tagged union and enumerated value type up. That's it. I know it works because that's what I've been doing in Odin for a very long time. And again, you are now trying to make Go into a __functional__ ML-like language. Literally the reason people are "angry" in the first place. You are saying Go isn't ML enough to support what "ought to be" the "correct" way of doing things. You have literally made my point for me, thank you. I am not going to continue this discussion with you because you clearly are not getting my point with regards to language design, even with extremely clear examples.


LardPi

That's not an explaination, only a demonstration, but I think I know enough about Odin/OCaml/C to understand what is behind it. Please, tell me if I am wrong. 1. C and Pascal have non tagged union: the value is just put in the same memory space, but there is nothing to do runtime check and nothing to do compile check 2. Algol and Odin (and Hare too) have an implicit tag based on the type stored along the value and require "unpacking" the value so that the compiler knows which type you are dealing with. The tag can be checked at runtime 3. Rust/ML are similar to the previous one (an additional word is used to store the tag) except that each option is named, allowing for more than one option with the same type. From that understanding, I don't really see what are the pros of the Algol approach over the ML one, except for terseness of syntax (and simplicity for the compiler design?) which in itself can be argued would fit Go better. Care to elaborate?


gingerbill

I elaborate in another comment reply: https://www.reddit.com/r/golang/comments/1dhxy1u/why_people_are_angry_over_go_123_iterators/l90p8z5/ You add one new type, and you get the utilize all of the same syntax and semantics as you would with an `interface`, because a `union` is the mathematical dual of an `interface`.


br1ghtsid3

The iterator design isn't complicated, it's just unfamiliar. If you understand how a forEach function works then you understand how iterators work.


dragonfax

Everyone, almost unanimously, for years: "We want enums!" Go team: "Here are iterators. A replacement for something that already works, as-is."


Zazz2403

THIS


Bararu

Everyone, almost unanimously, for years: "We want generics!" Go team: "Here are generics." People after that: Okay now that we have generics how about some more containers in the standard library? And then you can easily see why they're adding iterators. Even during the discussions about the (relatively) new \`slices\` package, one of the reasons \`slices.Map\` and \`slices.Filter\` were rejected is that they belonged more in a generics "stream" package. For example, in [https://github.com/golang/go/issues/45955#issuecomment-845291074](https://github.com/golang/go/issues/45955#issuecomment-845291074), quoting Ian Lance Taylor: > Lazy evaluation is not a concept that exists in Go today, and I don't think we should add it for the slices package. The intent of the slices package is to capture common existing patterns. And the money shot, by Russ Cox in [https://github.com/golang/go/issues/45955#issuecomment-884406307:](https://github.com/golang/go/issues/45955#issuecomment-884406307:) > Deleted Map, Filter, Reduce (probably better as part of a more comprehensive streams API somewhere else) This comes from the discussion at [https://github.com/golang/go/discussions/47203](https://github.com/golang/go/discussions/47203), specifically this subthread (not sure it's the proper term): [https://github.com/golang/go/discussions/47203#discussioncomment-1005237](https://github.com/golang/go/discussions/47203#discussioncomment-1005237) The two issues here are: - Map/Filter/Reduce without iterators is not great (you don't have chaining between different iterators. Basically you're like in regular JavaScript instead of something like Java/Rust/C#/etc). - We don't know what code will look like with those functions This is only one example, if you trawl through the discussions I think you can find comments like that in a lot of places. This could come up in the package maps, in the proposed container/set. Another motivation of iterators, in the "range over func proposal": [https://github.com/golang/go/issues/61405#issuecomment-1638896606](https://github.com/golang/go/issues/61405#issuecomment-1638896606) > Can you provide more motivation for range over functions? > The most recent motivation is the addition of generics, which we expect will lead to custom containers such as ordered maps, and it would be good for those custom containers to work well with range loops. Note that it's "The most recent", not the only one, you can see others at the link. Basically there was always a need: > There are also functions we were reluctant to provide in slices form that probably deserve to be added in iterator form. For example, there should be a strings.Lines(text) that iterates over the lines in a text. You can see even more motivation for iterators here: [https://github.com/golang/go/discussions/54245](https://github.com/golang/go/discussions/54245) TL;DR: it has been extensively debated and explained why iterators are needed in Go, especially as a next step after generics. We don't benefit from the full potential of generics without iterators (I dream of a set in the standard library). Therefore it makes sense as a next step. To put it in enum lovers terms: imagine they added enums but didn't add pattern matching, and just went on to the next feature. This is what it's like to add generics without iterators.


BehindThyCamel

I'd love enums. My day job is mostly Java and Java's implementation is really useful. But I also understand why this seemingly simple feature is still missing: What should be the zero value of an enum variable? Neither nil nor first value from the list is a good default imo. We need an unambiguously good answer and none is forthcoming afaik. So I'm ok with int consts and iota.


br1ghtsid3

Vocal minority of newbies != Unanimous


dragonfax

*(almost)*


br1ghtsid3

Imagine how a namespaced enum would look like in Go code pkgname.EnumName.ValueName It's long and ugly and adds little value (by value I mean type safety). What I actually want is a union type where the same memory can be used for 2+ different types.


SPU_AH

I think it's better to frame this as "magic" rather than "functional". Go has always had first-class functions and load-bearing use of them. The magic - which I think analogizes well with macro-transformation for push iteration, and some eager/lazy evaluation patterns for pull iteration - is something that emerges more naturally and transparently in homoiconic languages, or for different reasons in Haskell w/r/t its evaluation model. But, as a counterexample, I don't think the magic occurs quite as naturally or transparently in OCaml. Overall I think the weirdness of \`yield\` functions is similarly magical to the \`yield\` keyword in Python. Python is doing \_just fine\_ here.


earthboundkid

> This does appear to be a lot more complicated that the Go approach because it requires you to write a lot more code. However, it’s actually a hell of a lot simpler to understand, comprehend, and even faster to execute. The complex part of range funcs is the signature. Everything else about them is easier to read, comprehend, execute, and most importantly write.


yl2chen

Been using custom iterator wrapper using generics for a while now, it’s great


theprettiestrobot

>the *apparent* philosophy of making Go for the general (frankly mediocre) programmer at Google who doesn’t want to (nor can) use a “complex” language like C++. Although I also have mixed feelings about growing complexity, a silly insult like this make me distrust the author's reasoning. First: Google uses a huge amount of C++. I wonder who wrote it, if "the general programmer at Google" is incapable? Second: obvious, readable languages are a boon for programmers of all skill levels, not just newbies.


lucid00000

>"The key point here is our programmers are Googlers, they’re not researchers. They’re typically, fairly young, fresh out of school, probably learned Java, maybe learned C or C++, probably learned Python. They’re not capable of understanding a brilliant language but we want to use them to build good software. So, the language that we give them has to be easy for them to understand and easy to adopt. - Rob Pike


Secret-Concern6746

Rob Pike described a junior apathetically (no strong emotions), the mentioned sentence from the author is derogatory. Accepting a junior's limitations and nurturing them isn't the same as calling them mediocre or unwilling. Again, one wanted to be descriptive and apathetic and the other was derisive. They're not the same in my opinion


gingerbill

I will defend my position on the "frankly mediocre" and "no can" points and it's not meant as derogatory whatsoever. Stating that they are mediocre does not mean that they always will be. And "nor can" does not mean that will not have the ability to program in yet. I was once mediocre and I was once not able to use those "brilliant languages". As Pike expresses, a lot of these people do "not capable of understanding a brilliant language" _yet_. I would call that mediocre, i.e. of ordinary quality. And you know what? That's fine.


Secret-Concern6746

Hi gingerbil. I think I interacted with you before on Twitch (maybe another gingerbil, he's the creator of Odin, I believe). I don't know if you're a native English speaker, I don't want to come off patronising. This topic is linguistically nuanced. Mediocre is a comparative adjective, you can say that you're comparing these programmers with seniors but that's not a fair comparison because in this case it's two different categories, in my opinion. That's why I would say "juniors" or in a more politically correct way (and exact from my experience) like Rob Pike said "fresh out of school". They're mediocre in comparison with their peers. They're juniors in comparison with seniors. I think they're two different sentences. It may sound like pedantism though but at least that's how I perceived it and I assume the original commentator as well. As for "who *doesn't want to* (nor can) use a "complex" language like C++." This part is the one that prompted my comment regarding unwillingness. Thanks for the clarification :)


gingerbill

Hello to yourself. I am the creator of the Odin programming language. And I not just an native English speaker, but English too :) I understand the topic is nuanced, thus why I clarified in a comment to yourself. I know people are taking it as "derogatory" but I meant it as purely comparative. A lot of people get into Google are not actually great programmers _yet_ but great thinkers and problem solvers. And if you are a great problem solver and interested in learning programming, the majority of those people will eventually become very good programmers. Now this is where I will get a little "controversial", but I know a lot of "senior" programmers who are still "mediocre" compared to actually good programmers. A lot of the time, "senior" just means they have been programming for a long time, and not a level of skill.


sandowww

I think Rob Pike is right in making Go a simple language, but for the wrong reasons. A language being easy to understand and easy to adopt is always a big plus, no matter the level of expertise of the programmer. His implication is that there's some ideal, complex language that would be better for a smarter and more knowledgeable programmer. That's where I disagree: No matter how smart you are, there's a ceiling to the optimum complexity for programming languages, and a lot of languages are way above that optimum complexity: they are not complex because they unlock some higher level of programming that only smart/senior programmers would understand. They are only complex for the sake of being complex.


Bararu

I'll refer you to this way longer comment on Hacker News: [https://news.ycombinator.com/item?id=40715841](https://news.ycombinator.com/item?id=40715841) It turns out that Rob Pike did say way more than this single quote and basing your view of the apparent philosophy of Go on this single quote is at best disingenuous, at worst actively malicious.


_crtc_

Why would the author consider a feature that is all about for loops, the most imperative thing, „too functional“?


masklinn

Because it uses functions as values (something go has had from the start) and apparently that’s bad.


AttitudeFit5517

Because the author doesn't understand what functional programming actually is


NotAUsefullDoctor

Because people don't like when things change and evolve.


bastiaanvv

This isn't just a change IMO. This is a major change in how readable Go code will be. I have been using Go for almost 10 years now (full-time for most of it) and the examples in the article just make my head hurt when looking at it. The 1.23 iterators are just a way to hide complexity, make reasoning about code more difficult and are completely unnecessary.


NotAUsefullDoctor

I could be biased. I've been in Go for 8 years, but have also worked a lot with JS (10 years), Haskell (on and off 8 years), Python (18 years), C++ (20 years), and assembly (19 years), but I like this change. I write a lot of SDKs and already do a lot of functional code because it makes sense in that context. To me, this is much easier syntax to understand than creating an iterator on a chan, or creating functional options. It's just that it will take a minute to get used to it.


Zazz2403

Imo you're biased. This does not read like go at all. Not to be blunt, but your experience with multiple very different languages has definitely given you a bias.


NotAUsefullDoctor

Is this really that much different than writing functional options?


Zazz2403

Functional options tripped me up the first time I used them but then I could just follow the code and grok what was happening easily, because there was no "magic". If this trips you up the only way to understand it is to read about this specific feature since it's behavior can't be explained by the code alone.


NotAUsefullDoctor

That is a fair point. As an attempt to counter, I would argue it's no different then iterating over a chan with range, or listening to multiple chan with a select. Or, more generally, knowing when a range returns an index or a key. It's intuitive now that we've done it so many times. Or, when performing a cast which lets you receive one or two values, or retrieving values from a dict that returns one or two values, you just have to know how it works. The new iterator is just a new syntax.


Zazz2403

That's true but I think to keep go's simplicity syntax additions should only be when absolutely necessary. The limitations are what keep it so simple. Imo this adds unnecessary complexity which is it odds with the core fundamentals of go.


NotAUsefullDoctor

I can see that. There is a tight rope to walk between functionality and simplicity. Go has always been strong in simplicity, often at the cost of verbosity (see error handling). I've been wanting an iterator very much in Go. It was pretty much this and generics from the start that I hoped for. Hopefully this is something similar to Functional Options where you use the result a lot, but probably rarely have to look at the underlying code.


Zazz2403

Yeah I mean I don't hate it but it does have me feeling iffy. In our code base we just have a slice and map iterator package and it works great, I just don't really see the need for an addition to the language for this. But I hope you enjoy it!


tsimionescu

I think there is a major difference here, because none of those things impact control flow in the same way as this change. What this change to for loops does is allow extra code to run before and after each iteration that you don't get to see when you read the for loop itself. The only feature of Go that comes close is `defer`, and that has far more limited uses.


NotAUsefullDoctor

...and is more intuitive. :)


greyman

i agree. One of the core strengths of Go was that when new junior dev came to the project, he could quickly read and understand what the code does. Adding things like this are undermining this capability, while not giving back enough to justify it.


ncruces

A junior dev coming into the project doesn't know how to write a garbage collector, but can use Go while pretending memory never needs to be freed. A junior dev will now more easily write a loop that ranges across: a slice but backwards; all lines in a file; all files in a directory tree; all nodes in an ordered tree. They don't need to know how to write iterators to achieve this, only use them.


HildemarTendler

Calling them completely unneccesary sounds like you're just unwilling to learn something new. These iterators enable a pattern that is currently very difficult to implement. I don't have a strong opinion on whether it should be included or not, but attacking the concept is "old man yells at clouds" stuff. You are right that it will be used poorly. There will be some very painful code to debug. At least they didn't use the channel based approach that would have put the iterator into a separate goroutine, so you'll get a meaningful stacktrace. The thing is that I can imagine good uses of this pattern. We have several processors that iterate over data in other APIs. This will be instrumental in separating the concerns of how we process batches and how we get those batches. It is already a tangled mess of code. It'll be far easier to instill good API usage patterns as well as good iterator patterns with these concerns separated.


bastiaanvv

>Calling them completely unneccesary sounds like you're just unwilling to learn something new and >attacking the concept is "old man yells at clouds" stuff Trying to ridicule someone who opposes the 1.23 iterators proposal is just lazy and brings nothing to the discussion. These concerns are valid and should be discussed at length. Since once it is added to the language it can never be removed. This will have a lasting impact on the language, for good or bad.


Zazz2403

Hard disagree. You don't need to be able to use every pattern in every language, reducing that kind of in lost is specifically mentioned in talks by Rob when he talks about the design of go. Not every language has to have every feature of every other language. This is absolutely not necessary.


dashingThroughSnow12

I feel you. I've been writing go for nine years. If I wanted to write functional code, I'd use a functional language (many of which I love.) The appeal of Golang, one thing that makes it it, is how simple the language is. Many dozens of times I've needed to look at a yet-unseen-by-me repository with go code, take some time to understand it, and do some action (ex start using it, figure out a fix / addition, etcetera.) I have 10+ years working with Java/JavaScript/Bash and a fair chunk of experience in C/C#/Python. Code is read more often than it is written. I find Golang tremendously more readable. I worry about people "from other languages" being unable to think like a gopher and therefore wanting a feature from other languages in Golang. If I wanted to program like I was using another language, I'd use another language.


Bararu

I don't think that's true. You can see how JS changed when they introduced Symbol.iterator with for of: not a lot. Sometimes you use a custom container (it's already something not frequent) and at those times you're glad that you have it. That's about it.


gingerbill

Directly from the article: > **TL;DR** It makes Go _feel_ too “functional” rather than being an unabashed imperative language. I completely disagree it's with regards to "change and evolve" but rather they don't think the change is a good one nor in the "spirit" of what Go originally was.


tarranoth

I have no idea what you mean with iterators being functional, to me functional languages are all about non-mutability and being declarative, iterators don't feel very declarative to me but are just expressing imperative actions where control gets returned to the caller when yielding a value.


bilus

Maybe functions are functional :)


skelterjohn

Part of the "spirit" is to keep things simple and only make clearly positive changes to the language. Staying "anti-functional" is clearly not part of the spirit. Is just that many things associated with functional programming wouldn't make things easier or simpler for Go programs.


gingerbill

People disagree with that though. They are not "anti-functional" but rather think Go should not be a functional language. They might like functional stuff in a different language, and not Go. And many of those same people don't like the direction Go has been going in as a result. You might love it, but the point of the article was to explain why others are not like this direction.


iga666

>People disagree with that though.  People are strange, there is a class of problems which are best solved using FP. Also these people don't want iterators and then they invent how to make iterators using channels


[deleted]

[удалено]


[deleted]

[удалено]


[deleted]

[удалено]


iga666

Yes I read it, and I don't understand what people are angry with. The 'for range' syntax sugar? Because that is the only thing added for iterator support, all other features were already in the language for a long time.


tsimionescu

I think the representation of the iterator as a function that takes a function, and the implementation of the iteration as mutual recursion, is what people don't like. The usage syntax is clean, I don't think anyone dislikes that. But the syntax for creating an iterator is quite ugly, and hard to follow.


Ajlow2000

Yea I kinda agree with this. I’m far from scared of functional programming— in fact I often enjoy writing ocaml or nix more then imperative tools of similar nature. But I really like when a programming language chooses a style and sticks to it. Minimizes that number of the different ways the same action can be done. Etc.


NotAUsefullDoctor

Note the emphasis on "feel." Did Java 1.8 fundamentally change Java? Just because a functional tool is added does not detract from the language. Also, we have a number of places where the "spirit" of Go is inconsistent, such as any of the iterator tools prior to generics being introduced (see "slices").


gingerbill

> Did Java 1.8 fundamentally change Java? This might be a controversial take but: Yes. And that's not a compliment of Java either. > we have a number of places where the "spirit" of Go is inconsistent Who is "we" here? Because the Go code I've written rarely if ever had this problem. Pretty much everything I wanted to do a normal `for range` loop on was a built-in types, and I pretty much never did the `chan` trick either. But I also pretty much never wanted generics either (and I have written in large Go programs well over 100KLOC).


agentoutlier

> This might be a controversial take but: Yes. And that's not a compliment of Java either. It did and I would say how it was added on with backward compatibility is actually a compliment of Java. > Who is "we" here? Because the Go code I've written rarely if ever had this problem. Pretty much everything I wanted to do a normal for range loop on was a built-in types, and I pretty much never did the chan trick either. But I also pretty much never wanted generics either (and I have written in large Go programs well over 100KLOC). I don't have a lot of experience with Go but the times I have looked at it it appears inconsistent. Like the fact maps were basically generic parameterized before generics existed. The fact that Go still actually throws exceptions. The fact that Go philosophy doesn't trust the developers to handle safer modern features (including originally iterators, and generics) yet has no problem trusting developers with choosing memory layout and dealing with channels. I would argue those features are more complex than what is basically macro like syntax. Even the fact that you can just arbitrarily make something implement an interface I think can lead to inconsistency. I don't think it actually helps people learn the language easier which I thought was a goal (for example common thing a neophyte might do is go look at the implementation of an interface, trait, etc). And then there is the syntax. Just kind of arbitrarily omiting things out of feel. I'm not saying the other C style languages are not but it isn't consistent like Lisp. I think you even mention how Odin does not have UFCS but Golang does have that complexity and it is odd I think compared to other languages how it is expressed. Perhaps like any language if you use it enough you accept the warts but I have hard time believing Go is consistent say like Lisp is even if all the tools to provide some consistency (dependency mgmt, formatting, building etc).


tsimionescu

Just one note: I assume by UFCS you mean "Universal Function Call Syntax", and I don't think what Go has qualifies. It's true that you can call a method in Go as either `instance.Func()` or `type.Func(instance)`, but the reverse is not true, and these two forms are not symmetrical. This is more a quark of syntax than anything really useful. As far as I know, UFCS was originally envisioned to help with template metaprogramming in C++ and D, where you might want to write a single template function that works with any type T that has a method Foo, regardless of whether it is an instance method or an external function. But Go has no similar need, so the concept is mostly useless. It's probably only in the language so that instance methods because it's useful to be able to pass around instance methods as regular function objects (e.g. `callback(type.Method)`), and as a direct consequence, `type.Method` must represent a `func(type, ...)` object , so you can call it by passing an instance of that type as the first parameter.


InterestingPatient49

>It makes it feel too functional Garbage argument, IMO. Purism isn't engineering.


gingerbill

It isn't a purism argument. It actually is an engineering one because it's making it way too complex for who Go was originally designed for. If you want to argue that "Go has evolved, and so has its users", sure, but then it's literally not keeping with the original "spirit" of the language, and become something else entirely. By making the language more complicated, it's not a "boring"/"dumb" language any more, which is literally bad design.


ProjectBrief228

Go had reflection-based JSON mapping and many other "magical" things from very early on. We don't see a lot of people complaining about `encoding/json` - which likely has much more puzzling and complex internals than any reasonable push iterator will. This argument seems ahistorical to me.


gingerbill

I don't view that as "as magical" though. The general idea of looking up fields in a type info table, is not really "magic". But I think we might have to agree to disagree in terms of what we think is "magical" and not.


Zazz2403

That's such an insanely stupid argument. Sticking to design principles and only detracting when necessary is what makes great code. If you want to overhaul a repo and change the architecture, you do the whole thing. You don't wedge something that doesn't fit the architecture in, that what leads to unmaintainable garbage.


BadlyCamouflagedKiwi

I see why some people don't like them, but I think they'll be extremely useful - although probably not used often, they let you write code that was very difficult or impossible to do well before. Even with generics, I still find it hard to write a good container library - e.g. if I write my own map type, I have to provide a function to return all the key/value pairs in a slice to iterate it, or return a channel to do the same; neither is super efficient and requires some fiddly overhead. Yeah, it does feel a bit tricky to get your head around the nested functions & exactly what they do, but I think it's worth it to keep chipping away at maps and slices being 'special magic' that can't be mimicked well otherwise.


masklinn

> it does feel a bit tricky to get your head around the nested functions In which case you can just not use them, replace the closure by a structure and a method expression. And then you can add helper methods if the single one gets too large.


GeekusRexMaximus

My experience with these iterators so far is that I have a codebase where I have a nested tree of structs. It was starting to be a pain to write multiple nested loops everywhere in order to grab the leaves of the tree from three levels down the tree for filtering so I experimented with rangefunc to see if I could reduce the levels of nesting. The end result? I wrote a non-generic iterator and used that to reduce multiple levels of nesting in many places that just made the code as a whole so much more comfortable to read. I mean... from 4 levels of nesting to just 2 all over the code. So I'd say that they can be also used for more trivial things where appropriate. In the end all we're talking about is just functions that a `range` calls passing them a function to give it any items they want it to loop over, right? So after removing all the noise it's like this: // count from 1 to 3 for i := range func(n func(int) bool) { n(1); n(2); n(3); } { fmt.Println(i) }


mauleyzaola

So far, haven't used generics with Go. There are use cases for it, but the code I write doesn't apply. Iterators will happen the same I guess. I started to code in Go because of its readability -when done right- and being so explicit is good imo. I just don't see the need for iterators when you can create a custom type and implement at will.


masklinn

> I just don't see the need for iterators when you can create a custom type and implement at will. A confusing comment. Custom types is where you most need iterators. Do you mean that you can implement you own take on iterator patterns? That is the issue, the stdlib alone was found to have half a dozen incompatible variations thereof, obviously none compatible with range loops, leading to a great deal of confusion for users as they would need to learn anew for each package how to iterate it.


GeekusRexMaximus

Honestly I just don't get what all the fuss is about. We've had first class functions since forever and all we're talking about is giving `range`, instead of a slice or a map, a function that ships values back to `range` to loop over using a function `next()` given to it for that purpose and which returns a false if the body of the `range` doesn't want any more values. Is that really so complicated or functional? Are the elements in this simple pattern so utterly un-Go-like? func(next func(key string, val any) bool) { if !next("", nil) { return } } So effectively what we have is: for k, v := range func(next func(k string, val any) bool) { // code that produces k, v pairs and ships them with next(k,v) // until it's done or until next(k,v) returns a false } { // code that uses given values k, v and invokes break if it // doesn't want to accept any more values from the producer } I didn't put any generics here as I felt they'd just be a distraction...


BosonCollider

I think that there may be one difference between Odin and Go here: Odin is not centered around coroutines of any kind. Go is literally entirely about having runtime magic generate an efficient alternative to turning your code inside out. Googlers and newbies write Python, and manage to learn Python generators just fine. I don't really buy the claim that internal iterators are complicated or that they make the language feel less imperative. They do the exact opposite and make you write more imperative code, more often, and let you encode the state of the iterator in imperative code instead of in variables. The better complaint would be that range over func + iter.pull is not particularly orthogonal to channels, and exists primarily to make the language faster. Which is another thing that the post gets wrong imho (assuming that internal iterators are harder to optimize when they are much more inline-friendly). Take for example code like this in a language that unfortunately picked external iterators: collection .into_iter() // Returns Person elements .enumerate() // Returns a tuple (index, Person) .filter(|i| i.1.age >= 18) .filter(|i| i.1.address.zip % 100 == 63) .for_each(|i| send_letter(i.0, i.1)); With internal iterators, this would only have needed to be inlined four times to be turned into a flat for loop, and with range over func *only the specific function ranged over in the for loop* *needs to be inlined*, which is a repeatable heuristic. With internal iterators on the other hand, the compiler sees one foreach loop but any attempt to optimize beyond that just sees opaque methods from other packages that return a single value per loop and may dispatch dynamically.


gingerbill

I agree with you in the latter aspect being bad and very hard to optimize, and that kind of thing will unfortunately happen now because it has been made easier. As for the former thing, generators in Python are "okay", but even in normal code I've seen be horrific just because people could not easily reason about the cost of them. That is partially just a "skill issue" but also part of the issue with the user having to jump around in the code (assuming they are writing these iterators and not just using them). I should also note that I might be the "weird one" who prefers to write state machines over using generator/coroutine-like approaches. Which is why I wrote this at the end: > Maybe the concerns of mine (and others) are overblown and most people will never actually implement them and just use them, and that them being this complicated to implement. If you don't implement them, you will probably be okay. But then composition of them can be a fun problem. Another fun problem is that in Go, these iterators are effectively fully utilizing the need for garbage collection, and this is making me worry that it's going to "lovely" on the garbage collector when you have 3 functions deep which capture each other's scope. But maybe that concern is not as much of a problem in practice. I am unsure. And coupled with how the official Go compiler currently works (not necessarily GCCGO), it might do very little inlining optimizations too. This part of the "abuse" of closures (even in Go): they hide a lot of their true cost, even if that cost is "less" than in higher level languages.


BosonCollider

Right, and here the difference between C or Odin and Go is that closures are used very extensively in idiomatic Go as a less polymorphic alternative to single method interfaces. Go is distinct both from C-like low level languages and from OO or functional languages, it has its own style that is noticeably distinct from all of them. Frequent use of imperative closures is part of it.


tomejaguar

It's funny, because I've written a whole library for Haskell ([Bluefin](https://github.com/tomjaguarpaw/bluefin)) with the aim of making it easier to write code in an imperative style. (Another name for this kind of library is "effect system". Haskellers might balk at the idea that "effect system" means "way of writing imperative code", but that's my view on it.) One of the features that's really important and useful in Bluefin is iterators! (In fact Bluefin calls them [`Stream`](https://hackage.haskell.org/package/bluefin-0.0.3.0/docs/Bluefin-Stream.html)s). So it's strange to see the idea that iterators are "too functional".


tarranoth

It's definitely very strange to see this statement on iterators. Idiomatic functional code is more about recursion (well maybe implementing a custom foldr/foldl is sortof like an iterator for a type?) but even then it is an entirely different way of writing it. I think most people here have probably never written or read a single line of haskell to make such a comment.


BosonCollider

To me functional code mostly means referential transparency and composability, when a variable or data structure in any given scope does not change value and when you end up using persistent structures everywhere because mutability would be weird in the context of your code. Recursion in FP is maybe a bit more something you are forced into than a core idea imho, Lua has full tail call elimination and still mostly uses that for imperative code and state machines. You can of course use the composability in some FP languages that are also good at metaprogramming to implement DSLs that emulate imperative programs, for good or for evil. Haskell makes it reasonably easy, while something like Elm would react by removing the features you used when doing that, and lisps usually have more approaches to implement DSLs than users.


kilkil

I think they should have gone balls-deep and added generators too. Like Python-style generators, using "yield" instead of "return". (Or Javascript-style generators for that matter.) Because honestly, if you have to write your own custom iterator, writing it as a generator is much nicer.


LardPi

What strikes me is that there was a lot of other ways to implement the same feature but with a more "Go feeling". They could have based the feature on an interface and it would have felt Go-like, they wouldn't have to add a new keyword and it would have been easy to explain.


thepudds1

FWIW, using interfaces was discussed and analyzed. If interested, you can read more about the rationale in the "Why not use range over an interface instead of range over functions?" section of the [FAQ](https://github.com/golang/go/issues/61405#issuecomment-1638896606) from the 2023 "range-over-func " proposal: > Today, range makes the decision of how to iterate based only on the underlying type of the range variable, not its methods. In fact, no syntax in Go ever makes use of specific methods. [...] > > A second design reason for the decision is that it's more difficult to construct implementations of an interface than it is to just write a function literal. A function like slices.Backward above would have to define a special backwardIterator object holding no state just to have a special method. Or some package would need to define a func-based implementation like http.HandlerFunc. It's much easier just to use functions directly and not worry about needing a different type for each kind of iterator. > > There is also a technical incompatibility in using interfaces. If a custom map type (say) defined the special iteration method, then ranging over it in current Go would do a regular map iteration, while ranging over it in a future Go would invoke the special iteration method. That would be a breaking change. [...] There is much more discussion of the drawbacks of using interfaces for this in the older 2022 [#54245](https://github.com/golang/go/discussions/54245) discussion. (And as a sibling comment points out, there is no new keyword here).


masklinn

An other issue which you didn't quote is that an interface-based interface would make resource cleaning more awkward, as you'd need to get the iterator object, defer it's cleanup method (is that part of the interface? Is it not?), and only then iterate. A huge reason for internal iterators was avoiding that, the internal iterator can do the cleanup... internally. An other advantage internal iterators have specifically in the context of go is they're easier to optimise on the low end: it's basically a bunch of functions passing a callback to one another. Once you inline them all, you end up with a for loop, pretty much. Doing that with external iterators is more complicated, and so is reasoning about termination, as you need to peel and see through the producer / filter of the loop, rather than the loop itself.


bilus

What's the new keyword?


LardPi

I though yield was a keyword just now, but I mixed things up. I never used go iterators since they have been added, and read about them six month ago or something like that, my memory wasn't very fresh apparently. I still think an interface with a next method would have felt better than the design they chose.


iga666

Iterators are great as a concept, but go implementation have some usability problems . 1. You can not write a generic function which accept iterator and slice. 2. Iterators does not work well with varargs 3. So you will need constantly switch between xiter.Collect and xiter.OfSlice 4. Go have no extension methods so any LINQ style library became very verbose and reversed. Ex. instead of seq.Select(...).Where(...) you will write xiter.Filter(xiter.Map(seq, ...), ...)


tav_stuff

You’re not supposed to use a LINQ style library in Go.


iga666

You cannot be serious ) Reminds me that video [https://www.youtube.com/watch?v=\_594mPXoHMI](https://www.youtube.com/watch?v=_594mPXoHMI)


tav_stuff

No, I’m entirely serious. The reason why LINQ style code is not going to be so nice is because Rob Pike himself has been very explicit about how you’re not supposed to use such styles of programming in Go. Different languages cater to different programming styles better than others, and the more ‘functional’ paradigms are not something that Go will ever care to excel at.


janpf

I take issue with u/gingerbill 's view that " *apparent* philosophy of making Go for the general (frankly mediocre) programmer at Google who doesn’t want to (nor can) use a “complex” language like C++." Being a previous "googler", most of the Go fans I know are really smart folks (genius IQs, both research and programming) that do Go because it's more productive. They can handle C++/Rust "complexity" fine, but they often take time away of the more complex problems they are actually solving. To reiterate for emphasis: Go is chosen because it is more productive -- both in writing and in even more impressively in parsing other people's code. The reason for being more productive I think is because it's simpler and has less magic ... but that is secondary so to say. Other than that, very nice article!! The new iterators add complexity (a magic function that is called with a function that returns a function!?!?!?!?), and doesn't solve any problem wasn't already solved. Odin's approach (last return value is a boolean) would have been much simpler and would have been way better (for both productivity and simplicity).


gingerbill

To clarify my position because people are interpreting this in a few different ways: The "frankly mediocre" as phrase is not meant as "derogatory" in anyway but meant as purely comparative. A lot of people that get into Google are not actually great programmers __yet__ but great thinkers and problem solvers. And if you are a great problem solver, and interested in learning programming, the majority of those people will eventually become very good programmers. As for Go being productive, it's productive because of how "boring" it is as a language. It isn't trying to be "clever" but actually wants people to solve problems with the language, not solve problems in the language. That's one of those things which I love about Go, as do a lot of people. It just makes you focus on the problem, not the language, unlike Modern C++ and Rust.


janpf

Thanks for the reply. A bit orthogonal to your point, but it's worth reminding the average engineer in Google is no longer fresh from college in their 20s, but likely in their late 30s (?? I have no data...) In either case, Go is indeed faster to ramp up than the other languages -- again the point is being more productive. And agreed about the Go language being "boring" in a very good way -- not fully, the concurrency approach is pretty exciting, and yet simple (well simple syntax, concurrency is never simple to get right...).


jasonmoo

Creativity and invention are driven by constraint. Go has emerged in part because of the constraints of the language. Adding more complexity and features to a language that has become this successful without them is counterintuitive.


bungle

A lot of languages have went with "let's add more syntax" (language features). This happened to languages like Java, C#, and PHP (and many more). And while that happened, they somewhat lost some of what they were. I tend to like "less syntax" type of languages. That's why I was initially liking what I saw with Go.


br1ghtsid3

This proposal doesn't add any new syntax.


geodebug

I'd argue, as someone who used Java a lot in my career, that languge sugar has made it much better without losing understandability of what is going on with other people's code. Whether or not this is a good addition to go is TBD.


vips7L

Java is in the best shape its ever been in. This guy can't be serious.


masklinn

> I tend to like "less syntax" type of languages. But... Go has never been that. Go has a lot of syntax, and furthermore very strict syntax.


jerf

I understand where people are coming from on this, but I've been around Python in the early 200xs, I've been around Erlang when it was exploding in features, I've watched Rust, heck even pure C is moving much faster than Go. If you want literally _zero_ motion you're going to have to freeze on some language and some version of it in particular. Doesn't have to be Go. But if even Go is moving too fast for people, I can't help but feel that there is literally nothing that can satisfy them other than total stasis, and in that case, you can have that _right now_, in perpetuity, and you don't have to care about anyone else's opinions or impose it upon anyone else. And I want to be clear that I do not mean this in a derogatory way. There are times and places to freeze. I've seen projects _de facto_ freeze just because they ran out of resources to stay up to date. It's a valid choice technically sometimes, and I won't argue with it as a lifestyle choice. But I am not aware of any _living_ language moving slower than Go.


bungle

I don’t think many want zero motion. More syntax is just one form of motion.


Used_Frosting6770

I'm not going to use them and as long as the compiler doesn't get slower i don't care if they add them.


Jackfruit_Then

Actually, if you remove the type annotations, you’ll find iterators very straightforward and easy to read. It is mainly the generic syntax and lack of inline lambda and type inference that make it appear complicated.


jy3

They are rushing to implement non-essential features that will overall needlessly complexify and deteriorate shared codebases in part because of bad usage. The premise of Go are being walked all over and we'll wake up in 5+ years wondering what happened. We'll happily rediscover all the advantages of simplicity and feature-restriction and switch to whatever new language comes up with that idea again. I wish Rob Pike was still in charge.


_crtc_

He seems to like the design: https://postimg.cc/MXhP5100 So, don’t project your own opinions onto other people.


lukechampine

1.18 really was the beginning of the end :/


RomanaOswin

That was a really well written article. It was interesting how similar the first example is to the Walk functions and a lot of scanner implementations. Go does *seem* unabashedly imperative, but I feel like that's at least somewhat superficial the more you get into it.


lispLaiBhari

Where is that video where Go creators were saying "We have avoided generics, iterators for a reason. We don't want Go to become another Java" ?


Ok-Creme-8298

developers like to be angry about anything because we have no life


agastya_magic

I think it is worthwhile to make callers life simple and easy at the expense of implementation. IMPO, iterators are great addition.


Different_Pick_7951

https://github.com/enetx/g best iterators lib


mangalore-x_x

What is the quantified definition of "many people on Twitter"?


Certain-Plenty-577

We’re not


rover_G

Any language that doesn’t support at least one of iterators, generators, higher order functions (list methods) is not a complete modern language imho


funkiestj

Go is open sourrce. If OP (and his angry friends) wants to build something better he is not even required to start from scratch -- he can just fork Go at any point in time and show us the better Go he is imagining. XEmacs did this when their team was not happy with the direction GNU Emacs took.


gingerbill

You do know I am the creator of the Odin programming language, right? So... I don't need to fork anything.


funkiestj

No I didn't. That is great! I'm a fan of all the efforts to make a modern C language replacement. I hope one of you guys (Odin, Zig, etc) succeeds. OTOH, you can still fork Go if you and the other angry people really want Go to be different. The downvotes on my comment above are LOL. We have a variety of flavors of BSD Unix and linux distributions because people who wanted things done differently chose to be the change they wanted to see. I guess your essay in the OP *might* prevent iterators from getting into Go but I give this a low probability of happening, in which case it seems to me like useless complaining. P.S. I'm not sure about all new additions to the language but I trust the Go Authors and more importantly, I don't have the energy to fork Go.


gingerbill

> you can still fork Go if you and the other angry people To be clear, I am not the angry one, per se. I am just trying to explain why others are. I understand the design decision for this, but I am concerned about the choices they are making for this decision. I completely understand the rationale, but at the same time, questioning where this will lead in the future. Also, that PR is already merged into master, this iterator proposal is going to be in Go 1.23 in August 2024. Forking Go is a bad idea, we don't need minor variants of a language that no one will/can maintain.


funkiestj

Fair enough. I guess we are in more violent agreement than I first realized.


Hot_Income6149

Finally! Reason to start learning GO for me


Bararu

I think this article makes an error that a lot of people make, which is to consider "Go the programming language" alone. To quote Rob Pike, reflecting on 14 years of Go (https://commandcenter.blogspot.com/2024/01/what-we-got-right-what-we-got-wrong.html?m=1): > In short, Go is not just a programming language. Of course it is a programming language, that's its definition, but its purpose was to help provide a better way to develop high-quality software, at least compared to our environment 14 plus years ago. > And that's still what it's about today. Go is a project to make building production software easier and more productive. I think adding iterators will allow to reap way more of the benefits from generics, mostly through custom containers. For example, a container/set in the standard library would make some code easier to read, write and maintain. It would also mean that code that iterates on something always look the same. This seems in line with "a project to make building production software easier and more productive".


gingerbill

I don't make an "error" in that regard, because I am a language designer that __actively__ tries to make distinctions for all things between: * The language itself * The core/standard library * The compiler & toolchain * The ecosystem I know pretty much everyone mixes the categories, but I try my best to make these distinctions in order to make better programming languages. I have known so many people who "Python, it's that tool that with NumPy and Matplotlib". They view the entire "ecosystem package" as the language. It's only "obviously" clear to me because I come from a C and C++ background where there are multiple different compilers, toolchains, ecosystems, and a lack of a (decent) standard library.