Towards member patterns
Gavin Bierman
gavin.bierman at oracle.com
Fri Jan 26 12:36:22 UTC 2024
Hi Remi,
On 26 Jan 2024, at 11:08, Remi Forax <forax at univ-mlv.fr> 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?
Gavin
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/amber-spec-experts/attachments/20240126/da567397/attachment.htm>
More information about the amber-spec-experts
mailing list