Are templated string embedded expressions "method parameters" or "lambdas"?
John Rose
john.r.rose at oracle.com
Sat Oct 30 23:18:38 UTC 2021
On Oct 30, 2021, at 3:22 PM, John Rose <john.r.rose at oracle.com<mailto:john.r.rose at oracle.com>> wrote:
restrict the operand x (the receiver LHS of the S.T.)
to be a statically constant expression. If we do that,
then we can hand three things to a bootstrap method
that can do static validation:
1. the (constant) receiver
2. the string body (with holes marked)
3. maybe the static types of the arguments (this is very natural for indy)
Completing the design is pretty straightforward, but I might
as well write out more of my work. Here’s *one possible* design
in which the terminal “apply” operation is performed under
the name “MethodHandle.invokeExact”.
X."y…\{z…}" translates to an invokedynamic instruction
The static arguments to the indy instruction are X (formed
as a CONSTANT_Dynamic constant as necessary) and the
string body containing y with hole indicators.
Thus, the indy BSM gets the following:
1. a Lookup
2. a name (ignored)
3. a method-type (composed of the static types of z, returning R the expression type)
4. X (via condy)
5. "y…" where the holes are appropriately marked
It returns a CallSite, which is then used for all evaluations
of the expression. Applications will use a ConstantCallSite.
That is the mechanism. It does not say what is the logic of the BSM
or the type R. That is where the language rules come in.
The type of X must contain, directly or not, two or three methods,
validate, apply, asMethodHandle. The methods are declared as abstracts
using one or two API types. (Logically, they could also be left “hanging”
outside of any interface as the magic methods Brian detests.)
I will show one-interface and two-interface potential designs.
interface ST_A<R,E> { // 1 type with 3 methods
ST12 validate(Lookup, String, MethodType);
<R> apply(E…);
MethodHandle asMethodHandle();
}
interface ST_B<R,E> { // 2 types with 1 or 2 methods
Applier<R,E> validate(Lookup, String, MethodType);
interface Applier<R,E> {
<R> apply(Object… E);
MethodHandle asMethodHandle();
}
//default R validateAndApply(Lookup, String, MethodType) { … }
}
interface ST_C<R> { // 1 type with 2 methods, plus MH
MethodHandle validate(Lookup, String, MethodType);
R validateAndInvoke(Lookup, String, MethodType, Object...);
}
// “apply” here is MethodHandle::invokeExact; asMethodHandle is a nop
The language infers R as usual, as if the call were going through
apply (A), validate then apply (B) or validateAndInvoke (C).
But the BSM uses drives the same API points to obtain the
needed MethodHandle, which is then installed in a CCS.
Further variations: Have a static hook to produce, not a CCS
but a general CS such as a MCS. Drop the Lookup argument
from the APIs, because who wants that? You can add it later.
The oddity here, as in existing prototypes, is that there are
“two sets of books”, one for indy with its static bootstrap
logic that produces a method handle, and one “for the
police” which shows how all the types (including R) fit
together.
All of the above APIs allow implementations (subtypes) of the
interface to supply different calling sequences for the eventual
apply (or invoke). This is important, because a logger-oriented
Applier wants to accept delayed evaluation lambdas if possible,
while other simpler uses of the S.T. mechanism will be happy
to get along with just the Object arguments of apply(Object…).
One of the fine points of this design is whether and how
to statically type the *hole arguments* and whether the
static type of the receiver (x in x."…") can affect the
subsequent static typing of the hole arguments. With
a separate Applier type, the degrees of freedom in hole
type checking are, maybe, a little easier to manage,
but all of the API types above are malleable to some
degree. Ultimately, I think we will be pushed to allow
some amount of overloading on the “apply” method,
if use cases demand static checking of argument
lists. I’ve put in the “E” parameter above as a stop
gap to allow (at least) the necessary distinction between
Object and Supplier<Object> for distinct use cases.
If we ever do “Varargs 2.0” (better varargs, with
richer argument type patterns encoded into the VA
receiver), that will naturally add value to the above
APIs, if they can be retrofitted or replaced with VA2.0
APIs situated on apply.
That last one (ST_C) is nice and simple. Maybe that’s
a good one to start with, maybe sans Lookup. The others
can be layered on later on.
A final word: If you said “that’s a curried function” to
yourself at some point reading the above, you are not
wrong.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20211030/6f24c8c7/attachment-0001.htm>
More information about the amber-spec-experts
mailing list