----- Mail original ----- > De: "Brian Goetz" <brian.go...@oracle.com> > À: "Guy Steele" <guy.ste...@oracle.com> > Cc: "amber-spec-experts" <amber-spec-experts@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