> On Jan 24, 2021, at 1:16 PM, Brian Goetz <brian.go...@oracle.com> wrote:
>> 
>> 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?

Yes, exactly so.  (Well, I observed that the use-site syntax _could_ be the 
same whether the pattern is static or instance, but that there is a bit of a 
trade-off.  See below.)

> . . .
> Allow me to inject a confounding factor: static imports.

Well, sure.  With respect to the `max` method, the static import feature means 
I have to write “Math.” only once per compilation unit rather than once per 
use—but I still resent it.  :-/

> 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.

I think you must have meant to say `Optional x = of(y)`, yes?

> So surely, patterns would want the same consideration?

Certainly.

> 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?

It can, but only after after agreement that the semantics of the notation will 
indeed be extended to cover that case.

> 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?

Whee!

>> 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.

Okay, if you are not opposed to letting `Optional.of` in a pattern refer to 
either a static pattern or an instance pattern, depending on what was declared, 
then I agree that idea (c) is superior to (a) or (b).

>> 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 was hoping you would like it.  :-)

Reply via email to