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