> From: "Gavin Bierman" <[email protected]> > To: "Remi Forax" <[email protected]> > Cc: "Brian Goetz" <[email protected]>, "amber-spec-experts" > <[email protected]> > Sent: Friday, January 26, 2024 1:36:22 PM > Subject: Re: Towards member patterns
> Hi Remi, >> On 26 Jan 2024, at 11:08, Remi Forax <[email protected]> wrote: >> Let's retry. >> I think your proposal solves the cases where the type you are switching on is >> closed (final, sealed) but not if the type is open (non-sealed). >> Let's take an example, let suppose I've the following hierarchy >> public sealed interface Tree { >> static Tree none() { return None.NONE; } >> static Tree cons(Tree tree) { return new Cons(tree); } >> } >> private enum None implemnts Tree { NONE } >> private class Cons implements Tree { >> private final Tree tree; >> private Cons(Tree tree) { this.tree = tree; } >> } >> If I want to have a static method children that returns all the children of >> the >> Tree, using the pattern matching I would like to write >> static List<Tree> children(Tree tree) { >> return switch(tree) { >> case Tree.none() -> List.of(); >> case Tree.cons(Tree child) -> List.of(child); >> }; >> } >> And inside Tree, i can add the following inverse methods >> static inverse Tree none() { if (that == Tree.NONE) __yield (); } >> static inverse Tree cons(Tree tree) { if (that instanceof Cons cons) __yield >> (cons.tree); } >> As I said, it works great with a closed hierarchy, but now let suppose the >> hierarchy is not sealed, if the hierarchy is not sealed, having static >> factories make less sense because we do not know all the subtypes. So we have >> public interface Tree {} >> public enum None implemnts Tree { NONE } >> public class Cons implements Tree { >> private final Tree tree; >> public Cons(Tree tree) { this.tree = tree; } >> } >> and in the future, someone may add >> public class Node { >> private final Tree, left, right; >> public Node(Tree left, Tree right) { this.left = left; this.right = right; } >> } >> Because the hierarchy is open, we need to use the late binding here. >> So i may rewrite children like this >> static List<Tree> children(Tree tree) { >> return switch(tree) { >> case that.extract(List<Tree> list) -> list; // wrong syntax, it's just to >> convey >> the semantics >> }; >> } > Already here I would disagree. I think you have missed the abstraction. You > want all `Tree` instances to support an instance pattern member (I think of an > instance pattern member as a *view*, which I find quite suggestive)? Then you > need to say it, e.g.: > public interface Tree { > pattern Parent(List<Tree> children); // all trees can be viewed as a > // parent with children > } > Now your `None` and `Cons` classes will be required to implement this instance > pattern member, i.e. > public enum None implements Tree { NONE > pattern Parent(List<Tree> children) { > children = List.of(); > } > } > public class Cons implements Tree { > private final Tree tree; > public Cons(Tree tree) { this.tree = tree; } > pattern Parent(List<Tree> children) { > children = List.of(tree); > } > } > Then you can rewrite your `children` static method (although it is perhaps a > little defunct): > static List<Tree> children(Tree tree) { > return switch(tree) { > case Parent(List<Tree> children) -> children; > }; > } > The switch is exhaustive because the `Parent` view is total (by the absence of > the `partial` modifier - maybe we'll insist on `total`, TBD). > Now you can freely extend the hierarchy, and your `children` static method > will > work without modification: > public class Node implements Tree { > private final Tree left, right; > public Node(Tree left, Tree right) { this.left = left; this.right = right; } > pattern Parent(List<Tree> children) { > children = List.of(left, right); > } > } > (Perhaps a better implementation would be to declare a *partial* `Parent` view > and then have `None` fail, but I leave that to your imagination. This approach > still works.) > Or did I misunderstand your example? I don't think so,you got it. What was not clear is how the compiler links the pattern Parent to the pattern method Parent(List<Tree>) inside Tree. Thanks to the mail of Brian, the answer is either "case Tree.Parent(List<Tree> children) -> ..." or "case tree.Parent(List<Tree> children) -> ...", both will work. > Gavin Rémi
