<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body>
    <font size="4"><font face="monospace">We dropped this out of the
        record patterns JEP, but I think it is time to revisit this.  <br>
        <br>
        The concept of array patterns was pretty straightforward; they
        mimic the nesting and exhaustiveness rules of record patterns,
        they are just a different sort of container for nested
        patterns.  And they have an obvious duality with array creation
        expressions.  <br>
        <br>
        The main open question here was how we distinguish between
        "match an array of length exactly N" (where there are N nested
        patterns) and "match an array of length at least N".  We toyed
        with the idea of a "..." indicator to mean "more elements", but
        this felt a little forced and opened new questions.  <br>
        <br>
        It later occurred to me that there is another place to nest a
        pattern in an array pattern -- to match (and bind) the length. 
        In the following, assume for sake of exposition that "_" is the
        "any" pattern (matches everything, binds nothing) and that we
        have some way to denote a constant pattern, which I'll denote
        here with a constant literal.  <br>
        <br>
        There is an obvious place to put this (optional) pattern: in
        between the brackets.  So:<br>
        <br>
            case String[1] { P }:<br>
                        ^ a constant pattern<br>
        <br>
        would match string arrays of length 1 whose sole element matches
        P.  And<br>
      </font></font><br>
    <font size="4"><font face="monospace"><font size="4"><font face="monospace">    case String[] { P, Q }<br>
            <br>
            would match string arrays of length exactly 2, whose first
            two elements match P and Q respectively.  (If the length
            pattern is not specified, we infer a constant pattern whose
            constant is equal to the length of the nested pattern
            list.)  <br>
            <br>
            Matching a target to `String[L] { P0, .., Pn }` means<br>
            <br>
                x instanceof String[] arr<br>
                    && arr.length matches L<br>
                    && arr.length >= n<br>
                    && arr[0] matches P0<br>
                    && arr[1] matches P1<br>
                    ...<br>
                    && arr[n] matches Pn<br>
            <br>
            More examples:<br>
          </font></font><br>
            case String[int len] { P }<br>
        <br>
        would match string arrays of length >= 1 whose first element
        matches P, and further binds the array length to `len`.  <br>
        <br>
            case String[_] { P, Q } <br>
        <br>
        would match string arrays of any length whose first two elements
        match P and Q.  <br>
        <br>
            case String[3] { }<br>
                        ^constant pattern<br>
        <br>
        matches all string arrays of length 3.<br>
        <br>
        <br>
        This is a more principled way to do it, because the length is a
        part of the array and deserves a chance to match via nested
        patterns, just as with the elements, and it avoid trying to give
        "..." a new meaning.  <br>
        <br>
        The downside is that it might be confusing at first (though
        people will learn quickly enough) how to distinguish between an
        exact match and a prefix match.  <br>
        <br>
        <br>
        <br>
      </font></font><br>
    <div class="moz-cite-prefix">On 1/5/2021 1:48 PM, Brian Goetz wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:5f55e727-8a29-3bdf-57ec-6ec6245ef5c5@oracle.com">
      
      <font size="+1"><font face="monospace">As we get into the next
          round of pattern matching, I'd like to opportunistically
          attach another sub-feature: array patterns.  (This also bears
          on the question of "how would varargs patterns work", which
          I'll address below, though they might come later.)<br>
          <br>
          ## Array Patterns<br>
          <br>
          If we want to create a new array, we do so with an array
          construction expression:<br>
          <br>
              new String[] { "a", "b" }<br>
          <br>
          Since each form of aggregation should have its dual in
          destructuring, the natural way to represent an array pattern
          (h/t to AlanM for suggesting this) is:<br>
          <br>
              if (arr instanceof String[] { var a, var b }) { ... }<br>
          <br>
          Here, the applicability test is: "are you an instanceof of
          String[], with length = 2", and if so, we cast to String[],
          extract the two elements, and match them to the nested
          patterns `var a` and `var b`.   This is the natural analogue
          of deconstruction patterns for arrays, complete with nesting. 
          <br>
          <br>
          Since an array can have more elements, we likely need a way to
          say "length >= 2" rather than simply "length == 2".  There
          are multiple syntactic ways to get there, for now I'm going to
          write<br>
        </font></font><br>
      <font size="+1"><font face="monospace"><font size="+1"><font face="monospace">    if (arr instanceof String[] { var a,
              var b, ... }) <br>
              <br>
              to indicate "more".  The "..." matches zero or more
              elements and binds nothing.<br>
              <br>
              <digression><br>
            </font></font></font></font>People are immediately going to
      ask "can I bind something to the remainder"; I think this is
      mostly an "attractive distraction", and would prefer to not have
      this dominate the discussion.<br>
      <font size="+1"><font face="monospace"><font size="+1"><font face="monospace"><font size="+1"><font face="monospace"><font size="+1"><font face="monospace"></digression></font></font></font></font><br>
              <br>
              Here's an example from the JDK that could use this
              effectively:<br>
              <br>
              String[] limits = limitString.split(":");<br>
              try {<br>
                  switch (limits.length) {<br>
                      case 2: {<br>
                          if (!limits[1].equals("*"))<br>
                              setMultilineLimit(MultilineLimit.DEPTH,
              Integer.parseInt(limits[1]));<br>
                      }<br>
                      case 1: {<br>
                          if (!limits[0].equals("*"))<br>
                              setMultilineLimit(MultilineLimit.LENGTH,
              Integer.parseInt(limits[0]));<br>
                      }<br>
                  }<br>
              }<br>
              catch(NumberFormatException ex) {<br>
                  setMultilineLimit(MultilineLimit.DEPTH, -1);<br>
                  setMultilineLimit(MultilineLimit.LENGTH, -1);<br>
              }<br>
              <br>
              becomes (eventually)<br>
            </font></font></font></font><br>
      <font size="+1"><font face="monospace"><font size="+1"><font face="monospace"><font size="+1"><font face="monospace">   
                  switch (limitString.split(":")) {<br>
                          case String[] { var _, Integer.parseInt(var i)
                  } -> setMultilineLimit(DEPTH, i);<br>
                </font></font><font size="+1"><font face="monospace">   
                      case String[] { </font></font><font size="+1"><font face="monospace"><font size="+1"><font face="monospace">Integer.parseInt(var i)</font></font>
                  } -> setMultilineLimit(LENGTH, i);<br>
                          default -> { setMultilineLimit(DEPTH, -1);
                  setMultilineLimit(LENGTH, -1); }<br>
                      }<br>
                  <br>
                  Note how not only does this become more compact, but
                  the unchecked "NumberFormatException" is folded into
                  the match, rather than being a separate concern.<br>
                  <br>
                  <br>
                  ## Varargs patterns<br>
                  <br>
                  Having array patterns offers us a natural way to
                  interpret deconstruction patterns for varargs
                  records.  Assume we have:<br>
                  <br>
                      void m(X... xs) { }<br>
                  <br>
                  Then a varargs invocation<br>
                  <br>
                      m(a, b, c)<br>
                  <br>
                  is really sugar for<br>
                  <br>
                      m(new X[] { a, b, c })<br>
                  <br>
                  So the dual of a varargs invocation, a varargs match,
                  is really a match to an array pattern.  So for a
                  record<br>
                  <br>
                      record R(X... xs) { }<br>
                  <br>
                  a varargs match:<br>
                  <br>
                      case R(var a, var b, var c): <br>
                  <br>
                  is really sugar for an array match:<br>
                  <br>
                      case R(X[] { var a, var b, var c }): <br>
                  <br>
                  And similarly, we can use our "more arity" indicator:<br>
                  <br>
                      case R(var a, var b, var c, ...):<br>
                  <br>
                  to indicate that there are at least three elements.<br>
                  <br>
                  <br>
                </font></font></font></font></font></font> </blockquote>
    <br>
  </body>
</html>