Primitive-parametrized classes covariance with pre-existing specialized types

Olexandr Rotan rotanolexandr842 at gmail.com
Thu May 16 14:06:05 UTC 2024


It would compile, but then predicate will no longer be a functional
interface, isn't it? Correct me if I am wrong. There could be workarounds
using default methods though.

Also, both Function and Predicate are in the public API, so changing class
hierarchy could potentially (and will) break behavioural backward
compatibility in if and switch statements, and it will break it silently,
introducing bugs in various unexpected places.

On Thu, May 16, 2024 at 4:53 PM Paul Bjorkstrand <paul.bjorkstrand at gmail.com>
wrote:

> It would compile if Predicate<T> extends Function<T, boolean>. I don't
> think that specialized generics would equate to "immediately remove all
> hand specializations." With Predicate's wide use, it would probably take a
> decade or longer to fully remove it from the ecosystem, and that is
> assuming it would be removed at all (how much code still uses HashTable,
> when there are now generic replacements?)
>
> // Paul
>
>
> On Thu, May 16, 2024 at 8:23 AM Olexandr Rotan <rotanolexandr842 at gmail.com>
> wrote:
>
>> While this is in some sense natural, this sort of “anything goes”
>>> flexibility that incorporates both nominal and structural subtyping creates
>>> new problems, and even if not, is unlikely to move us very far in the
>>> direction of “IntStream is-a Stream<int>”.
>>
>> My main concern with transitioning from  Predicate<T> to Function<T,
>> boolean> (or any other primitive-specialized, predicate is not the biggest
>> problem, obviously) is that if currently, hypothetically, we have changed
>> all specialized functional types to generic functional types, even if they
>> essentially "behave the same", would completely brake
>> backwards compatibility. Not even talking about binary compatibility, about
>> which I don't know virtually anything, I could easily think of a scenario
>> where this change would break source compatibility:
>>
>> ToIntFunction<T> mapper = ....;
>> someList.stream().mapToInt(mapper) .... // now won't compile if mapToInt
>> expects Function<T, int>
>>
>> That was the main concern that I have tried to express with the
>> initial letter. I am all for any easier workaround than the one I have
>> described, but if we want to move to “IntStream is-a Stream<int>” (and I
>> guess we really want), some way to convert between specialized and generic
>> types should be present at least in some form.
>>
>> On Thu, May 16, 2024 at 3:35 PM Brian Goetz <brian.goetz at oracle.com>
>> wrote:
>>
>>> I am currently working on some Stream API enhancements and its internals
>>> have become really complicated as time passed, mainly (imo), due to
>>> primitive-specialized pipelines.
>>>
>>>
>>> Slight correction: the internals were complicated from day 1, for this
>>> reason.  This is not something that “happened over time”; the
>>> hand-specialization was part of its fundamental complexity.
>>>
>>> JEP 402, at first glance, looked like salvation
>>>
>>>
>>> You are mistaking JEP 402 for the full sweep of the Valhalla project.
>>> Indeed, Valhalla grew out of the observations of the painfulness of hand
>>> specialization (among other challenges).  But before we can get to full
>>> generic specialization with both layout and code specialization, we have to
>>> cover some fundamentals first.  There are many steps along the way here.
>>> We walk before we can run.
>>>
>>> What you are asking for is to blur the distinction between nominal and
>>> structural function types, allowing Predicate<T> and Function<T, boolean>
>>> to be considered “the same” in some way.  While this is in some sense
>>> natural, this sort of “anything goes” flexibility that incorporates both
>>> nominal and structural subtyping creates new problems, and even if not, is
>>> unlikely to move us very far in the direction of “IntStream is-a
>>> Stream<int>”.
>>>
>>> Rest assured that we share your goal, but the path to get there is
>>> somewhat longer.
>>>
>>> , but the more I think about it, the more problems I see with
>>> integrating legacy with new features. Specialized stream pipelines are
>>> wired with specialized functional interfaces (ToXFunction,
>>> XPredicate etc.). This (and also some specialized for primitive ops of
>>> streams), make it impossible to harmlessly switch from XStream to Stream<X>
>>> (where X is primitive).
>>>
>>> While the second issue (specialized methods) could be addressed
>>> separately, one thing that could certainly help with eventually moving on
>>> from primitive-specialized types induced mess in current APIs is
>>> introducing covariance between generic types and their specialized
>>> versions. It is intuitive that ToLongFunction<T> is essentially the same as
>>> Function<T, long>. I saw a note at the end of the JEP that Long! and long
>>> are not fully interchangeable for some internal for JVM reasons, but the
>>> API that these interfaces provide are exactly the same. I understand that
>>> if public APIs that types provide are similar, that still doesn't mean
>>> their behaviour is the same, but while it does not mean it, it could be the
>>> case (and is a case in huge amounts of situations as when it comes to
>>> specialized types).
>>>
>>> If such covariance would be present in language, huge amounts of code
>>> could be just clamped as they are no longer needed. For example, whole
>>> IntPipeline could be clamped to IntPipeline extends ReferencePipeline<int>
>>> { /* proprietary methods of IntStream */ } instead of virtually a second
>>> ReferencePipeline class but specialized for int. This example is just the
>>> thing that is closest to me right now, but I am sure anyone could think of
>>> a way where this could be helpful for them.
>>>
>>> I am not really sure how this is implementable in a way that does not
>>> weaken the type system though. We can't just let users write that class A
>>> is covariant to class B<X> or something like that for obvious reasons. The
>>> easiest solution is to just add some compiler rules for a pre-defined
>>> number of types in jdk, but this still will leave all library devs and just
>>> users that had to write specialized types for some reason stranding. The
>>> rules for this covariance could surely be a topic of discussion for more
>>> experienced developers than me, but to have this feature would be a
>>> dramatic step forward in Java.
>>>
>>> Regards
>>>
>>>
>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20240516/374d39a1/attachment.htm>


More information about the valhalla-dev mailing list