Patterns: declaration

Remi Forax forax at univ-mlv.fr
Mon Jan 25 12:03:34 UTC 2021


----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Guy Steele" <guy.steele at oracle.com>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Dimanche 24 Janvier 2021 19:16:06
> Objet: Re: Patterns: declaration

>>
>> Hi, Brian,
>>
>> This exploration of the different ways of declaring patterns is very
>> useful, as
>> if the observation that for every sort of constructor or method, there
>> can be a
>> corresponding sort of pattern.
>>
>> I believe, however, that it then falls prey to a fallacy: I believe
>> that it is
>> not correct to conclude, just because every sort of constructor or
>> method has a
>> corresponding form of pattern, that for every sort of constructor or
>> method the
>> corresponding form of pattern should be used.
> 
> This is a great point; this is where language design meets library
> design, and while it may be sensible for the language to have all the
> options, perhaps the libraries need not follow the path of least
> resistance.  As a concrete example, let's take Optional.  We have static
> factories Optional.of(x) and Optional.empty(), which are the _only_ ways
> to construct Optionals from outside the capsule.  And clearly, we want
> to capture the conceptual and syntactic symmetry of:
> 
>     Optional<String> o = Optional.of(foo);
>     ...
>     if (o instanceof Optional.of(var foo)) { ... }
> 
> But, it eluded me that the second locution need not appeal to a static
> pattern, just because the factory is a static method.  If we define:
> 
>     final class Optional<T> {
>         static<T> Optional<T> of(T t) { ... }
>         pattern(T t) of() { ... }
>     }
> 
> then the same use-site syntax refers to the instance pattern here. And
> if the target is a broader type than Optional, then either would require
> the same synthetic target-applicability test (is the target an Optional.)
> 
> This is your main point, right?
> 
>> There is an inherent asymmetry between a static factory method and a
>> pattern:
>> the static method has no target, whereas a pattern (whether static or
>> instance)
>> _always_ has a target.
> 
> Yes, this is an unfortunate asymmetry, because it forces the language to
> give special meaning to a parameter.  (The proposed alternate syntax
> that Tagir and I discussed hide this somewhat, but its still there.)
> 
>> I have long felt that static methods in Java are a kind of
>> second-class kludge.
> 
> John frequently says: "static has messed up every job we've given it;
> don't give it more jobs to mess up."
> 
>> For the first example, this produces code that looks pretty good,
>> because it is
>> obviously necessary to indicate a type in addition to the method names:
>>
>> ```
>> switch (myObject) {
>>     case Optional.of(var t): ...
>>     case Optional.empty(): ...
>>     case OptEither.left(var t): ...
>>     case OptEither.right(var u): ...
>>     case OptEither.empty(): ...
>> }
>> ```
> 
> Allow me to inject a confounding factor: static imports.  We have static
> imports for methods, so you can static import Optional.of, and then say
> `Optional x = new of(y)` if you so desire.  So surely, patterns would
> want the same consideration?
> 
> But, this is a confounding factor, I think; it's a matter of specifying
> "pattern selection" carefully, and I believe that saying
> `Qualifier.name(stuff)` vs `name(stuff)` is mostly independent from
> static-ness.  The former could describe either an instance or static
> pattern (modulo same problems we already have with methods, if there are
> instance and static methods with the same name and descriptor), and the
> latter could either determine the qualifier from the static type of the
> switch operator, _or_ from the `import static` context. So either form
> of use could be either form of declaration.
> 
>> We can get this concision for the second example by declaring instance
>> patterns
>> rather than static patterns to complement the static factory methods.
>> Unfortunately, this makes the first example no longer work.
> 
> I think the first example can continue to work even if they are instance
> patterns?  We do a member selection for `Optional.of(...)`, discover
> there is an applicable pattern there, that it is an instance pattern on
> Optional<T>, do our target-applicability calculation, and we're off to
> the races?
> 
>> Idea (c) is to regard the meaning of a pattern of the form `T.P(...)` as
>> depending on whether the pattern P declared in class T is a static
>> pattern or an
>> instance pattern.  This allows us to use the originally proposed form
>> with dots:
>>
>> ```
>> switch (myObject) {
>>     case Optional.of(var t): ...
>>     case Optional.empty(): ...
>>     case OptEither.left(var t): ...
>>     case OptEither.right(var u): ...
>>     case OptEither.empty(): ...
>> }
>> ```
> 
> I think (c) is where we're aiming at now (though to be fair, it was only
> discussed in passing, I think in response to AlanM's recent mail.)
> 
>> at the expense of overloading the notation `T.P`, which some
>> programmers might
>> find disturbing.
> 
> We've already got an analogous overloading, which has actually turned
> out to be super-popular: method references.  Foo::bar is either (a) a
> method reference to the static method bar in Foo, or (b) a method
> reference to the _unbound_ instance method bar in Foo, in which case the
> receiver is added as an extra first parameter (eta abstraction).  So
> `String::length` is a method reference that is equivalent to the lambda
> `(String s) -> s.length()`.  Even though the Javadoc says the length
> method has no arguments, users don't seem fazed by this at all.  So,
> doubling down on this seems like a good move.
> 
>> Bottom line: Static patterns, like static methods, are clunky to use and
>> therefore instance patterns should be used wherever possible, even as
>> complements to static factory methods.
> 
> I am surprised to find that this works so well, but I can find no flaw
> in your argument!  (But no, Remi, I don't think this obviates the value
> of having static patterns in the language, it just means we might use
> them less often.)   Very slick.

I glad we are going in that direction, having a semantics closer to '::' than to '.'

About "users don't seem fazed by this at all", some of my students are.
Usually their IDEs propose to rewrite a lambda as a method reference and i have a question why Foo::bar can call an instance method,
despite the fact that I take the time to explain the 5 kinds of method references during the lecture :)

Rémi


More information about the amber-spec-experts mailing list