And now that I have posed the model below, I can spot at least two ways in which the model is wrong in its details. But I think its structure provides a useful framework for discussion. I will try to produce a corrected version late today.
—Guy > On Aug 12, 2020, at 12:20 PM, Guy Steele <guy.ste...@oracle.com> wrote: > >> On Aug 12, 2020, at 9:45 AM, fo...@univ-mlv.fr wrote: >> . . . >> yep, i'm hapy with "default P", let me explain why : >> >> I see the switch semantics as a kind of compact way to represent a cascade >> of if-else, >> with that they are several pattern >> case Constant is equivalent to if (o.equals(Constant)) or if (o == Constant) >> case null is equivalent to if (o == null) >> case P p is equivalent of if (o instanceof P p) >> and >> default P p is equivalent to else { P p = o; } >> >> some patterns accept null (case null and default P), so if one of them is >> present in a switch, the switch accept null, otherwise it does not. >> >> so the following code >> if (o instanceof Frog f) { ... } >> else if (o instanceof Chocolate c) { ... } >> else { var x = o; ... } >> can be refactored to >> switch(o) { >> case Frog f: ... >> case Chocolate c: ... >> default var x: ... >> } >> >> About exhaustiveness, if a switch is exhaustive, by example with >> sealed interface Container permits Box, Bag { } >> the following switch is exhaustive >> switch(container) { >> case Box box: ... >> case Bag bag: ... >> } >> >> Here there is no need for a total pattern and if a user want to allow null, >> he can add a "case null". >> >>> Now, what is the story for nested patterns? >>> >>> switch (container) { >>> case Box(Frog f): ... >>> case Box(Chocolate c): ... >>> case Box(var x): .... >>> >>> case Bag(Frog f): ... >>> case Bag(Chocolate c): ... >>> case Bag(var x): .... >>> >>> } >> >> so this is a mix between an exhaustive switch with two total patterns once >> de-constructed, for me, it should be written like this >> switch(container) { >> case Box(Frog g): ... >> case Box(Chocolate c): ... >> default Box(var x): ... >> >> case Bag(Frog g): ... >> case Bag(Chocolate c): ... >> default Bag(var x): ... >> } >> >> using the syntax "default Box(var x)" to say that the nested-patterns are >> locally total thus accept null. >> It's a little weird to have the "default" in front of the type name while it >> applies on the nested part but i'm Ok with that. > > Very interesting proposal, to allow more than one “default” clause in a > switch! > > I’m not yet sure whether I like this path, but I want to explore it further, > and to do that I will attempt to formalize it a bit more and then look at a > more detailed example. > > Remi suggested these rules: > > `case Constant` is equivalent to `if (o.equals(Constant))` or `if (o == > Constant)` > `case null` is equivalent to `if (o == null)` > `case P p` is equivalent to `if (o instanceof P p)` > `default P p` is equivalent to `else { P p = o; }` > > I think that to get the desired effect in the last example, it is necessary > to be more detailed, and distinguish various kinds of patterns (let T stand > for a type, let P: Pattern T, let Q be any pattern, and let S be a statement): > > `case X` is equivalent to `if (CASE_EXPAND(o, X))` > `default X` is equivalent to `if (DEFAULT_EXPAND(o, X))` > (**) we will refer to this rule later > `default` is equivalent to `default var unused_variable` > > `CASE_EXPAND(o, Constant)` is equivalent to `o.equals(Constant)` or `(o > == Constant)` > `CASE_EXPAND(o, null)` is equivalent to `(o == null)` > `CASE_EXPAND(o, T p)` is equivalent to `o instanceof T p` > `CASE_EXPAND(o, var p)` is equivalent to `o instanceof var p` [just > a way to bind p to the value of o in the middle of an expression] > `CASE_EXPAND(o, P(Q))` is equivalent to `o instanceof P(T alpha) && > CASE_EXPAND(alpha, Q)` > > `DEFAULT_EXPAND(o, Constant)` is a static error? > `DEFAULT_EXPAND(o, null)` is a static error? > `DEFAULT_EXPAND(o, T p)` is equivalent to `{ T p = o; }` > `DEFAULT_EXPAND(o, var p)` is equivalent to `{ var p = o; }` > `DEFAULT_EXPAND(o, P(Q))` is equivalent to `o instanceof P(T alpha) && > DEFAULT_EXPAND(alpha, Q)` > > where by an abuse of notation I write `{ T p = o; }` for an “expression” that > checks to see whether the value of o is assignable to type T, and if it is > then binds the variable p to that value produces the value true, and > otherwise produces false. (In other words, it is like `o instanceof T p` but > accepts nulls.) > > I am assuming that `o instanceof P(T alpha)` is null-friendly (it always > allows the possibility that alpha may be bound to null). > > (And I note that I have been sloppy about how the cases and their associated > statements are glued together to make a complete translation of a switch > statement.) > > > Now let’s examine this extended example (assume `record Box(Object f)` and > `record Bag(Object f)` and record `FrogBox(Frog f)`): > > switch(o) { > case Box(Frog g): ... > case Box(Chocolate c): ... > default Box(var x): ... > > case Bag(Frog g): ... > case Bag(Chocolate c): ... > default Bag(Object x): … // I changed `var x` to > `Object x` here > > case FrogBox(Toad t): … > case FrogBox(Tadpole tp): ... > default FrogBox(Frog fr): … > > default: ... > } > > This would expand to something like: > > if (o instanceof Box(Object alpha) && alpha instanceof Frog g) … > else if (o instanceof Box(Object alpha) && alpha instanceof Chocolate > c) … > else if (o instanceof Box(Object alpha) && { var x = alpha; }) … > > else if (o instanceof Bag(Object alpha) && alpha instanceof Frog g) … > else if (o instanceof Bag(Object alpha) && alpha instanceof Chocolate > c) … > else if (o instanceof Bag(Object alpha) && { Object o = alpha; }) … > > else if (o instanceof FrogBox(T alpha) && alpha instanceof Toad t) … > else if (o instanceof Box(T alpha) && alpha instanceof Tadpole tp) … > else if (o instanceof Box(T alpha) && { Frog fr = alpha; }) … > > else … > > > But now I realize that this model is not quite what we had discussed before: > I think I need to change one of the rules above (**) to three rules: > > `default T p` is equivalent to `T p = o; if (true)` > `default var p` is equivalent to `var p = o; if (true)` > `default P(Q)` is equivalent to `if (DEFAULT_EXPAND(o, P(Q)))` > > That is, at “top level”, the situations `default T p` and `default var p` are > not conditional, but are required to succeed, and you get a static error for > the first one if o is not assignable to T. This may be ugly, but at least it > reveals explicitly that we are treating the outermost situation in a > `default` label a bit differently from nested situations. > > > Does this model capture the intent of what everyone wants? > > —Guy >