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