----- Mail original -----
> De: "Brian Goetz" <brian.go...@oracle.com>
> À: "Remi Forax" <fo...@univ-mlv.fr>
> Cc: "Guy Steele" <guy.ste...@oracle.com>, "amber-spec-experts" 
> <amber-spec-experts@openjdk.java.net>
> Envoyé: Dimanche 30 Août 2020 16:23:38
> Objet: Re: [pattern-switch] Totality

> Yes, this looks very much like a PoC I did once too.  The upshot of that
> experiment is that operators like AND, OR, nesting, and guarding are amenable
> to implementation as combinators, which is nice.
> 
>> To deal with the deconstruction, i need 3 primitives
>> - deconstruct(Object value) that calls the deconstructor if the deconstructed
>> object is a class
> 
> Here you’re making an assumption about the language model, which is that 
> classes
> may only have one deconstructor.  This is a pretty serious limitation, and
> forces pattern matching to stay very much on the periphery of the object 
> model.
> (I know why you want to make this assumption, because you want the bindings to
> be the return type and there is no return type overloading.)

It's a PoC.

You can notice that adding more fields to the anonymous record, the record 
returned by the destructor is a binary compatible change.
It can also be a source compatible change if the compiler allows the 
deconstructing pattern to have less parameters than the declared parameters of 
the deconstructor.
By example, for a record Pixel(int x, int y, String color) {},
i.e if instead of the pattern Pixel(var x, var y, var _), one can write the 
pattern Pixel(var x, var y).

You can also select among several deconstructors at runtime (as a linking pass 
done once) if they return a disjoint set of values,
by first selecting all the applicable deconstructors and then selecting the 
most specific when you construct the method handle tree.
But i'm not sure you need that dynamic linking selection in practice.


>> - extract(Record record, String name) that can extract the value of a record
>> component from its name
>> - with(Record record, String name, Object value) that creates a new record 
>> with
>> the value of the record component corresponding to the name changed
>> 
>> The implementation of those primitives relies on the reflection so it's slow 
>> and
>> wrong in term of security, but it's enough for a prototype [2]
> 
> Yes, but.  It also makes an assumption about runtime resolution of bindings
> based on component name.  Again, you are making a big assumption about “how 
> the
> language should work”, but again it has gotten buried in an implementation
> detail.  So whatever we learn from this model comes attached to some big
> assumptions.

The assumption is that
  case Pixel(_ , _, var color) -> System.out.println(color);
is equivalent to
  if (value instanceof Pixel pixel) {
    var record = pixel.deconstructor();  // if Pixel is not a record
    System.out.println(record.color());
  }

>From a user POV you don't care, from a binary compatible POV, there is no 
>explicit reference to a specific deconstructor in the bytecode because the 
>matching is done by name instead of being positional.

> 
> FWIW, my implementation treated a pattern as a bundle of method handles, one 
> was
> a mapping from the match target to an opaque carrier (null meant no match), 
> and
> N method handles from the carrier to the binding value.  In many cases the
> target itself was a suitable carrier.

a real implementation should not use null because we want the carrier object to 
be an inline, but saying by convention that if __index__ == 0 (which is 
equivalent to saying it's the default value of an inline) represents "not 
match" should be enough.  

> 
>> The deconstruction is based on the names of the record components and not 
>> their
>> declared positions in the record.
>> You may think that this way of doing the deconstruction is not typesafe but 
>> the
>> fact that all the fields of the carrier record are typed means that all the
>> bound variables have the correct type.
> 
> Yes, but type safety is one of the lesser of my concerns here.
s t
ok, good.

Rémi


Reply via email to