[pattern-switch] Totality

forax at univ-mlv.fr forax at univ-mlv.fr
Sat Aug 29 00:50:13 UTC 2020


----- Mail original -----
> De: "Guy Steele" <guy.steele at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Brian Goetz" <brian.goetz at oracle.com>, "amber-spec-experts" <amber-spec-experts at openjdk.java.net>
> Envoyé: Samedi 29 Août 2020 00:49:26
> Objet: Re: [pattern-switch] Totality

>> On Aug 28, 2020, at 5:59 PM, forax at univ-mlv.fr wrote:
>> 
>> . . .
>> Again, it should work like a cascade of if ... instanceof, so
>>   case Pixel(var x, var y, var color) -> color
>> should be equivalent to
>>   if x instanceof Pixel p { yield p.color() }
> 
> But I do not believe that at all.  I do believe that
> 
>  case Pixel(var x, var y, var color) -> color
> 
> should be equivalent to
> 
>  if x instanceof Pixel(var x, var y, var color) p { yield p.color() }
> 
> or, if you prefer, to
> 
>  if x instanceof Pixel(var x, var y, var color) { yield color }
> 
> The point is that the switch label `case Pixel(var x, var y, var color)` does
> not merely demand that the selector value be a Pixel; it demands that it be a
> Pixel having a specific three-argument destructor.  It can be equivalent only
> to an instanceof expression that makes those same demands.
> 
> If you want a switch clause that is equivalent to
> 
>  if x instanceof Pixel p { yield p.color() }
> 
> then you should write
> 
>   case Pixel p -> p.color()


It doesn't have to be that way.

Let say Pixel have a deconstructor, something like
  deconstructor Pixel { return this; }
because Pixel is already a record, it can return itself

and when a pattern is used, you have something like this
  x instanceof Pixel(var foo, var bar, var baz)

so for the compiler accessing to foo is equivalent of extracting Pixel::x, accessing to bar is equivalent to accessing to Pixel::y and accessing to baz is equivalent to accessing to Pixel::color,
the compiler matches the positional parameters of the destructor with the position of the destructuring pattern

so for
  if x instanceof Pixel(var foo, var bar, var baz) { yield baz }
the equivalent code at runtime is
  if x instanceof Pixel p { yield p.color(); }

This is very similar to the way enums works, when you declare an enum, each constant has a position but when you match an enum using a switch, at runtime the constants are matched by name not by position.
Similarly the serialization of enums, which is a form of adhoc matching, is also done by name not by position.

Another way of seeing it, it's very similar to the way the VM resolve fields, it's a two step process, in the bytecode you have a name, and at runtime the VM find the corresponding offset.
Here, the compiler matches the record resulting from the deconstructor with the destructuring pattern and insert in the bytecode the corresponding name,
at runtime, we do the opposite, we call the deconstructor (because the matching is done at runtime, you don't need more than one) and extract the value from the name.

Rémi


More information about the amber-spec-experts mailing list