3 ms·
> Certain things in Go are not easy (not simple), because Go has few language features (is simple). Too true, I touch on that in a post I made here as well. In
by asdkhadsj 7y ago
> Certain things in Go are not easy (not simple), because Go has few language features (is simple).
Too true, I touch on that in a post I made here as well. In Go I often found myself having to reuse logic in functions that, in Rust, could be hidden behind iterators or custom iterator implementations.
While the Rust version is definitely more complex if you look at the implementation of the iterator; in practice you are just using the iterator - and that ends up meaning the problem you're solving keeps locality, and is easier to reason about. In my experience, at least.
- PyroLagus 7y agoI wonder if it is actually more complex. Emergent complexity is a thing, and one only needs to look at Go (the game; pun definitely (not) intended). The rules are dead-simple; you can learn them in under a minute. But to actually play the game well, you have to learn a lot more. You have to memorize openings, learn to identify and utilize patterns, etc. There is a lot of "meta-play" going on, and you could say that high-level Go play is full of abstractions. In fact, you won't have a chance without the abstractions. If you only see individual stones rather than shapes, you're done for. I think the same applies to languages like Go. You don't have generics, but if you want to effectively re-use code, you need abstractions. The difference now is that in languages like Rust, you have predefined and marked abstractions, while in Go, you define them yourself and need to recognize them yourself. Granted, Go also makes certain kinds of abstractions difficult or impossible to manually define within the language, so you may end up with copy-pasted code, but I imagine even that will be manually abstracted through naming and such. The Rust iterator is a pretty simple abstraction in my opinion. It's essentially a lazy list. You see an iterator used, you know what you're getting. You see the map function used, you know it's the same as a for loop over all the elements. A fold/reduce is just a for loop over all elements with a result variable that gets returned. I really don't think using iterators is any more complex than a for loop, and with a for loop, you actually have to look through it to identify the pattern used and find out what it does. I'd say having to manually check for patterns is more work, but I couldn't say which one is more complex; maybe neither is.
- kjksf 7y agoYou can encapsulate iteration logic in Go. I do it in my libraries when warranted. The pattern is: v := NewIteratedValue() for v.Next() { item := v.Item // process item } if v.Err() != nil { // process error } A variation of this pattern exists in standard library and third part libraries. I know that you meant implementing some sort of iteration protocol natively supported by the language, so that you can write: for v { ... } vs: for v.Next() { } But you ended up making much stronger and untrue claim that one can't write re-usable iteration logic in Go.
- atombender 7y agoThat's an apples-to-oranges comparison because your iterator interface here isn't generic like Rust's. It isn't reusable. Your iterator has to be specific to a set of types — "v.Item" in your example is always of some specific type. Using an untyped interface{} would be worse than any alternative because every single use would have to use type switches and casting; you lose out of compile-type type safety. Because of this, even if you have a local iteration system, it isn't composable beyond your package and its types. Given this (simpler) interface: type Iterator interface { Next() (Foo, error) } ...then I can write a map function: func Map(it Iterator, mapper func(Foo) Foo)) Iterator ...but I cannot write a generic one that works with anything other than Foo. And if I need this: func MapFooToBar(it Iterator, mapper func(Foo) Bar)) Iterator ...then I have to write it just for that purpose. That's why there's no "iterator library" for Go, like there is with Rust.
- asdkhadsj 7y ago> But you ended up making much stronger and untrue claim that one can't write re-usable iteration logic in Go. Not at all. Are you telling me that `v.Next()` can be written in a reusable way for different data structures? Nonsense. `v.Next()` will have a single, bound return type. So at that point you're re-implementing iterators for anything you want to iterate on. Furthermore, lets say I want to `v.Next().Map()`. How can I write a Map function in a reusable way? The entirety of your iterator chain would have to be custom implemented for the one data structure you're using. Or you throw your entire type information away and use `interface{}`. With Go, your limit of reusability is hand writing the entire iterator implementation per type. Which is hardly reusable in my view. I touched on this when I mentioned creating separate functions for these patterns. That's all you're doing here - creating custom methods to make something look like, but not behave like an actual iterator. No Map support, no Filter, no .. anything. All you made was a loop, and a hand written one. I'm not trying to be overly negative here. However saying Go has an iterator pattern is imo strongly misleading.