Two comments on the Constants API
Brian Goetz
brian.goetz at oracle.com
Mon Jun 4 17:28:56 UTC 2018
> First -- and this is the easy one -- consider changing the package name
> to from `java.lang.invoke.constant` to `java.lang.constant`.
That's entirely reasonable; "JLC" was one of the candidates we kicked
around, and there is nothing that makes JLIC intrinsically better than
JLC. We ended up with JLIC mostly because JLI has really become
"JL-VM", and we thought that putting this in JLIC would make it clear to
casual users that this is a low-level package for low-level users. But,
I would be happy with either choice.
> Second, please consider alternative approaches that don’t change the core
> classes that millions of Java developers use every day (e.g., `String`)
> by making them implement two new, obscure, low-level interfaces
Yeah, this has been a concern all along. IMO, the main cost here is
that, during the period between "WTF" and "Oh, I guess I can ignore it",
there's cognitive friction.
In an earlier iteration, we had a type ("ConstantRef.OfSelf") which
extended both ConstantRef and Constable, had default implementations of
the two required methods (both of which just return `this`), and was
implemented by the five special types (String, Integer, Long, Float, and
Double.) This got removed during the early review cycles of this API,
on the theory of minimizing unnecessary new types, around the same time
the Ref classes were renamed to Desc. Such a mechanism could be brought
back, probably with a different name.
That said, these five types _are_ special; they are fundamental to the
JVM. The challenge is, how we explain that in a non-distracting way, so
people can get back on with their lives.
Of the two, I think it is considerably more important that String et al
implement ConstantDesc than Constable; fortunately, I think this is also
the less subtle and less arcane of the two to explain.
One of the big things pushing us towards String <: CD is that the
varargs type `ConstantDesc...` shows up directly in the API in prominent
places (e.g., the static argument list of indy/condy.) Strings and
numbers are very commonly used as bootstrap arguments. (If it showed up
only in a non-varargs input context, we could use overloadings more
easily to work around special cases.) Some bad options we encountered
in the early days of this project, before the unifying type ConstantDesc
was discovered:
- We could type these API points as accepting `Object...` instead, and
do a dynamic test to make sure that its either one of the valid, but
this is more dynamic work and less type safety. (This also creates a
new API problem: since you can't safely overload methods with Object...
varargs at all, this would affect a number of overloads with some fixed
arguments and a varargs tail.)
- We could have a wrapper type for string and friends that implements
ConstantDesc; we actually tried this in an earlier version, and it was
(a) error-prone (you forgot the wrapper _every time_), (b) made the code
ugly, and (c) made the code less efficient. I realize a/b are "just"
pain borne by experts, but I think leaving this off of String and
friends would have a pretty big impact on not only the intrinsics from
JEP 303, but any other API that uses it (e.g., bytecode generation
APIs.) I would prefer to find a way to explain these types in a manner
that gets people to "nothing to see here, move along" more quickly.
On the other hand, Constable rarely shows up in an API (yet); instead
its used only reflectively by compilers / frameworks. So if String and
friends were not Constable, a compiler could add these dynamic checks in
the shadow of the existing dynamic check for Constable-ness.
> I’m not sure what the best way to fix this might be.
So, here's two ways we could reduce this friction.
1. Drop Constable (but not CD) off of String and friends (the cost of
this is a manageable amount of additional spec+compiler complexity).
Now the task reduces to explaining String <: CD. Some work on the spec
of ConstantDesc, rewriting it from the perspective of "You probably got
here because our mutual friend String gave you a coupon for a free
symbolic reference", aiming to minimize the amount of time spent in that
cognitive-friction zone between "WTF" and resumption of blissful
ignorance, could help. And, CD is not _that_ hard to explain; it's
something low-level code can use as the description for a low-level
component of a classfile.
2. Reintroduce some form of ConstantDesc.OfSelf, say NativeConstant
(shed to be painted), which implements both CD and Constable and
implements its methods. This is trivial to do, and the Javadoc for this
can be much simpler and friendlier than the Javadoc for CD, since it can
appeal to "I'm just a convenient supertype for String and four special
Number types." The time spent in the cognitive-friction zone is even
less. But, its not perfect; while now String only directly extends
NativeConstant, CD and Constable will still show up in the list of "all
implemented interfaces." This could still be confusing.
Its possible that the downside of (2) could be further mitigated by
Javadoc presentation changes (such as, in the "all implemented
interfaces" list, instead of saying "A, B, C", it could say "A, B (via
A), C", to make it clear what the inheritance path was. If this list
for String said "All implemented interfaces: Serializable,
Comparable<String>, CharSequence, NativeConstant, ConstantDesc (via
NativeConstant), Constable (via NativeConstant)", it would be clear that
the weirdnesses are introduced by NC, and a short trip to the NC docs
says "nothing to see here, move along." In other words, maybe this is
mostly a tooling problem. (Or maybe this just is creating new problems.)
We've been in (2) before and I'm fine to go there again; the thing to
most like about (2) is that it creates a type that explains the role of
these five special types at a higher level. (1) has the advantage of
reducing the number of weird types that show up, but only gets us
halfway back to blissful ignorance (there are only three numbers in
computer science; 0, 1, and n. We're in n now, this gets us back to 1,
hence, halfway.)
More information about the amber-dev
mailing list