I do not see (B) as sacrifying the consistency because the premise is that an expression switch should be consistent with ?:
But an expression switch can also be modeled as a classical switch that returns it's value to a local variable. int a = switch(foo) { case 'a' -> 2; case 'b' -> 3; } can be see as int a = $switch(foo); with int $switch(char foo) { case 'a': return 2; case 'b': return 3; } In that case, given that return allows target typing, (B) seems to be the only consistent choice, or (B) is as consistent as (A) with a different premise. regards, Rémi ----- Mail original ----- > De: "daniel smith" <daniel.sm...@oracle.com> > À: "amber-spec-experts" <amber-spec-experts@openjdk.java.net> > Envoyé: Mercredi 28 Mars 2018 21:37:51 > Objet: Feedback wanted: switch expression typing > (Looking for some feedback on real-world code usage. Please read to the end, > then if you can experiment with the code you work on and report back, I'd > appreciate it!) > > Switch expressions, from a type checking perspective, are basically > generalizations of conditional expressions: instead of 2 operands to check, we > have n. > > A reasonable expectation is that, if I rewrite my conditional expression as a > switch expression, it will behave the same: > > test ? foo() : bar() > is equivalent to > switch (test) { case true -> foo(); case false -> bar(); } > > So, as a starting point, the typing rules for switches should be the same as > the > typing rules for conditionals, but generalized to an arbitrary number of > results. > > (The "results" of a switch expression are all expressions appearing after a > '->' > or a 'break'.) > > Conditional expressions and switch expressions are typically used as poly > expressions (in a context that has a target type). But that won't always be > the > case. One notable usage that doesn't have a target type is an initializer for > 'var': "var x = ...". So they are sometimes poly expressions, sometimes > standalone. > > Conditional expression typing is driven by an ad hoc categorization scheme > which > looks at the result expressions and tries to predict whether they will all > have > type boolean/Boolean, primitive/boxed number, or something else/a mix ("tries > to predict" because in some cases we can't type-check the expression until > we've completed the categorization). > > In the numeric case, we then identify the narrowest primitive type that can > contain the results. > > In the other/mixed case, we then type check by pushing down a target type, or, > if none is available, producing a reference type from the lub operation. > > A couple of observations: > > - The primitive vs. reference choice is meaningful, because the primitive and > reference type hierarchies are different (e.g., int can be widened to long, > but > Integer can't be widened to Long). Preferring primitive typing where possible > seems like the right choice. > > - The ad hoc categorization is a bit of a mess. It's complex and imperfect. > What > people probably expect is that, where a target type is available, that's what > the compiler will use—but the compiler ignores the target type in the > primitive > cases. > > Why? Well, in 8, when we introduced target typing of conditionals, we > identified > some incompatibilities that would occur if we changed the handing of > primitives, and we didn't want to be disruptive. > > Some examples: > Boolean x = test ? z : zbox; // specified: can NPE; target typing: no null > check > Integer x = test ? s : i; // specified: ok; target typing: can't convert > short->Integer > Number x = test ? s : i; // specified: box to Integer; target typing: box to > Short or Integer > double d = test ? l : f; // specified: long->float loses precision; target > typing: long->double better precision > m(test ? z : zbox); // specified: prefers m(boolean); target typing: > m(boolean) > and m(Boolean) are ambiguous > > At this point, we've got a choice: > A) Fully mimic the conditional behavior in switch expressions > B) Do target typing (when available) for all switch expressions, diverging > from > conditionals > C) Do target typing (when available) for all switches and conditionals, > accepting the incompatibilities > > (A) sacrifices simplicity. (B) sacrifices consistency. (C) sacrifices > compatibility. > > General thoughts on simplicity (is the current behavior hard to understand?) > and > consistency (is it bad if the conditional/switch refactoring leads to subtly > different typing?) are welcome. > > And we could use some clarification is just how significant the compatibility > costs of (C) are. With that in mind, here's a javac patch: > > http://cr.openjdk.java.net/~dlsmith/logPrimitiveConditionals.patch > > A javac built with this patch supports an option that will output diagnostics > wherever conditionals at risk of incompatible change are detected: > > javac -XDlogPrimitiveConditionals Foo.java > > If you're able to build OpenJDK with this patch and run it on some real-world > code, I'd appreciate any insights about what you find. > > —Dan