Allow case constants as nested patterns?

Gavin Bierman gavin.bierman at oracle.com
Tue Nov 7 09:37:28 UTC 2023


Hi Sebastian,

Yes, there has been some discussion about adding constant patterns if you look through the archive. There are interesting syntactic issues, but IIRC we have been waiting for the primitive type patterns feature to settle so we have a proper basis to deal with any type conversion questions. (For example, you write switch(new Box(b)) { case Box(0) -> … } where b is a byte and 0 is an integer constant…)

So, it’s on our radar :-)

Gavin

> On 2 Nov 2023, at 11:13, Sebastian Fischer <mail at sebfisch.de> wrote:
> 
> Hello.
> 
> I would like to improve my understanding of the interaction of two JEPs: Pattern matching for switch and Record patterns.
> 
> I am new to this list so might have missed previous discussion about this but could not find what I was looking for in the archives.
> 
> JEP 441 shows the following grammar for switch labels.
> 
> SwitchLabel:
>   case CaseConstant { , CaseConstant }
>   case null [, default]
>   case Pattern [ Guard ]
>   default
> 
> JEP 440 shows the following grammar for patterns.
> 
> Pattern:
>   TypePattern
>   RecordPattern
> 
> TypePattern:
>   LocalVariableDeclaration
> 
> RecordPattern:
>   ReferenceType ( [ PatternList ] )
> 
> PatternList : 
>   Pattern { , Pattern }
> 
> As a consequence it is not possible to have case constants in the pattern list of a record pattern. For example consider the following definitions modeling (a simplified version of) boolean expressions.
> 
> sealed interface BoolExpr {
>     enum Constant implements BoolExpr { TRUE, FALSE }
>     record And(BoolExpr left, BoolExpr right) implements BoolExpr {}
> 
>     default BoolExpr recursively(UnaryOperator<BoolExpr> transform) {
>         return transform.apply(switch (this) {
>             case Constant c -> c;
>             case And(var left, var right) -> new And(
>                 left.recursively(transform),
>                 right.recursively(transform)
>             );
>         });
>     }
> 
>     default BoolExpr shortCircuit() {
>         return recursively(expr -> switch (expr) {
>             case And(var left, var unused)
>                 when left == Constant.FALSE -> Constant.FALSE;
>             default ->
>                 expr;
>         });
>     }
> }
> 
> In the definition of `shortCircuit` (which does partial evaluation) we need to use a guard to check if the left argument of `And` is false.
> 
> Instead of using an enum, we can model constants as empty records as follows.
> 
>     sealed interface Constant extends BoolExpr permits True, False {}
>     record True() implements Constant {}
>     record False() implements Constant {}
> 
> Now we can write `shortCircuit` using a nested record pattern.
> 
>     default BoolExpr shortCircuit() {
>         return recursively(expr -> switch (expr) {
>             case And(False(), var unused) -> new False();
>             default -> expr;
>         });
>     }
> 
> It would be convenient to be able to use a nested pattern instead of a guard with the original definition using an enum for constants. Here is a hypothetical implementation of `shortCircuit` that is currently not supported.
> 
>     default BoolExpr shortCircuit() {
>         return recursively(expr -> switch (expr) {
>             case And(Constant.FALSE, var unused) -> Constant.FALSE;
>             default -> expr;
>         });
>     }
> 
> What are potential problems with allowing case constants as nested patterns?
> 
> Kind regards,
> Sebastian
> 
> 
> 
> 
> 



More information about the amber-dev mailing list