> On Aug 12, 2026, at 6:17 AM, Johan Sjolen <[email protected]> wrote:
> 
> The problem comes up because of the extreme parametricity of this code along 
> with the megamorphic nature of the lambda. Let's look at it.
> 
> We have:
> 
> public interface Parser<Context, Problem, Value> extends Function1<
>        State<Context>,
>        PStep<Context, Problem, Value>
> 
> public interface Function1<T1, R> extends Serializable, Function<T1, R> {
>  R apply(T1 var1);
> }

For context, this is a good example of one of the limitations described in the 
JEP:

"First, flattening and scalarization require that a variable only stores 
references to instances of a specific value class; e.g., the date field of a 
LocalDateTime object always stores a LocalDate reference. Flattening and 
scalarization typically cannot be applied to a variable declared with a 
supertype of a value class, such as Object.”

"The same principles apply to method parameters: A parameter of type LocalDate 
is reliably scalarizable, while a parameter of type Object or T is not. (If the 
method call can be inlined, however, a JIT may be able to skip the assignment 
and heap allocation completely.)”

Unfortunately, it’s not always easy to locate all of these polymorphic 
variables.

And also unfortunately, value object performance is not going to be *strictly 
better* in all use cases: there are tradeoffs, where certain lower-priority 
areas of the performance profile have to be degraded in order to enable the big 
performance gains we want to see in higher-priority areas. Which is not to say 
there’s nothing that can be done in the platform implementation—more 
engineering work can often minimize the losses while preserving the gains—but I 
expect there will always be some trade-offs.

> We can help C2 out, by making our apply method carry some more type info, by 
> giving Parser this override:
> 
>    @Override
>    PStep<Context, Problem, Value> apply(State<Context> state);

I guess there are two “best practice” lessons here:

- If your program has a bunch of lambdas that operate on the same value class 
types, declare a specialized interface rather than relying on generic 
interfaces like `Function`, `Predicate`, etc.

- When an abstract class or interface has a generic supertype, parameterized 
with a value class type argument, it’s helpful to redeclare any abstract method 
signatures that the type argument appears in.

(With the caveat that many simple use cases will just inline and these extra 
efforts won’t have any impact. But these are simple changes that can help to 
avoid depending on the whims of inlining.)

Reply via email to