Compact deconstruction patterns

Tagir Valeev amaembo at gmail.com
Wed Mar 4 11:19:46 UTC 2020


Hello!

Yes, this clarifies things better, thanks.

ср, 4 мар. 2020 г., 18:03 Remi Forax <forax at univ-mlv.fr>:

> 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