5 ms·
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 w
by imiric 3y ago
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.
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.
I hope that this proposal is rejected, and that it's not a sign of Go becoming yet another kitchen sink language.
- emmelaich 3y agoI'm a go newb, but would you do this with channels and gofuncs now? The readability this provides is considerable.
- d1str0 3y agoThats how I would do it. You can have a goroutine send on a channel (buffered or non buffered), and then another function can consume the chan as needed which lets the goroutine generate results as needed as well.
- slashdev 3y agoAnd make a simple iterator into a multithreaded program with channels and goroutines? Slower. More complex. Why?!? Just call your function in a loop.
- Groxx 3y agoWhich is to say: yes! Many do it with channels currently. It's awful, but they do it.
- konart 3y ago> Slower Why would that be slower?
- Mawr 3y agoThe design of channels is constrained by the need to support concurrency. If you do not need concurrency, using something designed to support it will be suboptimal. And because it's specifically concurrency, the overhead will be substantial. ... that's the theory behind it. For more grounded explanations, see [1] & [2]. [1] https://news.ycombinator.com/item?id=29510751 https://news.ycombinator.com/item?id=29510751 [2] https://ewencp.org/blog/golang-iterators/index.html https://ewencp.org/blog/golang-iterators/index.html
- konart 3y agoYeah, I kind forgot we don't have anything concurrency related here. Thanks for the links!
- slashdev 3y agoChannels and the goroutine scheduler need to be thread safe. They use locks and/or synchronized instructions that run much slower because they need to synchronize between the different cores of your CPU (or between CPUs, if you have more than one, which is slower still.) Running goroutines also saves and restores registers, trash part of the cache, or move to another core with a cold cache. Threads have more overhead, and processes more still, but that still is very slow.
- jasonhansel 3y agoAs I recall, the performance of that approach is quite bad, even with Go's concurrency support.
- liquidgecka 3y agoThat kind of suffers if you want to be able to cheaply cancel the iteration. Lets say the logic is: for item := range list { if condition(item) { break } } That is nice and readable, but if the thing your iterating is returning items in a batch then this gets far, far uglier. Its possible to end up with one loop that fetches x items at a time, and another that iterates over those items.. etc. But if you switch that to channels you maintain the readability and everything is nice! .. Except, you need a way to cancel the iteration, and it can't be closing the channel or the writer will panic. items, cancelFunc := apiCall() for item := range items { if condition(items) { cancelFunc() for _ := range items { } break } } close(items) Now, this is even worse if you need some kind of finalization of the goroutine that spawns to write to the given channel. Now you have a wait group, or a second channel.. etc. If its possible to error in the middle of iteration then things get complicated as well.
- jppittma 3y agoI may not understand what you're looking for, but what's wrong with this: topLoop: for _, batch := range list { for _, item := range batch { if condition(item) { break topLoop } } } The language also supports a limited goto. Combined with select and context, I've never seen a situation I felt I couldn't adequately describe.
- Kamq 3y agoI would probably just do the following: type Iterator[T any] interface { HasNext() bool GetNext() T } And then use it like: for i.HasNext() { current := i.GetNext() // do things here } It's an extra line, sure, but it's pretty obvious what's going on.
- pkolaczk 3y agoThis is the way how Java iterators work. And having worked with them for many years I must say - please don't go that route. Having separate hasNext/next methods is terrible for implementers of the iterator. The problem arises when you need to actually compute the next item to learn if it exists. So then your hasNext is actually effectful and needs to cache the result internally. There are so many ways to get it wrong, I've even seen some terrible implementations which required the caller to call hasNext or otherwise they would not advance the iterator. Iterator design is another area where sum types would help simplify things a lot. Just one method returning Some/None would be so much better.
- masklinn 3y ago> Iterator design is another area where sum types would help simplify things a lot. Just one method returning Some/None would be so much better. It would be better, but since go has MRV and zero values func Next() (hasNext bool, nextValue T) works fine, this is not Java or C# where you need to split the protocol into two separate operations.
- Kamq 3y agoSince we're talking about implementing it in usercode, as opposed to adding it to the language, we fortunately have the option to rework it (or have a couple completing implementations to see what works in practice). If you're looking for those particular features, I'd recommend adopting go's `, ok` idiom that appears in several places in the language. A type like: type Iterator[T any] interface { Next() (T, bool) } Lets you write code like this, if that's more to your liking: for current, ok := i.Next(); ok; current, ok = i.Next() { // do things here } Which, really seems like a more go-like way to do this in the language as well (ranging over a function then becomes syntactic sugar for the above).
- tapirl 3y agoGo development is becoming more and more KPI driven.
- slekker 3y agoWhat is KPI in this context?
- nullifidian 3y agoThey probably meant that programming language designers/developers can only advance their careers by adding/implementing features, not presiding over something complete.
- MAGZine 3y agobecause go developers are sick of being pure and like to type shorter things (and I don't mean variable names) to get their jobs done. programming is a lazy hobby. it's why as soon as generics were released, I immediately implemented 'Keys', 'Values', 'ToSet', etc. Writing multiple lines to do the equivalent of these functions might be 'pure' but it's not easier to read. Worse: instead of standardizing these common operations, you just have everyone rolling their own implementations of the exact same functions with slightly different naming/parameters/etc. Am I confused in that there is some merit to this approach beyond dogma? Why do we, as a community, need to accept so few batteries? If you want to only use assembly in your code (the purest!), that's always an option.
- imiric 3y agoIt's not about purity or dogma. It's about keeping complexity out of the language as much as possible, which is much harder to do. Language designers always make a tradeoff between making programming tasks convenient, and introducing more complexity that makes the language more difficult to maintain and less approachable, and programs written in it more difficult to read and reason about. Being productive in a language is not just about using every convenience at your disposal, and typing less. It's about producing code that is easy to read and approachable for others or yourself years down the line. The more language features one needs to be aware of when writing or reading code, the less approachable it is. Having less features also forces the programmer to rethink their solution in a less clever way. In dynamic languages like Python and Ruby it's very convenient to use some fancy trick to write less code. In languages like Rust or Raku, you can (ab)use some obscure feature in a non-intuitive way, and feel good about the "elegance" of the solution, but a fresh pair of eyes might struggle to understand what the code actually does. Considering reading code is much more important than writing it, as we literally write code for humans, not machines, optimizing for simplicity and having less features in a language is a more productive long-term approach.
- pkolaczk 3y agoKeeping complexity out of the language does not remove the complexity. It moves it into the programs. MIPS assembly is a very simple language with specs fitting on 10 pages. Yet you probably wouldn't want to write a web application in it. Brainfuck is even simpler as a language, but the programs are even harder to understand.
- JyB 3y agoExactly. Go making it's way faster than I expected to be replaced by yet another language that discovers that there's a demand for feature restriction and simplicity. The very thing that made it popular in the first place. It should be rejected but I'm losing hope. I have less and less faith in rsc as time passes.
- steeve 3y agoWhy do you have less faith in rsc? Asking because I feel the opposite.
- throwawaygo 3y agoBingo
- cy_hauser 3y agoI think the pace has been extremely slow. So many of the changes to Go have been tool chain or library enhancements. Outside of generics related stuff, what are some added core language features that you'd prefer were not?
- Mawr 3y ago> iterators can be easily implemented with current Go features. It's rare that can is relevant when it comes to language design. You typically can do anything in any language. Adding dedicated support for iterators would promote it as the way to iterate. That would actually simplify the language - one way to express a common need as opposed to the current mess of ~5 different ways [1]: "In the standard library alone, we have archive/tar.Reader.Next, bufio.Reader.ReadByte, bufio.Scanner.Scan, container/ring.Ring.Do, database/sql.Rows, expvar.Do, flag.Visit, go/token.FileSet.Iterate, path/filepath.Walk, go/token.FileSet.Iterate, runtime.Frames.Next, and sync.Map.Range, hardly any of which agree on the exact details of iteration. Even the functions that agree on the signature don’t always agree about the semantics." Go would have been a far simpler language had it originally shipped with generics, iterators, and sum types. [1] https://github.com/golang/go/discussions/56413 https://github.com/golang/go/discussions/56413
- imiric 3y agoI agree with you to an extent. But adding more features is always a tradeoff between introducing complexity, and supporting a use case that might not be useful in all programs. For example, generics were a highly controversial topic, and many people still believe that Go didn't need them. I'm partially in that camp. I've encountered maybe one or two situations where generics would've been convenient since Go 1.18, and in both cases not jumping straight at the opportunity to use them, but being forced to refactor and approach the solution in a different way, has produced simpler and more readable code. To this day, I struggle with Go's generics syntax, and it just looks alien to me. Iterators are a useful feature, but are they really generally useful to deserve a change in the language spec? Like generics, I've yet to encounter a situation where they're truly required, and when I do, implementing them myself is trivial. So, sure, language designers can cram every programming construct we've invented in the last 50 years, as Rust and Raku have done, and many programmers will find this very convenient and flexible, but a more focused and conservative approach produces a simpler language that is easy to pickup and read. This is what drew me to Go in the first place, and lately it seems that this is changing.
- deleted 3y ago[deleted]
- cy_hauser 3y ago>> "there's only one way to do it" What's the one way to do iterators now? I thought this proposal came about because there wasn't a default way and everybody had to roll their own.
- ilyt 3y agoyou can just return channel. range works over channels. Close channel once you're done with iterator. Technically there is side effect of that (iterator done like that would always produce extra value before exiting) but you could probably work around it without much boilerplate
- masklinn 3y agoChannels introduce a ton of overhead (https://syslog.ravelin.com/so-just-how-fast-are-channels-anyway-4c156a407e45?gi=6bf63d1d1bfc https://syslog.ravelin.com/so-just-how-fast-are-channels-any...), plus concurrency issues, and termination concerns (they are essentially a resource leak). It's nice that you can iterate channels, but they're a terrible iteration protocol.
- greyman 3y agoI too hope this will be rejected, the proposal is terrible. One of the core advantages of Go was, that if newbie colleague comes to the team, he will be able to read and understand the code very quickly. Things like this will over time kill this Go's advantage.
- rplnt 3y ago> Adding syntax sugar muddles this, and introducing another keyword There is no new syntactic sugar nor there is a new keyword here though. It just changes how range works. That being said, I'm not a fan of range having two modes of functionality. Perhaps it could just use an interface, now that we have generics it can be implemented on the existing supported types. And compiler can do whatever with it if it would be too slow.
- assbuttbuttass 3y agoThis is a textbook example of syntactic sugar, where a feature can be trivially transformed into simpler code that doesn't make use of the feature