New pattern matching doc

forax at univ-mlv.fr forax at univ-mlv.fr
Tue Jan 12 16:44:30 UTC 2021


----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Lundi 11 Janvier 2021 22:37:34
> Objet: Re: New pattern matching doc

>> To summarize, this is what i'm proposing,
>> using the same notation as Alan, we have two syntax
>> IDENTIFIER(BINDINGS), the unqualified pattern, and
>> Qualifier.IDENTIFIER(BINDINGS), the qualified pattern.
> 
> Your previous mails seemed to focus on the declaration site, but now I
> think you're talking about how we connect the use site to a
> declaration.  That's an area which the document didn't fully cover, so
> good, let's talk about that.

Ok,

> 
> Positing that there are three "kinds" of patterns -- deconstruction,
> static, and instance -- we need to map the use-site description of a
> pattern to a selected pattern declaration, not unlike overload
> selection.  The doc didn't provide rules for that, but its a fair
> question.  Let's do some examples.
> 
>     X x = ...
> 
>     switch (x) {
>         case Foo(var x):    // probably a deconstruction pattern
>         case foo(var x):     // probably an instance pattern in X *
>         case X.foo(var x):  // either an instance or static pattern on X
>     }
> 
> Obviously, we can't take capitalization into effect, so we'll need some
> priority order for disambiguation.  (If we allow dispatch to local
> patterns, or static import of patterns, these will fall into the second
> bucket too.)  And in all cases, we will have to be prepared to do some
> overload selection on patterns, since there might be multiple with the
> same name.
> 
> If this is what you are proposing, we are already on the same page
> (modulo patterns that have explicit input arguments -- some syntax is
> needed for that at the use site, but I've deliberately not gone there
> yet, because we're still nailing down the model conceptually.)

yes,
do you have a use case for a pattern with input arguments ?

> 
>> For the qualified pattern, Qualifier.IDENTIFIER(BINDINGS) references
>> - either an instance method inside the class Qualifier, with 'this'
>> being the value switched upon
>> - or a static method inside the class Qualifier, the first parameter
>> containing the value switched upon
> 
> If by "instance method" or "static method", you mean "instance pattern"
> or "static pattern", then you've got this exactly right.
> 
>> For the unqualified pattern, IDENTIFIER(BINDINGS), it is equivalent to
>> - AnotherType(BINDINGS) if the identifier is the type AnotherType
>> (it's the deconstruction pattern)
>> - either Type.IDENTIFIER(BINDINGS) and the rules above applied (it's
>> the inference rule of Tagir)
>> - or AnotherType.IDENTIFIER(BINDINGS) if there is an import static
>> AnotherType.IDENTIFIER
> 
> Right, this is the universe of choices, we may want to prune the list if
> we decide that (say) static import of patterns isn't helpful.

yes,

> 
> So I think you have correctly intuited the mapping from use-site
> [Qualifier.]PatternName(BINDINGS) that I had in mind.
> 
> Now, a few words about overload selection.  We have to declare what it
> means for a pattern to be applicable to a "pattern invocation". This
> includes:
> 
>  - compatibility of the target
>  - compatibility of the input arguments
>  - compatibility of the bindings

and maybe the compatibility between partial and total pattern method

And we also have to think in term of overriding (if it's an instance pattern method).

> 
> For the target, this was outlined in the "Pattern Matching - Semantics"
> document.  For the input arguments, this will be lifted straight from
> overload selection.  For the bindings, these must agree in arity (unless
> it's a varargs pattern), and the type at the use site must be compatible
> with the declared type.  (Though most of the time, the type at the use
> site will be `var`, so I expect in practice, we'll mostly see
> overloading on arity.)  I suspect further that we'll borrow similar
> treatment of varargs from overload selection (prefer non-varargs to
> varargs).

so on a simple example, if the bindings are only types (here, in between brackets)

with this declaration
  class Foos {
    static pattern [int, CharSequence] bar() { ... }
  }

and this pattern matching
  switch(o) {
    case Foos.bar(var a, var b) -> ...
  }

as you said given that a and b are declared as var, the compiler will only checks the arity of the bindings and infers that the type of a is int and the type of b is CharSequence.

If there is overriding between instance pattern methods, we may allow
- the type of the binding to be covariant (like the return type of an instance method)
- more bindings in the overridden method than in the base method

By example,
  class A {
    pattern [int, CharSequence] bar() { ... } 
  }
  class B extends A {
    pattern [int, String, double] bar() { ... } 
  }

We also talk about requiring names when declaring the bindings
  class Foos {
    static pattern [length: int, text: CharSequence] bar() { ... }
  }
but given that the matching between the binding is positional, technically we don't need names but we may still keep them
- for code generation purpose (if the type of the bindings is a record generated by the compiler)
- if we want to allow to permute bindings at declaration site without recompiling (i know that you don't like that strategy).

regards,
Rémi


More information about the amber-spec-experts mailing list