Enumerable chaining #33
Labels
No labels
bug
discussion
documentation
duplicate
ignore
new feature
question
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
golibs/gms-gods#33
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
I noticed you have
MapandSelectmethod commented out inEnumerableWithIndexandEnumerableWithKeyinterfaces since returning a container will require type assertion at every step, pretty ugly for chaining.I would suggest returning
EnumerableWithIndex(orEnumerableWithKey) for those methods and add aToContainermethod at the end.This would allow API calls like:
At the end, the user is free to do any type assertion as they want.
I did notice you already have chaining tests in files like
arraylist_test, but these only allow implementation level chaining, it kind of defeats the purpose of having interfaces though...I agree with everything you said. I was not sure how to solve this problem and left it for some other time. I tried a few ways, but could not reach the desired effect.
How would you implement it? I am not sure I understood the
ToContainermethod? What does it do exactly?Also if one returns
EnumerableWithIndex(orEnumerableWithKey) then it's impossible to mix between those two, right?I am just not sure, if I can make this work as in Ruby where you can mix maps, arrays, etc. without any type assertions, which are really ugly to that point that the library becomes useless.
Can you please make a proof of concept, either in gods or on golang playground just so I get the idea. Thanks
Yep, I forked your repo to add and fix a few things for use of another project. The proposed enumeration changes are in there.
https://github.com/davidiamyou/go-companion/blob/master/containers/enumerable.go
You can then take a look at various data structure packages where there is the
enumerable.go. Essentially, all lists now implementEnumerableWithIndex, all sets now implementEnumerable, all maps now implementEnumerableWithKey. The reason for this is that lists are by nature ordered, sets are by nature unordered (even though underlying implementation can have its elements ordered, API should not make this visible unless user does explicit type assertion himself) and maps are by nature key-valued.The
ToContainermethod is really a type assertion disguised in API form. You can see in my implementations that it just returns the receiver as is. I just thought a redirection back to theContainertypes after chaining will be nice. It could be argued that we can change theToContainermethod to something more specific. i.e. ForEnumerableWithIndex, useToList()to allow direct access to more rich APIs thanContainer.I also returned interfaces only for
New()constructors except for the trees. IMHO, users should only work with interface APIs and be shielded away from the implementation details. This is partially why, I guess, the above proposed type assertion is way easier, as it no longer have to deal with specific concrete types.Thanks for finding time to explain your idea.
I found this in your commit. It looks like type assertion is still necessary or is there another actual chaining example? That's basically what I also get if I uncomment the code, but without the ToContainer() call.
I'll try to create a simpler example on golang playground and see if we can figure something out here without the ugliness of type assertions (at least not on the container types).
This is (probably unreachable) beauty of Ruby:
I was hoping gods would be able to do something like this without a ton of type assertions.
Right, this is what I am talking about removing the
ToContainer()method from the threeEnumerableinterfaces and replace with more specific methods likeToList(),ToSet()andToMap(). If I do that, type assertion in most cases would be unnecessary.Actually, thinking about it, I think I am gonna change my interfaces to that tomorrow. The only downside is that by doing this, I am effectively binding each of the three
Enumerableinterfaces to a specific kind of data structure. It would be hard later on for me to try to enumerate a map (with map entry instead of key value tuple, effectively treating it as a set), but I think it is a something I am okay with without massively changing your interface hierarchy.I totally agree with you on the beauty of the ruby syntax. Unfortunately, I suppose the core idea of Golang is to produce maintainable and simple code by preventing people to do too much smart things.
I will post my commit here tomorrow after I have changed my interfaces and fix the test cases.
You can find the changes to the interface in this commit
If we want things to be more flexible (for instance, capability to enumerate map as a set of entries and in the end convert that into actually a set), we would probably need to haul out something like Java's Stream API.
But for most day-to-day use cases, I would say this is sufficient.
I don't have too much of a problem with that, even though it's a huge task. If it would make the library more useful, I am up for it. However, I tried a few ways and could not make it work any close to Ruby.
IMHO somehow these To...() calls still resemble type assertions (in essence they are exactly that). Saying
ToSet()or type asserting.(*HashSet)is not that different and chaining would again be on the container super-type (lists or sets) level.I'll have to rethink this whole thing and perhaps try uncommenting the mentioned lines to return perhaps and Iterables? Perhaps that's the part that I missed trying to return the Container. Let me play with this for a while and see if there is any way to accomplish this, I probably need to look at it from another angle. I am even at the point to be forgiving in terms of having to type assert containers during chaining, if there is no other way.
You are right, it is still a type assertion in the end. Because enumerables have their own interfaces, I just cannot get away with type assertion without having to merge enumerable interfaces with data structure interfaces.
Please let me know if you have come up with anything, I would love to incorporate it into my project later.
At the meantime, I will take a look at the possibility of mimicing Java's Stream API.
i still don't have an idea how to solve this, closing after two years...