|
| 1 | +--- |
| 2 | +layout: doc-page |
| 3 | +title: Dropped: Weak Conformance |
| 4 | +--- |
| 5 | + |
| 6 | +In some situations, Scala used a {\em weak conformance} relation when |
| 7 | +testing type compatibility or computing the least upper bound of a set |
| 8 | +of types. The principal motivation behind weak conformance was to |
| 9 | +make as expression like this have type `List[Double]`: |
| 10 | + |
| 11 | + List(1.0, math.sqrt(3.0), 0, -3.3) // : List[Double] |
| 12 | + |
| 13 | +It's "obvious" that this should be a `List[Double]`. However, without |
| 14 | +some special provision, the least upper bound of the lists's element |
| 15 | +types `(Double, Double, Int, Double)` would be `AnyVal`, hence the list |
| 16 | +expression would be given type `List[AnyVal]`. |
| 17 | + |
| 18 | +A less obvious example is the following one, which was also typed as a |
| 19 | +`List[Double]`, using the weak conformance relation. |
| 20 | + |
| 21 | + val n: Int = 3 |
| 22 | + val c: Char = 'X' |
| 23 | + val n: Double = math.sqrt(3.0) |
| 24 | + List(n, c, d) // used to be: List[Double], now: List[AnyVal] |
| 25 | + |
| 26 | +Here, it is less clear why the type should be widened to |
| 27 | +`List[Double]`, a `List[AnyVal` seems to be an equally valid -- and |
| 28 | +more principled -- choice. |
| 29 | + |
| 30 | +To simplify the underlying type theory, Dotty drops the notion of weak |
| 31 | +conformance altogether. Instead, it provides more flexibility when |
| 32 | +assigning a type to a constant expression. The rule is: |
| 33 | + |
| 34 | + - If a list of expressions `Es` appears as one of |
| 35 | + |
| 36 | + - the elements of a vararg parameter, or |
| 37 | + - the alternatives of an if-then-else or match expression, or |
| 38 | + - the body and catch results of a try expression, |
| 39 | + |
| 40 | + and all expressions have primitive numeric types, but they do not |
| 41 | + all have the same type, then the following is attempted: Every |
| 42 | + constant expression `E` in `Es` is widened to the least primitive |
| 43 | + numeric value type above the types of all expressions in `Es`. Here |
| 44 | + _above_ and _least_ are interpreted according to the ordering given |
| 45 | + below. |
| 46 | + |
| 47 | + |
| 48 | + Double |
| 49 | + / \ |
| 50 | + Long Float |
| 51 | + \ / |
| 52 | + Int |
| 53 | + / \ |
| 54 | + Short Char |
| 55 | + | |
| 56 | + Byte |
| 57 | + |
| 58 | + If these widenings lead to all expressions `Es` having the same type, |
| 59 | + we use the transformed list of expressions instead of `Es`, otherwise |
| 60 | + we use `Es` unchanged. |
| 61 | + |
| 62 | +__Examples:__ |
| 63 | + |
| 64 | + inline val b: Byte = 3 |
| 65 | + inline val s: Short = 33 |
| 66 | + def f(): Int = b + s |
| 67 | + List(b, s, 'a') : List[Int] |
| 68 | + List(b, s, 'a', f()): List[Int] |
| 69 | + List(1.0f, 'a', 0) : List[Float] |
| 70 | + List(1.0f, 1L) : List[Double] |
| 71 | + List(1.0f, 1L, f()) : List[AnyVal] |
| 72 | + |
| 73 | +The expression on the last line has type `List[AnyVal]`, since widenings |
| 74 | +only affect constants. Hence, `1.0f` and `1L` are widened to `Double`, |
| 75 | +but `f()` still has type `Int`. The elements don't agree on a type after |
| 76 | +widening, hence the expressions are left unchanged. |
| 77 | + |
| 78 | + |
0 commit comments