Design for collections upgrades
Rémi Forax
forax at univ-mlv.fr
Sat Mar 12 15:27:17 PST 2011
On 03/12/2011 11:01 PM, Reinier Zwitserloot wrote:
> From personal experience, students are completely bewildered that
> someList.map(someClosure) has absolutely nothing at all to do with maps (in
> the java.util.Map sense). 'transform' is a better name. I'm not sure which
> SQL term is a good fit for map. "select" doesn't really seem to cover it,
> though I'm guessing that's what Llewellyn intended.
'map' means association. Map stores associations, map apply an
association function,
so they are related.
> Immutable maps are not part of java's type system (at least, rt.jar). Java's
> type system leans heavily towards mutable collections (i.e. the basic Map,
> Set, Collection and List include mutating methods, and none of them have
> methods that return new collection instances based on old instances. For
> example, no such interface has an 'append' method, or a not-in-place
> retainAll/removeAll).
Yes.
> Thus, as Stephen Colebourne said, consistency says that these methods should
> run in-place, even if that doesn't fit the expectation of those who are used
> to functional programming. It wouldn't make any sense for List to have NO
> .append() method, which returns a new list containing the same elements as
> this list plus a new element added at the end, while it DOES have a
> .filter() method which returns a new list containing all elements that pass
> the supplied filter closure. This does mean that i.e. .transform() HAS to
> transform from one type to the same type.
>
> .filter() is easily named: retainAll and removeAll are already in the API
> and need merely be overloaded with the signature that takes a closure
> instead of another collection. Just like retain and remove, these would
> definitely be in-place. That leaves 'filter' free for some future immutable
> collection API that returns a new instance.
I agree. We have to provide mutable version of of map/filter.
> .map() / .transform() is the true problem here: Not only is naming this one
> difficult, its also annoying to do in-place, as an in-place transform
> requires the mapping to go from ? super E to ? extends E; you can't map from
> String to Integer with such a construct. Nevertheless, including a
> non-in-place version of transform() as a defender method just doesn't feel
> consistent, but adding the full set of immutable 'mutators' (append,
> prepend, a non-in-place take on removeAll/retainAll/addAll, a non-in-place
> variant of removeAll/retainAll, in-place transform and non-in-place
> transform, and more) - that's a huge undertaking and adds a large bunch of
> new methods to the collections API, all of which has to be written with
> defender methods so as to stay backwards compatible, and they all suffer
> from the issue of not returning the same type of collection as the starting
> type. For some types this is irrelevant, but i.e. a LinkedHashMap returning
> a plain HashMap is definitely sub-optimal. Letting each collection type use
> i.e. .newInstance() to help with this also doesn't sound like a good idea;
> for starters not every conceivable subclass of Map/List/etc can be
> effectively cloned by re-filling it in the same order as its iterator()
> method supplies values, and it would create a large timespan in which some
> collections do return 'Self' for .newInstance(), but others fall back on
> HashMap. If this idea is persued, .clone() or more likely a newly introduced
> and defenderized .copy() should be used instead of .newInstance().
You forget the golden trick :)
We don't do lazy filter on the collection itself but on the stream
created from the collection.
collection.asStream().filter(...)
asStream() is here to say, I don't want to consider the collection as
a mutable collection but as an sequence of elements.
And filter or map will return a new stream.
> Thus, I don't think adding non-in-place versions of anything is a feasible
> idea for the existing Collections API; there are far too many issues.
>
> Instead, I suggest (perhaps for JDK8) an entirely new type tree is made for
> immutable collections, including the complete set of immutable not-in-place
> versions of retain/remove/appendAll/append/prepend/etc. As it would be new
> API none of these methods would need to be defender methods and thus each
> implementation can be responsible for returning the appropriate type.
>
> In the mean time, a utility method in Collections which filters / transforms
> existing collections into a new collection of a preset known type (HashSet,
> ArrayList, and LinkedHashMap seem like the most obvious choices here) can be
> added to at least let programmers do a String->Integer mapping using just
> the classes in JDK7's rt.jar. Such a method would have to be named
> appropriately to minimize confusion (as Collections.sort() would do an
> in-place sort, but Collections.transform() would produce a new ArrayList,
> for example. Possibly a new utility class needs to be created to separate
> these two modes.
>
> --Reinier Zwitserloot
Rémi
More information about the lambda-dev
mailing list