Array patterns (and varargs patterns)

Brian Goetz brian.goetz at oracle.com
Sat Sep 10 14:00:38 UTC 2022


Obvious correction: the `new` in the pattern examples was a cut and 
paste error, patterns don't say `new`.

On 9/10/2022 5:48 AM, Remi Forax wrote:
>
>
> ------------------------------------------------------------------------
>
>     *From: *"Brian Goetz" <brian.goetz at oracle.com>
>     *To: *"amber-spec-experts" <amber-spec-experts at openjdk.java.net>
>     *Sent: *Saturday, September 10, 2022 2:16:15 AM
>     *Subject: *Re: Array patterns (and varargs patterns)
>
>     John pulled a nice Jedi-mind-trick on me, and pointed out that we
>     actually have two creation expressions for arrays:
>
>         new Foo[n]
>         new Foo[] { a0, .., an }
>
>     and that if we are dualizing, then we should have these two patterns:
>
>         new Foo[] { P0, ..., Pn }  // matches arrays of exactly length N
>         new Foo[P]                 // matches arrays whose length match P
>
>     but that neither
>
>         new Foo[] { P, Q, ... }   // previous suggestion
>     nor
>         new Foo[L] { P, Q }       // current suggestion
>
>     correspond to either of those, which suggests that we may have
>     prematurely optimized the pattern form.  The rational consequence
>     of this observation is to do
>
>         new Foo[] { P0, ..., Pn }  // matches arrays of exactly length N
>
>     now (which is also the basis of varargs patterns), and once we
>     have constant patterns (which are kind of required for the second
>     form to be all that useful), come back for `Foo[P]`. 
>
>
> I like this proposal, it offers a clean separation between the array 
> pattern and a future spread pattern (or whatever when end up calling it).
>
> Rémi
>
>
>
>     On 9/6/2022 5:11 PM, Brian Goetz wrote:
>
>         We dropped this out of the record patterns JEP, but I think it
>         is time to revisit this.
>
>         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.
>
>         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.
>
>         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.
>
>         There is an obvious place to put this (optional) pattern: in
>         between the brackets.  So:
>
>             case String[1] { P }:
>                         ^ a constant pattern
>
>         would match string arrays of length 1 whose sole element
>         matches P.  And
>
>             case String[] { P, Q }
>
>         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.)
>
>         Matching a target to `String[L] { P0, .., Pn }` means
>
>             x instanceof String[] arr
>                 && arr.length matches L
>                 && arr.length >= n
>                 && arr[0] matches P0
>                 && arr[1] matches P1
>                 ...
>                 && arr[n] matches Pn
>
>         More examples:
>
>             case String[int len] { P }
>
>         would match string arrays of length >= 1 whose first element
>         matches P, and further binds the array length to `len`.
>
>             case String[_] { P, Q }
>
>         would match string arrays of any length whose first two
>         elements match P and Q.
>
>             case String[3] { }
>                         ^constant pattern
>
>         matches all string arrays of length 3.
>
>
>         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.
>
>         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.
>
>
>
>
>         On 1/5/2021 1:48 PM, Brian Goetz wrote:
>
>             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.org/pipermail/amber-spec-observers/attachments/20220910/bffa5451/attachment-0001.htm>


More information about the amber-spec-observers mailing list