Named record pattern

Remi Forax forax at
Tue May 31 22:15:20 UTC 2022

> From: "Maurizio Cimadamore" <maurizio.cimadamore at>
> To: "Brian Goetz" <brian.goetz at>, "Tagir Valeev" <amaembo at>
> Cc: "amber-spec-experts" <amber-spec-experts at>
> Sent: Tuesday, May 31, 2022 7:41:21 PM
> Subject: Re: Named record pattern

> While merging is an issue (if supported), I think early in the design we settled
> on the principle that a binding variable should always only have one
> corresponding declaration (in the places where it is defined).

> So, from a language perspective, I don’t see an immediate problem, in the sense
> that the annotations and finality for “s” would come from the place where “s”
> has been declared.

> From a compiler perspective, the big question is whether, even in the absence of
> merging in the flow scoping rules, we could still perform merging under the
> hood, in order to speed up computation. Consider:
> sealed interface Node
> record Pair (Node fst, Node snd) { } record A (String s) implements Node {}
> record B ( int i) implements Node {}

> And then:
> case Pair (A(String s1) , A (String s2) ) -> ... case Pair (A(String s3) , B (
> int i1) ) -> ... case Pair (B( int i2) , A (String s4) ) -> ... case Pair (B(
> int i3) , B ( int i4) ) -> ...

> (for simplicity, all binding names are different, so that we do not depend on
> whether the block after -> … completes normally or not)

> Of course the first two patterns share the common subpattern A(String s1) (if
> you ignore naming differences). So it might be possible, in principle, for the
> compiler/runtime to generate an optimized decision tree in which the String
> value for the first A(…) sub-pattern is computed only once, and then shared in
> the two cases. (A similar reasoning applies to the last two patterns).

> But if the types in the first and second pattern could be annotated differently,
> then we are faced with a number of challenges, as the compiler would not be
> able to just “blindly” reuse the cached values (as those values would be
> shared, and, therefore, unannotated). Instead, the compiler would have to
> assign the cached value into a fresh , correctly annotated local variable that
> is used only inside the given case . This is not impossible of course, but adds
> complexity to the translation strategy, and/or might affect the number of moves
> we might be able to do.

The same issue arise if the patterns parts are generated at runtime using an invokedynamic. 

I don't think it's an issue if we consider that inside the pattern tree, we have bindings and that once "->" is crossed, those bindings are materialized as local variables with annotations. 

I kind of easier to see it if there an invokedynamic and a carrier object. A switch(value) is translated to 
Object carrier = invokedynamic pattern_match(value) [send the patterns as a constantpool constant]; 
int index = invokdynamic extract (carrier)[binding 0 /* index */]; 
switch(index) { 
case 0 -> { 
// here we rematerialize the local variables 
String s1 = invokdynamic extract (carrier)[binding 1]; 
String s2 = invokdynamic extract (carrier)[binding 2]; 
case 1 -> { 
// we materialize s3 and i 
String s3 = invokdynamic extract (carrier)[binding 1]; 
int i1 = invokdynamic extract (carrier)[binding 2]; 

In a sense, this is similar to a lambda, it's not the parameter of the method of the lambda proxy which is annotated but the parameter of the static method desugared from the lambda body. 
Here, it's not the patterns part (that one that matches) which store the annotations but the part that extract the value from the carrier into local variables that stores the annotation. 

I think this idea also work if we do not use invokedynamic, the matching par can use a Tree or a DAG that fuses several bindings to one, it's not an issue if we generate local variables initialized from the binding values afterward. 


> Maurizio


> On 31/05/2022 17:12, Brian Goetz wrote:

>> As a confounding example that suggests that pattern variables are not "just
>> locals", in the past we talked about various forms of "merging":

>> if (t instanceof Box(String s) || t instanceof Bag(String s)) { ... }

>> or

>> case Box(String s):
>> case Bag(String s):
>> common-code;

>> If pattern variables could be annotated, then the language would be in the
>> position of deciding what happens with

>> case Box(@Foo(1) String s):
>> case Bag(@Foo(2) String s):

>> (This is the "annotation merging" problem, which is why annotations are not
>> inherited in the first place.)
>-------------- next part --------------
An HTML attachment was scrubbed...
URL: <>

More information about the amber-spec-experts mailing list