AW: Compact deconstruction patterns
Matthias Perktold
tias251 at gmail.com
Wed Mar 4 11:18:42 UTC 2020
OK this makes sense.
Now understand why my proposal is not a good idea.
Thanks for taking the time to clarify things!
Matthias
-----Ursprüngliche Nachricht-----
Von: Remi Forax <forax at univ-mlv.fr>
Gesendet: Mittwoch, 4. März 2020 11:18
An: Matthias Perktold <tias251 at gmail.com>; Tagir Valeev <amaembo at gmail.com>
Cc: amber-dev <amber-dev at openjdk.java.net>; Brian Goetz <brian.goetz at oracle.com>
Betreff: Re: Compact deconstruction patterns
I think Brian is miles ahead :)
that's why you did not understand his answer, let me try to explain the problem and why var/type is the best choice.
Let's takes an example, using the syntax you propose, var shape = ...
var value = switch(shape) {
case Point(x, y) -> x + y;
...
};
now we also want to support adding constant as pattern, something like var value = switch(shape) {
case Point(0, y) -> y;
...
};
obviously, if we support constant, we also need to support variable, so the last code can be rewritten like this by introducing a new local variable x var x = 0; var value = switch(shape) {
case Point(x, y) -> y;
...
};
And here, i'm sure you have spot the issue, 'case Point(x, y)' is now ambiguous, because whe don't know if x refer to the local variable above or to a fresh new local variable.
If you study other languages that have pattern matching, there are 3 possible solutions
1. be clever but ...
2. use a pin operator
3. use var
Option 1, we can be clever, in Java, you can not introduce the same local variable twice so 'case Point(x, y)' means that x is the already existing local variable because otherwise, introducing a new fresh local variable will result in an error. But this solution has two drawbacks, the most important, it means that introducing a local variable above a switch (or an instanceof, etc) change the semantics of the switch, not a good property apart if you write code puzzler for a living. The other issue is that it interacts badly with fields. By example, if you have the code
class Foo {
int x;
void m() {
var value = switch(shape) {
case Point(x, y) -> y;
...
};
}
}
here 'x' in case Point(x, y) means it's a fresh variable, if you want to use the field, you have to explicitly prefix it by 'this' (or the class for static field). If Java was not Java but Ruby (that has a different syntax for field and local variable), it may have been a good solution, but the fact that you can reference a field or a local variable using the same syntax in Java makes the option 1 not a good fit for Java.
Option 2, like in Elixir, we have a special syntax to denote that a name inside a pattern is an existing local variable and not a fresh local variable.
Elixir uses the operator ^ for that.
var x = 0;
var value = switch(shape) {
case Point(^x, y) -> y;
...
};
In Elixir, you declare a local variable like this "x = ..." like in Python, there is no prefix keyword let/var like in Java, JavaScript. So for Elixir, it makes sense to reuse the same syntax to create a fresh variable and to use a special syntax if you want to reference a local variable.
But in Java, you declare a new local variable by prefixing it with a type or var, so using a pin operator whatever the syntax will be alien to the rest of the Java code.
So Option 3, use var and the type everywhere you want to have a fresh local variable. It's stupid because in most of the cases, you want more a fresh new local variable than an existing local variable but it fits with the other constructs of the language and readability and consistence have always been more important that the number of characters in Java.
I hope this help !
Rémi
----- Mail original -----
> De: "Brian Goetz" <brian.goetz at oracle.com>
> À: "Tagir Valeev" <amaembo at gmail.com>
> Cc: "amber-dev" <amber-dev at openjdk.java.net>, "Matthias Perktold"
> <tias251 at gmail.com>
> Envoyé: Mercredi 4 Mars 2020 08:55:19
> Objet: Re: Compact deconstruction patterns
> That’s all fine, but I think you are mischaracterizing the reason we
> are preferring var? t’s not because we don’t like the alternative. It
> is because it is the natural Combination of type inference with type
> patterns. And, given that combination, there is no need to go further
> in the concision department, and lots of reasons not to.
>
>
>
> Sent from my iPad
>
>> On Mar 4, 2020, at 5:24 AM, Tagir Valeev <amaembo at gmail.com> wrote:
>>
>> Hello!
>>
>>> This has come up a few times. There are several reasons to prefer
>>> the current approach, most of which stem from the prime directive,
>>> “reading code is more important than writing code.” The argument
>>> for the compact version is really “I can do less typing”, which is a
>>> relatively weak motivation when the typing we are saving is the three characters v-a-r.
>>
>> For me, this argument doesn't sound convincing. The problem is
>> exactly with reading: you have to read all these noise var's, this
>> makes lines longer for no big reason, your eyes need to make more
>> horizontal movements. The deconstruction (especially the nested one)
>> might include many fields, so it's not just four characters (var +
>> space), but four times the number of pattern variables (16 in the
>> example supplied by Matthias). Also, don't forget about visually
>> impaired (not totally blind) people who set very big editor font
>> having much fewer symbols fitting the screen horizontally.
>>
>> I see the arguments below, and they look more reasonable, but
>> probably it's better to come with design decisions how all possible
>> patterns will look like before releasing the partial feature.
>> Probably there are ways to keep more common patterns shorter while
>> allowing to write more complex patterns. I don't think that we should
>> accept the var syntax solely because otherwise it might collide with
>> the syntax of a future feature, and at the same time ask "no syntax
>> bike shedding" for that future feature. What if the future feature
>> syntax could be adjusted to make the clash impossible?
>>
>> With best regards,
>> Tagir Valeev.
>>
>>>
>>> These are variable _declarations_, and variable declarations should
>>> look like declarations. `int x` and `var x` look like declarations;
>>> `x` looks like a use of an existing variable. (You might point out
>>> that we do a similar thing in lambdas, where we use an unadorned
>>> name, but the -> token is is a very, very strong indication that
>>> these are formals. I don’t think that justification is in play
>>> here. Note too that in lambdas you can say `(var x) -> …` now, and
>>> if we had `var` in the language when we did lambdas, we might well
>>> have not even supported the more compact `(x,y) -> …` form at the
>>> time. So rather than this being a precedent to emulate, we might
>>> choose to view it as a historical
>>> inconvenience.)
>>>
>>> Further, down the road, we intend to have patterns that can actually
>>> take input arguments, such as `regex(“a*b*” …)`. (Please, no syntax
>>> bike shedding on this
>>> now.) This creates the possibility where the `x` in `Foo.bar(x)`
>>> could either be an input parameter, or a constant pattern, or a
>>> fresh binding variable — this is asking a lot of readers.
>>>
>>> In the end, it comes down to: there is a benefit to being explicit
>>> (easier to spot declarations), and an additional cost to being
>>> implicit (potential for ambiguities down the road.) The benefit for
>>> being implicit is saving the typing of three characters. This seems a pretty easy decision.
>>>
>>>
>>>>> On Mar 3, 2020, at 9:00 AM, Matthias Perktold <tias251 at gmail.com> wrote:
>>>>
>>>> According to
>>>> https://cr.openjdk.java.net/~briangoetz/amber/pattern-match.html,
>>>> the current proposal is to allow to ways of using records as
>>>> deconstruction
>>>> patterns:
>>>>
>>>> 1) With nested type patterns: case AddNode(Node left, Node right)) -> ...
>>>> 2) With nested var patterns: case AddNode(var left, var right)) -> ...
>>>>
>>>> I was wondering whether it could make sense to allow to leave out var:
>>>> case AddNode(left, right)) -> ...
>>>>
>>>> This would be especially convenient for nested record patterns.
>>>>
>>>> Compare
>>>> case Line(Point(var x0, var y0), Point(var x1, var y1)) -> ...
>>>> with
>>>> case Line(Point(x0, y0), Point(x1, y1)) -> ...
>>>>
>>>> The consequence would be that the syntax rules for the innermost
>>>> patterns would more closely resemble the ones for lambda parameters
>>>> rather than local variables.
>>>>
>>>> Note that, there is no risk of ambiguity.
>>>> Since it appears right after case, it's clear from the context that
>>>> the whole expression is used as a pattern.
>>>> The same holds for instanceof patterns.
>>>>
>>>> Now I'm sure this idea has already crossed your mind.
>>>> Did you reject it for any particular reason?
>>>> Or do you simply want to wait until you are really sure this is
>>>> something you actually want?
>>>>
>>>> I realize this can be added any time in a future release.
>>>>
>>>> Thanks,
>>>> Matthias
>>>>
More information about the amber-dev
mailing list