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.


Reply via email to