----- Original Message -----
> From: "Brian Goetz" <[email protected]>
> To: "Remi Forax" <[email protected]>
> Cc: "Tagir Valeev" <[email protected]>, "amber-spec-experts" 
> <[email protected]>
> Sent: Thursday, September 8, 2022 12:15:04 AM
> Subject: Re: Array patterns (and varargs patterns)

>> For me, Arrays.of() is a named pattern with a vararg list of bindings, no ?
> 
> Its a named pattern, but to work, it would need varargs patterns -- and
> array patterns are the underpinnings of varargs, just as array creation
> is the underpinning of varargs invocation.  We're not going to do
> varargs patterns differently than we do varargs invocation, just to
> avoid doing array patterns -- that would be silly.

Here we want to extract the value into bindings/variables, that is not what the 
varargs does, the varargs  takes a bunch of value on stack and put them into an 
array.
Here we want the opposite operation of a varargs, the spread (or splat) 
operator that takes the argument from an array (or a collection ?) and put them 
on the stack. 

If we have the pattern method Arrays.of()

static <T> pattern (T...) of(T[] array) {  // here it's a varargs
  ...
}

and we call it using a named pattern
  switch(array) {
    case Arrays.of(/* insert a syntax here */) -> ...

the syntax should extract some/all values of the array into one or several 
bindings.

If we are in Caml, we have the :: operator to separate the first element from 
the rest
  switch(array) {
    case Arrays.of(String first :: String[] rest) -> ...

If we are in JavaScript, we have the spread operator (notice that the ... is 
before the type)
  switch(array) {
    case Arrays.of(String first, ... String[] rest) -> ...

So the varargs is at the declaration side, at the pattern side we need a new 
operator spread, so i think that adding an array pattern now is not a good idea.

regards,
Rémi

>>
>>>> With best regards,
>>>> Tagir Valeev.
>>>>
>>>> On Tue, Sep 6, 2022 at 11:11 PM Brian Goetz <[email protected]> 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.
>>>>>

Reply via email to