RFR: 8180352: Add Stream.toList() method

forax at univ-mlv.fr forax at univ-mlv.fr
Tue Nov 3 22:44:56 UTC 2020


Hi Aaron, 

> De: "Aaron Scott-Boddendijk" <talden at gmail.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Brian Goetz" <brian.goetz at oracle.com>, "Stuart Marks"
> <smarks at openjdk.java.net>, "core-libs-dev" <core-libs-dev at openjdk.java.net>
> Envoyé: Mardi 3 Novembre 2020 21:59:37
> Objet: Re: RFR: 8180352: Add Stream.toList() method

> >>> But it can not be immutable too, for the same reason.

> >> Nope. The spec of toList() very clearly says: no guarantees as to the type,
> >> mutability, serializability, etc etc of the returned list. That doesn't mean
> >> that every method returning a List added to the JDK after Collectors::toList
> >> must similarly disavow all such guarantees.

> >> (In fact, we would be entirely justified to change the behavior of
> >> Collectors::toList tomorrow, to match the proposed Stream::toList; the spec was
> >> crafted to preserve this option. We probably won't, because there are too many
> >> programmers who refuse to read the specification, and have baked in the
> >> assumption that it returns an ArrayList, and this would likely just be picking
> >> a fight for little good reason, regardless of who is right.)

> >I don't understand your argument.
>>You know that you can not change the implementation of Collectors.toList(), and
>>you also know that if there is a method Stream.toList(), people will replace
> >the calls to
>>.collect(Collectors.toList()) by a call to Stream.toList() for the very same
>>reason but you want the semantics of Stream.toList() to be different from the
> >semantics of Collectors.toList().

> Surely, taking that position, List.of(...) should not have produced an
> unmodifiable list since many combinations of creating Lists would not have
> produced unmodifiable lists.

The problem is that people will see stream.toList() as a shorcut for stream.collect(Collectors.toList()), 
i've not seen my student thinking that List.of() is a shortcut for creating an ArrayList with some elements, but maybe it's because we introduce ArrayList later compared to List.of(...). 

> In all of the teams I've worked with, through many Java version transitions (and
> 3rdparty library version transitions), dealing with the selective adoption of
> new APIs is nothing new.

> This case is no different, developers will need to identify the cases in which
> they can accept the list as unmodifiable and replace those uses of
> Collectors.toList() and Collectors.toUnmodifiableList().

We have Collectors.toList() and Collectors.toUnmodifiableList(), would it make more sense to have stream.toList() and stream.toUnmodifiableList() instead of having stream.toList() that behave half way in betwen the semantics of Collectors.toList() and Collectors.toUnmodifiableList(). 

> And static-analysis will be able to propose some of those replacements for us.

I believe that a tool able to suggest to use Collectors.toUnmodifiableList() instead of Collectors.toList() will rely one a global interprocedural static analysis, so such analysis will be invalid once you will update one of your dependency. 

> Does this mean it's more complex than a text-replace, sure. Will there be
> mistakes, sure. But are there benefits to the immutability of the API result? I
> suggest that's pretty hard to debate against given the risk of a mistake is an
> exception rather than bad data outcomes.

You want immutability/unmodifiability over the cost of people miss mis-using the API. I think it's a sin as bad as me want stream.toList() to return a null hostile List :) 
As Brain implies, Java is a blue collar language, we should not forget it, whatever we think about null or immutability. 

And I said above perhaps we should not discussed about adding a method Stream.toList() but a method Stream.toUnmodifiableList(). 

> Please don't blunt the tool to make it safer for children.

> --
> Aaron Scott-Boddendijk

Rémi 

> On Wed, Nov 4, 2020 at 9:30 AM < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr
> ] > wrote:

>> > De: "Brian Goetz" < [ mailto:brian.goetz at oracle.com | brian.goetz at oracle.com ] >
>> > À: "Remi Forax" < [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] >
>>> Cc: "Stuart Marks" < [ mailto:smarks at openjdk.java.net | smarks at openjdk.java.net
>> > ] >, "core-libs-dev"
>> > < [ mailto:core-libs-dev at openjdk.java.net | core-libs-dev at openjdk.java.net ] >
>> > Envoyé: Mardi 3 Novembre 2020 20:54:57
>> > Objet: Re: RFR: 8180352: Add Stream.toList() method

>> >>> There is no value in making users remember which stream methods are
>> >>> null-hostile and which are null-tolerant; this is just more accidental
>> >>> complexity.

>> >> I think that ship has sailed a long ago.
>> >> Some collectors are null hostile, some are not.
>> >> You can make a point that a Collector is technically not the Stream API per se.

>> > Yes, and this is no mere "technical argument". A Collector is a collection of
>> > behaviors, governed by its own spec. The behaviors passed to stream operations,
>> > whether they be lambdas passed by the user (`.map(x -> x.hopeXIsntNull())`) or
>> > collector objects or comparators, are under the control of the user, and it is
>> > the user's responsibility to pass behaviors which are compatible with the data
>> > domain -- which the user knows and the streams implementation cannot know.

>> >> Because of that, i don't think we even have the choice of the semantics for
>> >> Stream.toList(), it has to be the same as stream.collect(Collectors.toList()).

>> > This doesn't remotely follow. (And, if it did, I would likely not even support
>> > this RFE.)
>> It seems you had an issue when replying to my mail, there is a paragraph before
>> "Because"

>> > Let's take a step back,
>> > if we introduce a method toList() on Stream it will be used a lot, i mean really
>> > a lot, to the point where people will change the code from
>> > stream.collect(Collectors.toList()) to use stream.toList() instead.

>> > The spec of Collectors::toList was crafted to disavow pretty much anything other
>> > than List-hood, in part in anticipation of this addition.

>> >> But it can not be immutable too, for the same reason.

>> > Nope. The spec of toList() very clearly says: no guarantees as to the type,
>> > mutability, serializability, etc etc of the returned list. That doesn't mean
>> > that every method returning a List added to the JDK after Collectors::toList
>> > must similarly disavow all such guarantees.

>> > (In fact, we would be entirely justified to change the behavior of
>> > Collectors::toList tomorrow, to match the proposed Stream::toList; the spec was
>> > crafted to preserve this option. We probably won't, because there are too many
>> > programmers who refuse to read the specification, and have baked in the
>> > assumption that it returns an ArrayList, and this would likely just be picking
>> > a fight for little good reason, regardless of who is right.)
>> I don't understand your argument.
>> You know that you can not change the implementation of Collectors.toList(), and
>> you also know that if there is a method Stream.toList(), people will replace
>> the calls to .collect(Collectors.toList()) by a call to Stream.toList() for the
>> very same reason but you want the semantics of Stream.toList() to be different
>> from the semantics of Collectors.toList().

>> Rémi


More information about the core-libs-dev mailing list