Typed variants of primitives

John Rose john.r.rose at oracle.com
Wed Dec 2 08:06:47 UTC 2020


On Nov 29, 2020, at 4:13 PM, Stephen Colebourne <scolebourne at joda.org> wrote:
> 
> I wanted to raise a concept that I don't remember seeing as part of
> the valhalla work so far, and I'll do so via a java.time.* example.
>> Now of course, it is almost certainly pie-in-the-sky to try and make
> something this backwards compatible. But what about new types? In API
> design terms, there is appeal in defining a type that restricts the
> valid set of ints, for example a `PositiveInt` value type. But without
> the associated boxing/unboxing to `int` and maths operator-overloading
> it is generally more pain than it is worth to design an API that way.
> Has this concept been considered?

I think this concept is subsumed within the larger topic
of “user defined primitives”, which itself has several sides
including the ones you bring up.

Make Year be a user-defined primitive backed by a sufficiently
large built-in primitive, say int or short.  As a class-based type,
it will have {de-,re-,}constructors and field extractors to wrap
and unwrap a bare integral value.  The wrap operation
(constructor) can validate the year, if that’s desirable.  The
unwrap operation can just expose the integral value,
perhaps with special processing in the case of the default
value for Year (Year.default).

That’s enough to get going, I think.  And it’s about the same
story as if you were making user-defined (class-based) primitives
for complex numbers, unsigned integers, jumbo integers,
tiny floats, or SIMD vectors.

Maybe we want to do arithmetic using natural Java expression
notation; that’s a reasonable thing to want, though it’s hard to
do well.  (“Just overload operators like C++ kthxbi” is easy but
almost certainly wrong.)  There are lots of options here.

Maybe we want an automatic story for migrating from int-based
to Year-based API points, as you suggest. Again, a reasonable ask,
but it requires not only a flexible way for establish type mappings
(between old and new types) and a way for marking methods
for migration, but also a delicately balanced set of rules that
will tend to prevent surprises and errors.  Again, lots of options.
Note that user-defined operators is a near neighbor to user-defined
conversions (e.g., in C++) and so perhaps a solution for natural
expression notation can be parleyed into a solution for organizing
migration type conversions.

My working title for the JVM support for morphing types of API
points is “Bridge-O-Matic” (considered narrowly), or more broadly
“Method Implementation Dynamic”(friends call it “mindy”).  The
basic idea is to allow the JVM’s dynamic linker mechanism fall
back from a failed signature match to a call to a related entry
point, as if via MH.invoke instead of MH.invokeExact.  I don’t
know what the language-level notation for setting up such a
fallback would look like.  I mention it here because, if you can
allow Year and int to agree on an implicit conversion (arguably
a sub-goal for natural expressions on user-defined primitives),
then you could ask the JVM to apply an asType transform
including such a conversion to help you adjust the mismatch
between a caller and callee.

Also, there’s no need for a fully-dynamic Bridge-O-Matic
if javac is somehow tasked with emitting static bridges that
contain (perhaps statically or perhaps via indy+asType)
the required conversions.  Such static bridges would be,
I presume, marked “Synthetic”, so that they would not
appear as API points visible to source code, just JVM-callable
legacy methods for down-rev code.

So, yes, we’ve thought about this stuff in various venues, and
it seems that your concerns could be handled by a mix of
“user defined primitives done right” and “method signature
migration done right”.  Did I miss anything?

— John


More information about the valhalla-dev mailing list