Record pattern, the runtime side

Brian Goetz brian.goetz at oracle.com
Wed Mar 16 16:41:49 UTC 2022



> It works in 3 steps:
> Step 1, at compile time, the compiler takes all the patterns and creates a tree of pattern from the list of patterns,
> pattern that starts with the same prefix are merged together.

We can "normalize" a complex pattern into a sequence of simpler 
conditionals.  For example, matching the record pattern

     case Circle(Point(var x, var y), var r)

can be unrolled (and type inference applied) as

     x matches Circle(Point(var x, var y), var r)
     === x matches Circle(Point p, int r) && p matches Point(int x, int y)

Deconstruction patterns are known to have only an `instanceof` 
precondition; the deconstructor body won't ever fail (unlike more 
general static or instance patterns like Optional::of.)  So we can 
further rewrite this as:

     x matches Circle(Point(var x, var y), var r)
     === x matches Circle(Point p, int r) && p matches Point(int x, int y)
     === x instanceof Circle c && c.deconstruct(Point p, int r) && p 
instanceof Point && p.deconstruct(int x, int y)

(where the "deconstruct" term invokes the deconstructor and binds the 
relevant values.)

If we are disciplined about the order in which we unroll (e.g., always 
depth-first and always left-to-right), with a modest amount of 
normalization, your "same pattern prefix" turns into the simpler "common 
prefix of normalized operations".  Record deconstructors can be further 
normalized, because the can be replaced with calling the accessors:

     x matches Circle(Point(var x, var y), var r)
     === x matches Circle(Point p, int r) && p matches Point(int x, int y)
     === x instanceof Circle c && (Point p = c.center()) && (int r = 
c.radius()) && p instanceof Point
&& (int x = p.x()) && (int y = p.y())

Of course, this is all very implementation-centric; factoring matching 
this way is somewhat unusual, since the arity and order of side-effects 
might be surprising to Java developers.  (Yes, having side-effects in 
pattern definitions is bad, but it may still happen.)  So the spec will 
have to do some fast dancing to allow this.

> In the end, the tree of patterns is encoded in the bytecode as a tree of constant dynamic (each Pattern is created only from constant and patterns).

With record patterns, we don't even need pattern descriptions, because 
we can translate it all down to instanceof tests and invoking record 
component accessors.  Of course, that ends when we have deconstruction 
patterns, which correspond to imperative code; then having a Pattern 
instantiation, and a way to get to its matching / binding-extraction 
MHs, is needed.




More information about the amber-spec-experts mailing list