Member Patterns -- the bikeshed

forax at univ-mlv.fr forax at univ-mlv.fr
Thu Apr 4 13:59:00 UTC 2024


> From: "Brian Goetz" <brian.goetz at oracle.com>
> To: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Sent: Wednesday, April 3, 2024 5:00:55 PM
> Subject: Re: Member Patterns -- the bikeshed

> Despite several years of warnings and other attempts at preparing the ground,
> you seem intent on falling into the trap of thinking that these things are
> "just methods" and that we are better served by generalizing methods to support
> patterns. Everything about the model here places patterns as dual to methods;
> trying to hide that with syntax that makes it look like "just a method" is then
> putting the ball in our own net, because it props up wrong ideas about what is
> going on.

> (In the classfile translation, we will of course use methods, and some sort of
> carrier, but that's a compilation trick, and we surely don't want to expose
> this model to ordinary programmers (though MethodHandle programmers will
> probably have to deal with it.))
Also people using a debugger will see it, people using proxies, aop, interception etc will see it too. 

> Unlike methods, patterns are conditional.
> Unlike methods, patterns can bind zero or more results.
> Unlike methods, patterns are overloaded on their bindings, not their arguments.
- patterns are conditional 

Technically, the code of the pattern is not conditionnal, it will be always executed. 
We may want to tag at the declaration if a pattern can return no-match or not so the compiler will not require a default branch in a switch. 
But the body of a pattern behave the same way as the body of a method. 

- Unlike methods, patterns can bind zero or more results 

Yes, but it can be see as returning one result containing multiple components 

- Unlike methods, patterns are overloaded on their bindings, not their arguments 

No, patterns can be overloaded by both their bindings *and* their parameters 

And, patterns are also bound to an instance, static, default, abstract, synchronized, etc like methods. 
You may say that you are proposing a split where i see a lump, but for me it's like saying constructor are not methods, that's true in a sense. 

>> Now, what i call a carrier type is what you call a list of bindings.
> You can call it that, but the syntax you are proposing presents it as something
> different -- as a thing that is returned from a method.
Yes, the list of binding can be declared whenever we want but i think it's easier to understand a template if the binding list is declared where the return type should be, because seeing a template as like a method that instead of returning a value propagate several bindings is not a bad approximation. Like constructor do not specify a return type because as a casual user you want to think it "returns" the instance but the VM physics uses void. 

>> And I do not know how you define what a binding list is but multiple return +
>> components description is a good definition for me.

> We define it the same way as we define a parameter list. A parameter list is not
> a first-class thing in the language; you can't express one separately from a
> method call, or assign one to a variable, or return one. It is strictly a
> linguistic mechanism for (a) declaring the shape of a method and (b) passing
> parameters to a method at runtime.

> A binding list is the dual of this; it is strictly a linguistic mechanism for
> (a) declaring the shape of a pattern and (b) passing bindings _from_ a pattern
> at runtime.
I do not disagree. Maybe we do not need the keyword to be attached to the binding list but to be attached to the method itself. 
My idea was that people will see the runtime object like they see the lambda proxy so attaching the keyword on the binding list was a way to say, if you want to think it's like a method that returns several componenets, that's not a bad approximation. 

Rémi 

> On 4/3/2024 10:23 AM, [ mailto:forax at univ-mlv.fr | forax at univ-mlv.fr ] wrote:

>>> From: "Brian Goetz" [ mailto:brian.goetz at oracle.com | <brian.goetz at oracle.com> ]
>>> To: "Remi Forax" [ mailto:forax at univ-mlv.fr | <forax at univ-mlv.fr> ]
>>> Cc: "amber-spec-experts" [ mailto:amber-spec-experts at openjdk.java.net |
>>> <amber-spec-experts at openjdk.java.net> ]
>>> Sent: Wednesday, April 3, 2024 2:48:40 PM
>>> Subject: Re: Member Patterns -- the bikeshed

>>> I would summarize your comments below as: Let's throw the entire model in the
>>> garbage, and replace it with something like Scala's "return an Optional<Tuple>"
>>> instead.

>>> We've been discussing the model for several years; you've been asking (and
>>> waiting patiently) for "when are we going to talk about declaration syntax",
>>> and now that we're there, you want to throw it all out and start over?
>> My makeup job was too big so you do not recognize your model behind :)

>> There are two parts, the declaration part and the use-site part.
>> Correct me if i'm wrong but apart the support of a method pattern with no
>> prefix, we are in agreement here.

>> For the declaration part, I think that
>> carrier(int x, int y) asCartesian()
>> is more readable than
>> inverse () asCartesian(int x, int y)

>> The inverse notation is a leaky abstraction in a leat two cases
>> - when a modifier or an annotation is used. For an annotation, there is a notion
>> of a target and the parameter target is at the wrong place,
>> - when declaring a lambda, because in that case the parameters are not inversed.

>> Now, what i call a carrier type is what you call a list of bindings.
>> In terms of syntax, I think it is important to put a name in front of that list
>> of bindings, i've proposed "carrier" so we provide a name for that feature,
>> it's easier when discussing about it it or google it.
>> That does not change the fact that a method that returns a carrier is a special
>> method because it requires at least a special erasure (because overloading),
>> and a special reflection API,

>> But I hope, we will not cross the line and have to use new opcodes in the
>> bytecode.
>> For me, a method that returns a carrier is something that can be desugared
>> classical Java elements like an enum or a record is desugared to a class.

>>> We've discussed how strategies that rely on "ask the user to declare a record
>>> for every API point" feel clever for about five minutes, but start to feel old
>>> quickly.
>> yes, this is what you have to do actually if you simulate the feature with Java
>> nowadays. Not, what you should have to do in the future.
>> And the idea is to do better, among other things, we want to suport overloading.

>>> The "carrier" concept in your examples seems to be just another way of
>>> reinventing multiple return -- with the added dis-bonus of being like but not
>>> quite the same as records. We've been pretty clear that "multiple return" is
>>> not the design center here.
>> The idea behind a carrier is to let users define their binding list is a way
>> that does not feel too strange, that why I propose to add a name/keyword in
>> front of the binding list.

>> And I do not know how you define what a binding list is but multiple return +
>> components description is a good definition for me.

>> Rémi

>>> The use of ! for indicating totality is interesting, that's worth thinking
>>> about.

>>> On 4/3/2024 6:21 AM, Remi Forax wrote:

>>>> I think that by not starting from the deconstructor, the notion of inverse
>>>> methods make less sense.
>>>> I think that the notion of carrier / carrier type is less disruptive that the
>>>> notion of member patterns.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-observers/attachments/20240404/5ed384a2/attachment-0001.htm>


More information about the amber-spec-observers mailing list