Next up for patterns: type patterns in switch
Guy Steele
guy.steele at oracle.com
Wed Aug 12 16:20:32 UTC 2020
> On Aug 12, 2020, at 9:45 AM, forax at 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
More information about the amber-spec-observers
mailing list