Specialized primitive implementations after generics specialization
Brian Goetz
brian.goetz at oracle.com
Tue Aug 31 14:26:06 UTC 2021
We surely would like for all the hand-specifications to retreat to the
dustbin of past expediences. For some, we can fairly easily coopt them
by retroactively giving them supertypes:
interface IntPredicate extends Predicate<int> { }
interface Predicate<T> extends Function<T, boolean> {
boolean apply(T t) { return test(t); }
}
and guiding future usage towards the more general `Function` type.
For others, like IntStream, we're not going to be able to make IntStream
extends Stream<int> (try it, you'll run into the fact that the dual of
covariant overrides is not present) but we can eventually deprecate
IntStream in favor of Stream<int> (the "cut off the head of the snake"
strategy.) As you suggest, this leaves the question of what to do with
methods that are not total (e.g., sum(), max(), sorted()), but this is
merely an extension of a more general need to express behavior that is
conditioned on the properties of a particular type parameter. Stream is
not special here; if the elements of a List are "comparable", it would
be nice if List had a sort() method, etc.
In Haskell, we'd express this as follows:
Ord t => sort :: List t -> List t
which says roughly "given the precondition that t is orderable, List
supports a sort operation". We almost have something like that today:
generic type bounds. But we don't yet have the ability to express
something like:
<where T has Comparator<T>>
void sort()
which would constrain the sort() method on List to instantiations where
the type parameter can provide a witness to ordering. This requires a
lot of things to pull off, including the ability to associate behaviors
with types, not just instances (e.g., something more like type classes),
and either specialization (so that the witness to `Ord t` can be derived
from `t`) or something like Scala's implicits to access the witness at
runtime, etc.
Rest assured we're exploring these areas as well, since they are
essential to creating expressive libraries. (But, let's not discuss it
further here; let's wait for a more complete story first.)
On 8/30/2021 10:26 PM, John Rose wrote:
> We expect to upgrade the existing generics so that the
> JVM can access enough information about types to
> perform various customizations. The details are hazy,
> but we fully expect to be able to supply ad hoc methods
> like sum. Maybe even ad hoc protocols like Summable,
> if we are lucky. Hopefully also ad hoc fields, like an
> “Optional::isPresent” field that is elided (typed as “void”
> or some other unit or empty type) when the isPresent
> logic can be derived from the main field of the Optional,
> as today.
>
> The basic insight here is that, if there is enough runtime
> information to guide the customization of data structures
> (as today’s primitive arrays and fields have layouts
> customized to the primitives), then there is also enough
> runtime information to perform the coordinated task
> of customizing algorithms (think Arrays.sort, on flat
> monomorphic inputs).
>
> Then, given those two firm requirements, the JVM
> surely also has enough information to drive the creation
> of optional methods, especially if (as we hope) the
> optional methods can be modeled as always-present
> but sometimes-degenerate methods.
>
> To take your example, List<X>::sum would do what
> you want if X has a “summable” type. (Where the
> condition of “summability” is encoded by a type bound
> or perhaps a type class; details to be worked out.)
>
> Meanwhile, if X has some other random type, then
> List::sum would still link but throw a suitable error
> when called. (The static compiler javac might report
> a static error when it can prove that this is bound to
> happen, for some static type X.)
>
> This scheme, of degenerate methods and fields, plays
> well with raw types and wildcards, where the decision
> to throw that error would depend on the type of the
> receiver object.
>
> For our current thought on how to rewire the JVM
> to pass around sufficient information about generics,
> please see the Parametric VM manifesto:
>
> https://github.com/openjdk/valhalla-docs/blob/main/site/design-notes/parametric-vm/parametric-vm.md
>
> HTH
>
> — John
>
> On Aug 30, 2021, at 6:45 PM, Nir Lisker <nlisker at gmail.com> wrote:
>> Hi,
>>
>> I have a question(s) about the need for primitive specialized
>> implementations when type parameters will accept primitives.
>> Today we need various specializations like IntFunction and IntStream.
>> However, the difference between them is that IntStream brings with it
>> methods that make sense on int, like sum(), while IntFunction does not.
>> Does this mean that some specializations will still be needed, or will
>> there be another way of doing it?
>> (I'm not advocating to do anything with IntFunction or IntStream, just
>> using them as examples.)
>>
>> - Nir
More information about the valhalla-dev
mailing list