Array patterns (and varargs patterns)

Brian Goetz brian.goetz at oracle.com
Tue Jan 5 18:48:29 UTC 2021


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.)

## Array Patterns

If we want to create a new array, we do so with an array construction 
expression:

     new String[] { "a", "b" }

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:

     if (arr instanceof String[] { var a, var b }) { ... }

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.

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

     if (arr instanceof String[] { var a, var b, ... })

to indicate "more".  The "..." matches zero or more elements and binds 
nothing.

<digression>
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.
</digression>

Here's an example from the JDK that could use this effectively:

String[] limits = limitString.split(":");
try {
     switch (limits.length) {
         case 2: {
             if (!limits[1].equals("*"))
                 setMultilineLimit(MultilineLimit.DEPTH, 
Integer.parseInt(limits[1]));
         }
         case 1: {
             if (!limits[0].equals("*"))
                 setMultilineLimit(MultilineLimit.LENGTH, 
Integer.parseInt(limits[0]));
         }
     }
}
catch(NumberFormatException ex) {
     setMultilineLimit(MultilineLimit.DEPTH, -1);
     setMultilineLimit(MultilineLimit.LENGTH, -1);
}

becomes (eventually)

switch (limitString.split(":")) {
         case String[] { var _, Integer.parseInt(var i) } -> 
setMultilineLimit(DEPTH, i);
   case String[] { Integer.parseInt(var i) } -> 
setMultilineLimit(LENGTH, i);
         default -> { setMultilineLimit(DEPTH, -1); 
setMultilineLimit(LENGTH, -1); }
     }

Note how not only does this become more compact, but the unchecked 
"NumberFormatException" is folded into the match, rather than being a 
separate concern.


## Varargs patterns

Having array patterns offers us a natural way to interpret 
deconstruction patterns for varargs records. Assume we have:

     void m(X... xs) { }

Then a varargs invocation

     m(a, b, c)

is really sugar for

     m(new X[] { a, b, c })

So the dual of a varargs invocation, a varargs match, is really a match 
to an array pattern.  So for a record

     record R(X... xs) { }

a varargs match:

     case R(var a, var b, var c):

is really sugar for an array match:

     case R(X[] { var a, var b, var c }):

And similarly, we can use our "more arity" indicator:

     case R(var a, var b, var c, ...):

to indicate that there are at least three elements.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/amber-spec-experts/attachments/20210105/938c092e/attachment.htm>


More information about the amber-spec-experts mailing list