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