<html>
  <head>

    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <font size="+1"><tt>There are a lot of directions we could take next
        for pattern matching.  The one that builds most on what we've
        already done, and offers significant incremental expressiveness,
        is extending the type patterns we already have to a new context:
        switch.  (There is still plenty of work to do on deconstruction
        patterns, pattern assignment, etc, but these require more design
        work.)  <br>
        <br>
        Here's an overview of where I think we are here.  <br>
        <br>
        [JEP 305][jep305] introduced the first phase of [pattern
        matching][patternmatch]<br>
        into the Java language.  It was deliberately limited, focusing
        on only one kind<br>
        of pattern (type test patterns) and one linguistic context
        (`instanceof`).<br>
        Having introduced the concept to Java developers, we can now
        extend both the<br>
        kinds of patterns and the linguistic context where patterns are
        used.<br>
        <br>
        ## Patterns in switch<br>
        <br>
        The obvious next context in which to introduce pattern matching
        is `switch`;  a<br>
        switch using patterns as `case` labels can replace `if .. else
        if` chains with<br>
        a more direct way of expressing a multi-way conditional.  <br>
        <br>
        Unfortunately, `switch` is one of the most complex, irregular
        constructs we have<br>
        in Java, so we must teach it some new tricks while avoiding some
        existing traps.<br>
        Such tricks and traps may include:<br>
        <br>
        **Typing.**  Currently, the operand of a `switch` may only be
        one of the<br>
        integral primitive types, the box type of an integral primitive,
        `String`, or an<br>
        `enum` type.  (Further, if the `switch` operand is an `enum`
        type, the `case`<br>
        labels must be _unqualified_ enum constant names.)  Clearly we
        can relax this<br>
        restriction to allow other types, and constrain the case labels
        to only be<br>
        patterns that are applicable to that type, but it may leave a
        seam of "legacy"<br>
        vs "pattern" switch, especially if we do not adopt bare constant
        literals as<br>
        the denotation of constant patterns.  (We have confronted this
        issue before with<br>
        expression switch, and concluded that it was better to
        rehabilitate the `switch`<br>
        we have rather than create a new construct, and we will make the
        same choice<br>
        here, but the cost of this is often a visible seam.)<br>
        <br>
        **Parsing.**  The grammar currently specifies that the operand
        of a `case` label<br>
        is a `CaseConstant`, which casts a wide syntactic net, later
        narrowed with<br>
        post-checks after attribution.  This means that, since parsing
        is done before we<br>
        know the type of the operand, we must be watchful for
        ambiguities between<br>
        patterns and expressions (and possibly refine the production for
        `case` labels.)<br>
        <br>
        **Nullity.**  The `switch` construct is currently hostile to
        `null`, but some<br>
        patterns do match `null`, and it may be desirable if nulls can
        be handled<br>
        within a suitably crafted `switch`.  <br>
        <br>
        **Exhaustiveness.**  For switches over the permitted subtypes of
        sealed types,<br>
        we will want to be able to do exhaustiveness analysis --
        including for nested<br>
        patterns (i.e., if `Shape`  is `Circle` or `Rect`, then
        `Box(Circle c)` and<br>
        `Box(Rect r)` are exhaustive on `Box<Shape>`.)<br>
        <br>
        **Fallthrough.**  Fallthrough is everyone's least favorite
        feature of `switch`,<br>
        but it exists for a reason.  (The mistake was making fallthrough
        the default<br>
        behavior, but that ship has sailed.)  In the absence of an OR
        pattern<br>
        combinator, one might find fallthrough in switch useful in
        conjunction with<br>
        patterns:<br>
        <br>
        ```<br>
        case Box(int x):<br>
        case Bag(int x):<br>
            // use x<br>
        ```<br>
        <br>
        However, it is likely that we will, at least initially, disallow
        falling out<br>
        of, or into, a case label with binding variables.<br>
        <br>
        #### Translation<br>
        <br>
        Switches on primitives and their wrapper types are translated
        using the<br>
        `tableswitch` or `lookupswitch` bytecodes; switches on strings
        and enums are<br>
        lowered in the compiler to switches involving hash codes (for
        strings) or<br>
        ordinals (for enums.)<br>
        <br>
        For switches on patterns, we would need a new strategy, one
        likely built on<br>
        `invokedynamic`, where we lower the cases to a densely numbered
        `int` switch,<br>
        and then invoke a classifier function with the operand which
        tells us the first<br>
        case number it matches.  So a switch like:<br>
        <br>
        ```<br>
        switch (o) {<br>
            case P: A<br>
            case Q: B<br>
        }<br>
        ```<br>
        <br>
        is lowered to:<br>
        <br>
        ```<br>
        int target = indy[BSM=PatternSwitch, args=[P,Q]](o)<br>
        switch (target) {<br>
            case 0: A<br>
            case 1: B<br>
        }<br>
        ```<br>
        <br>
        A symbolic description of the patterns is provided as the
        bootstrap argument<br>
        list, which builds a decision tree based on analysis of the
        patterns and their<br>
        target types.<br>
        <br>
        #### Guards<br>
        <br>
        No matter how rich our patterns are, it is often the case that
        we will want<br>
        to provide additional filtering on the results of a pattern:<br>
        <br>
        ```<br>
        if (shape instanceof Cylinder c && c.color() == RED) {
        ... }<br>
        ```<br>
        <br>
        Because we use `instanceof` as part of a boolean expression, it
        is easy to<br>
        narrow the results by conjoining additional checks with
        `&&`.  But in a `case`<br>
        label, we do not necessarily have this opportunity.  Worse, the
        semantics of<br>
        `switch` mean that once a `case` label is selected, there is no
        way to say<br>
        "oops, forget it, keep trying from the next label".  <br>
        <br>
        It is common in languages with pattern matching to support some
        form of "guard"<br>
        expression, which is a boolean expression that conditions
        whether the case<br>
        matches, such as:<br>
        <br>
        ```<br>
        case Point(var x, var y)<br>
            __where x == y: ...<br>
        ```<br>
        <br>
        Bindings from the pattern would have to be available in guard
        expressions.<br>
        <br>
        Syntactic options (and hazards) for guards abound; users would
        probably find it<br>
        natural to reuse `&&` to attach guards to patterns; C#
        has chosen `when` for<br>
        introducing guards; we could use `case P if (e)`, etc.  Whatever
        we do here,<br>
        there is a readability risk,  as the more complex guards are,
        the harder it is<br>
        to tell where the case label ends and the "body" begins.  (And
        worse if we allow<br>
        switch expressions inside guards.)<br>
        <br>
        An alternate to guards is to allow an imperative `continue`
        statement in<br>
        `switch`, which would mean "keep trying to match from the next
        label."  Given<br>
        the existing semantics of `continue`, this is a natural
        extension, but since<br>
        `continue` does not currently have meaning for switch, some work
        would have to<br>
        be done to disambiguate continue statements in switches enclosed
        in loops.  The<br>
        imperative version is strictly more expressive than most
        reasonable forms of the<br>
        declarative version, but users are likely to prefer the
        declarative version.<br>
        <br>
        ## Nulls<br>
        <br>
        Almost no language design exercise is complete without some
        degree of wrestling<br>
        with `null`.  As we define more complex patterns than simple
        type patterns, and<br>
        extend constructs such as `switch` (which have existing opinions
        about nullity)<br>
        to support patterns, we need to have a clear understanding of
        which patterns<br>
        are nullable, and separate the nullity behaviors of patterns
        from the nullity<br>
        behaviors of those constructs which use patterns.  <br>
        <br>
        ## Nullity and patterns<br>
        <br>
        This topic has a number of easily-tangled concerns:<br>
        <br>
         - **Construct nullability.**  Constructs to which we want to
        add pattern<br>
           awareness (`instanceof`, `switch`) already have their own
        opinion about<br>
           nulls.  Currently, `instanceof` always says false when
        presented with a<br>
           `null`, and `switch` always NPEs.  We may, or may not, wish
        to refine these<br>
           rules in some cases.<br>
         - **Pattern nullability.**  Some patterns clearly would never
        match `null`<br>
           (such as deconstruction patterns), whereas others (an "any"
        pattern, and<br>
           surely the `null` constant pattern) might make sense to match
        null.<br>
         - **Refactoring friendliness.**  There are a number of cases
        that we would like<br>
           to freely refactor back and forth, such as certain chains of
        `if ... else if`<br>
           with switches.<br>
         - **Nesting vs top-level.**  The "obvious" thing to do at the
        top level of a<br>
           construct is not always the "obvious" thing to do in a nested
        construct.<br>
         - **Totality vs partiality.**  When a pattern is partial on the
        operand type<br>
           (e.g., `case String` when the operand of switch is `Object`),
        it is almost<br>
           never the case we want to match null (except in the case of
        the `null`<br>
           constant pattern), whereas when a pattern is total on the
        operand type (e.g.,<br>
           `case Object` in the same example), it is more justifiable to
        match null.<br>
         - **Inference.**  It would be nice if a `var` pattern were
        simply inference for<br>
           a type pattern, rather than some possibly-non-denotable
        union.<br>
        <br>
        As a starting example, consider:<br>
        <br>
        ```<br>
        record Box(Object o) { }<br>
        <br>
        Box box = ...<br>
        switch (box) {<br>
            case Box(Chocolate c):<br>
            case Box(Frog f):<br>
            case Box(var o):<br>
        }<br>
        ```<br>
        <br>
        It would be highly confusing and error-prone for either of the
        first two<br>
        patterns to match `Box(null)` -- given that `Chocolate` and
        `Frog` have no type<br>
        relation, it should be perfectly safe to reorder the two.  But,
        because the last<br>
        pattern seems so obviously total on boxes, it is quite likely
        that what the<br>
        author wants is to match all remaining boxes, including those
        that contain null.<br>
        (Further, it would be terrible if there were _no_ way to say
        "Match any `Box`,<br>
        even if it contains `null`.  (While one might initially think
        this could be<br>
        repaired with OR patterns, imagine that `Box` had _n_ components
        -- we'd need to<br>
        OR together _2^n_ patterns, with complex merging, to express all
        the possible<br>
        combinations of nullity.))<br>
        <br>
        Scala and C# took the approach of saying that "var" patterns are
        not just type<br>
        inference, they are "any" patterns -- so `Box(Object o)` matches
        boxes<br>
        containing a non-null payload, where `Box(var o)` matches all
        boxes.  This<br>
        means, unfortunately, that `var` is not mere type inference --
        which complicates<br>
        the role of `var` in the language considerably.  Users should
        not have to choose<br>
        between the semantics they want and being explicit about types;
        these should be<br>
        orthogonal choices.  The above `switch` should be equivalent to:<br>
        <br>
        ```<br>
        Box box = ...<br>
        switch (box) {<br>
            case Box(Chocolate c):<br>
            case Box(Frog f):<br>
            case Box(Object o):<br>
        }<br>
        ```<br>
        <br>
        and the choice to use `Object` or `var` should be solely one of
        whether the<br>
        manifest types are deemed to improve or impair readability.<br>
        <br>
        #### Construct and pattern nullability<br>
        <br>
        Currently, `instanceof` always says `false` on `null`, and
        `switch` always<br>
        throws on `null`.  Whatever null opinions a construct has, these
        are applied<br>
        before we even test any patterns.  <br>
        <br>
        We can formalize the intuition outlined above as: type patterns
        that are _total_<br>
        on their target operand (`var x`, and `T t` on an operand of
        type `U`, where `U<br>
        <: T`) match null, and non-total type patterns do not. 
        (Another way to say<br>
        this is: a `var` pattern is the "any" pattern, and a type
        pattern that is  total<br>
        on its operand type is also an "any" pattern.)  Additionally,
        the `null`<br>
        constant pattern matches null.  These are the _only_ nullable
        patterns.<br>
        <br>
        In our `Box` example, this means that the last case (whether
        written as `Box(var<br>
        o)` or `Box(Object o)`) matches all boxes, including those
        containing null<br>
        (because the nested pattern is total on the nested operand), but
        the first two<br>
        cases do not.<br>
        <br>
        If we retain the current absolute hostility of `switch` to
        nulls, we can't<br>
        trivially refactor from<br>
        <br>
        ```<br>
        switch (o) {<br>
            case Box(Chocolate c):<br>
            case Box(Frog f):<br>
            case Box(var o):<br>
        }<br>
        ```<br>
        to<br>
        <br>
        ```<br>
        switch (o) {<br>
            case Box(var contents):<br>
                switch (contents) {<br>
                    case Chocolate c:<br>
                    case Frog f:<br>
                    case Object o:<br>
                }<br>
            }<br>
        }<br>
        ```<br>
        <br>
        because the inner `switch(contents)` would NPE before we tried
        to match any of<br>
        the patterns it contains.  Instead, the user would explicitly
        have to do an `if<br>
        (contents == null)` test, and, if the intent was to handle
        `null` in the same<br>
        way as the `Object o` case, some duplication of code would be
        needed.  We can<br>
        address this sharp corner by slightly relaxing the
        null-hostility of `switch`,<br>
        as described below.<br>
        <br>
        A similar sharp corner is the decomposition of a nested pattern
        `P(Q)` into<br>
        `P(alpha) & alpha instanceof Q`; while this is intended to
        be a universally<br>
        valid transformation, if P's 1st component might be null and Q
        is total,  this<br>
        transformation would not be valid because of the existing (mild)
        null-hostility<br>
        of `instanceof`.  Again, we may be able to address this by
        adjusting the rules<br>
        surrounding `instanceof` slightly.<br>
        <br>
        ## Generalizing switch<br>
        <br>
        The refactoring example above motivates why we might want to
        relax the<br>
        null-handling behavior of `switch`.  On the other hand, the one
        thing the<br>
        current behavior has going for it is that at least the current
        behavior is easy<br>
        to reason about; it always throws when confronted with a
        `null`.  Any relaxed<br>
        behavior would be more complex; some switches would still have
        to throw (for<br>
        compatibility with existing semantics), and some (which can't be
        expressed<br>
        today) would accept nulls.  This is a tricky balance to achieve,
        but I think we<br>
        have a found a good one.  <br>
        <br>
        A starting point is that we don't want to require readers to do
        an _O(n)_<br>
        analysis of each of the `case` labels just to determine whether
        a given switch<br>
        accepts `null` or not; this should be an _O(1)_ analysis.  (We
        do not want to<br>
        introduce a new flavor of `switch`, such as `switch-nullable`;
        this might seem<br>
        to fix the proximate problem but would surely create others.  As
        we've done with<br>
        expression switch and patterns, we'd rather rehabilitate
        `switch` than create<br>
        an almost-but-not-quite-the-same variant.)<br>
        <br>
        Let's start with the null pattern, which we'll spell for sake of
        exposition<br>
        `case null`.  What if you were allowed to say `case null` in a
        switch, and the<br>
        switch would do the obvious thing?<br>
        <br>
        ```<br>
        switch (o) {<br>
            case null -> System.out.println("Ugh, null");<br>
            case String s -> System.out.println("Yay, non-null: " +
        s);<br>
        }<br>
        ```<br>
        <br>
        Given that the `case null` appears so close to the `switch`, it
        does not seem<br>
        confusing that this switch would match `null`; the existence of
        `case null` at<br>
        the top of the switch makes it pretty clear that this is
        intended behavior.  (We<br>
        could further restrict the null pattern to being the first
        pattern in a switch,<br>
        to make this clearer.)<br>
        <br>
        Now, let's look at the other end of the switch -- the last
        case.  What if the<br>
        last pattern is a total pattern?  (Note that if any `case` has a
        total pattern,<br>
        it _must_ be the last one, otherwise the cases after that would
        be dead, which<br>
        would be an error.)  Is it also reasonable for that to match
        null?  After all,<br>
        we're saying "everything":<br>
        <br>
        ```<br>
        switch (o) {<br>
            case String s: ...<br>
            case Object o: ...<br>
        }<br>
        ```<br>
        <br>
        Under this interpretation, the switch-refactoring anomaly above
        goes away.<br>
        <br>
        The direction we're going here is that if we can localize the
        null-acceptance of<br>
        switches in the first (is it `case null`?) and last (is it
        total?) cases, then<br>
        the incremental complexity of allowing _some_ switches to accept
        null might be<br>
        outweighed by the incremental benefit of treating `null` more
        uniformly (and<br>
        thus eliminating the refactoring anomalies.)  Note also that
        there is no actual<br>
        code compatibility issue; this is all mental-model
        compatibility.<br>
        <br>
        So far, we're suggesting:<br>
        <br>
         - A switch with a constant `null` case  will accept nulls;<br>
         - If present, a constant `null` case must go first;<br>
         - A switch with a total (any) case matches also accepts nulls;<br>
         - If present, a total (any) case must go last.<br>
        <br>
        #### Relocating the problem<br>
        <br>
        It might be more helpful to view these changes as not changing
        the behavior of<br>
        `switch`, but of the `default` case of `switch`.  We can equally
        well interpret<br>
        the current behavior as:<br>
        <br>
         - `switch` always accepts `null`, but matching the `default`
        case of a `switch`<br>
           throws `NullPointerException`;<br>
         - any `switch` without a `default` case has an implicit
        do-nothing `default`<br>
           case.<br>
        <br>
        If we adopt this change of perspective, then `default`, not
        `switch`, is in<br>
        control of the null rejection behavior -- and we can view these
        changes as<br>
        adjusting the behavior of `default`.  So we can recast the
        proposed changes as:<br>
        <br>
          - Switches accept null;<br>
          - A constant `null` case will match nulls, and must go first;<br>
          - A total switch (a switch with a total `case`) cannot have a
        `default` case;<br>
          - A non-total switch without a `default` case gets an implicit
        do-nothing<br>
            `default` case;<br>
          - Matching the (implicit or explicit) default case with a
        `null` operand<br>
            always throws NPE.<br>
        <br>
        The main casualty here is that the `default` case does not mean
        the same<br>
        thing as `case var x` or `case Object o`.  We can't deprecate
        `default`, but<br>
        for pattern switches, it becomes much less useful.  <br>
        <br>
        #### What about method (declared) patterns?<br>
        <br>
        So far, we've declared all patterns, except the `null` constant
        pattern and the<br>
        total (any) pattern, to not match `null`.  What about patterns
        that are<br>
        explicitly declared in code?  It turns out we can rule out these
        matching<br>
        `null` fairly easily.  <br>
        <br>
        We can divide declared patterns into three kinds: deconstruction
        patterns (dual<br>
        to constructors), static patterns (dual to static methods), and
        instance<br>
        patterns (dual to instance methods.)  For both deconstruction
        and instance<br>
        patterns, the match target becomes the receiver; method bodies
        are never<br>
        expected to deal with the case where `this == null`.  <br>
        <br>
        For static patterns, it is conceivable that they could match
        `null`, but this<br>
        would put a fairly serious burden on writers of static patterns
        to check for<br>
        `null` -- which they would invariably forget, and many more NPEs
        would ensue.<br>
        (Think about writing the pattern for `Optional.of(T t)` -- it
        would be<br>
        overwhelmingly likely we'd forget to check the target for
        nullity.)  SO there<br>
        is a strong argument to simply say "declared patterns never
        match null", to<br>
        not put writers of such patterns in this situation.<br>
        <br>
        So, only the top and bottom patterns in a switch could match
        null; if the top<br>
        pattern is not `case null`, and the bottom pattern is not total,
        then the switch<br>
        throws NPE on null, otherwise it accepts null.<br>
        <br>
        #### Adjusting instanceof<br>
        <br>
        The remaining anomaly we had was about unrolling nested patterns
        when the inner<br>
        pattern is total.  We can plug this by simply outlawing total
        patterns in<br>
        `instanceof`.<br>
        <br>
        This may seem like a cheap trick, but it makes sense on its
        own.  If the<br>
        following statement was allowed:<br>
        <br>
        ```<br>
        if (e instanceof var x) { X }<br>
        ```<br>
        <br>
        it would simply be confusing; on the one hand, it looks like it
        should always<br>
        match, but on the other, `instanceof` is historically
        null-hostile.  And, if the<br>
        pattern always matches, then the `if` statement is silly; it
        should be replaced<br>
        with:<br>
        <br>
        ```<br>
        var x = e;<br>
        X<br>
        ```<br>
        <br>
        since there's nothing conditional about it.  So by banning "any"
        patterns on the<br>
        RHS of `instanceof`, we both avoid a confusion about what is
        going to happen,<br>
        and we prevent the unrolling anomaly.<br>
        <br>
        For reasons of compatibility, we will have to continue to allow<br>
        <br>
        ```<br>
        if (e instanceof Object) { ... }   <br>
        ```<br>
        <br>
        which succeeds on all non-null operands.  <br>
        <br>
        We've been a little sloppy with the terminology of "any" vs
        "total"; note that<br>
        in<br>
        <br>
        ```<br>
        Point p;<br>
        if (p instanceof Point(var x, var y)) { }<br>
        ```<br>
        <br>
        the pattern `Point(var x, var y)` is total on `Point`, but not
        an "any" pattern<br>
        -- it still doesn't match on p == null.<br>
        <br>
        On the theory that an "any" pattern in `instanceof` is silly, we
        may also want<br>
        to ban other "silly" patterns in `instanceof`, such as constant
        patterns, since<br>
        all of the following have simpler forms:<br>
        <br>
        ```<br>
        if (x instanceof null) { ... }<br>
        if (x instanceof "") { ... }<br>
        if (i instanceof 3) { ... }<br>
        ```<br>
        <br>
        In the first round (type patterns in `instanceof`), we mostly
        didn't confront<br>
        this issue, saying that `instanceof T t` matched in all the
        cases where<br>
        `instanceof T` would match.  But given that the solution for
        `switch` relies<br>
        on "any" patterns matching null, we may wish to adjust the
        behavior of<br>
        `instanceof` before it exits preview.<br>
        <br>
        <br>
        [jep305]: <a class="moz-txt-link-freetext" href="https://openjdk.java.net/jeps/305">https://openjdk.java.net/jeps/305</a><br>
        [patternmatch]: pattern-match.html<br>
        <br>
      </tt></font>
  </body>
</html>