Towards member patterns

Brian Goetz brian.goetz at oracle.com
Wed Jan 24 14:55:28 UTC 2024


> Hello,
> I agree until the section 'Recovering static factory arguments', because at that point, it's the tail waging the dog.

I'm not sure which is tail and which is dog here, but it sure sounds 
like you are saying "the entire premise of this exercise is flawed, so 
we should throw all this work in the trash."  (After all, you only got 
two paragraphs into the motivation before declaring divergence.)  I hope 
you're not saying that, but let's try to be more clear about what you 
are saying?

> In this section you introduce a new syntax with little justification ("it makes sense") which should not a big problem if it was not your way to justify the introduction of pattern method being static, which are static only because the syntax you have chosen looks like static calls.

If you're talking about the use-site syntax, we've been using these 
examples for years, so it is a little surprising to hear an objection 
now.  But let's back up and go over the motivations for this broader 
effort.

Java offers many means for aggregation, both formal (constructors) and 
informal (factories, builders, etc), and may offer more in the future 
(e.g., collection literals).  Further, these mechanism of aggregation 
compose freely; I can say

     Optional<Shape> os = Optional.of(Shape.redBall(1));

without Foo and Optional having to coordinate in anyway.

Java offers no formal means for disagreggation, instead dumping it in 
the user's lap to write ad-hoc APIs such as getters.  To ask "does os 
contain a red ball of radius 1", we would currently have to:

     Shape s = os.orElse(null);
     boolean isRedUnitBall = s != null
                            && s.isBall()
                            && (s.color() == RED)
                            && s.size() == 1;
     if (isRedUnitBall) { ... }

The flaws here are many: I have to use a different API to take apart 
Optional as Shape, the two do not compose, and the take-apart code looks 
nothing like the put-together code.  This adds cognitive friction both 
when reading and writing, and is error-prone.

So, goals:

  - It should be as easy to take apart as to put together
  - It should be as easy to compose take-apart operations as it is to 
compose put-together operations
  - Taking apart should _look like_ putting together

I don't think you are really objecting to this last bullet, but it sure 
sounds like you are.

To address your question of "why do we like this use-site pattern syntax 
for static patterns", the answer has been stated over and over: taking 
apart should look like putting together, because taking apart asks the 
Pattern Question about a particular form of putting together.

     case Point(int x, int y):

asks "could this object have come from `new Point(x, y)` for some (x, 
y)."  But since not all classes expose constructors, we should be able 
to ask the same question about a corresponding factory form as well (and 
questions about de-constructor and questions about de-factory *must* 
compose with nesting.)  The composed:

     case Optional.of(Shape.redBall(1)):

asks the Pattern Question again: Could this object have come from 
`Optional.of(Shape.redBall(1))`.

> I think that choosing a syntax at that point is premature given the semantics is still in flux, especially if that syntax is used as a justification to say that pattern methods are static (*). At that point, it's a trap.

Whoa, did you *read* the document?  There's nothing inherently static 
about "pattern methods" (if we want to call them that), any more than 
there is anything inherently static about methods.  I think you should 
go back and read the whole document again, and if you still think that 
pattern methods are "static", let's address that conception directly 
before proceeding?
> In case of method pattern, it's an issue because it means that if you have a pattern method on an interface, an implementation cannot override it.

Incorrect (and I think this should have been clear both from this 
document and from the earlier "Patterns in the Object Model" document.  
Just as an interface can declare both static and instance methods, it 
can have both static and instance patterns.  Instance patterns are 
overridable, can be abstract, etc.

> Here is an example using the syntax you propose, let say I want to do the inverse of Map.entry(), if write an inverse pattern method inside Map then the only way write it is to use the getters getKey() and getValue().

We are getting far ahead of ourselves, but if you wanted this behavior 
to be overridable, but still added retroactively, you could write a 
default pattern.  But given how many layers of mis-assumption we have 
here, let's prune this particular branch of discussion until it is clear 
you understand what is being proposed.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240124/00806707/attachment.htm>


More information about the amber-spec-experts mailing list