You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/match-types-spec.md
+117-2Lines changed: 117 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -283,8 +283,123 @@ At the top level, `variance = 1` and `scrutIsWidenedAbstract = false`.
283
283
284
284
#### Disjointness
285
285
286
-
This proposal does not affect the check for provably disjoint types in match types.
287
-
If a case is legal and does not match, the existing disjointness check is used to decide whether we can move on to the next case.
286
+
This proposal initially did not include a discussion of disjointness.
287
+
After initial review, it became apparent that it should also provide a specification for the "provably disjoint" test involved in match type reduction.
288
+
Additional study revealed that, while *specifiable*, the current algorithm is very ad hoc and severely lacks desirable properties such as preservation along subtyping (see towards the end of this section).
289
+
290
+
Therefore, this proposal now also includes a proposed specification for "provably disjoint".
291
+
To the best of our knowledge, it is strictly stronger than what is currently implemented, with one exception.
292
+
293
+
The current implementation can prove that `{ type A = Int }` is provably disjoint from `{ type A = Boolean }`.
294
+
However, it is not able to prove disjointness between any of the following:
295
+
296
+
*`{ type A = Int }` and `{ type A = Boolean; type B = String }` (adding another type member)
297
+
*`{ type A = Int; type B = Int }` and `{ type B = String; type A = String }` (switching the order of type members)
298
+
*`{ type A = Int }` and class `C` that defines a member `type A = String`.
299
+
300
+
Therefore, we drop the very ad hoc case of one-to-one type member refinements.
301
+
302
+
On to the specification.
303
+
304
+
A scrutinee `X` is *provably disjoint* from a pattern `P` iff it is probably disjoint from the type `P'` obtained by replacing every type capture in `P` by a wildcard type argument with the same bounds.
305
+
306
+
We note `X ⋔ Y` to say that `X` and `Y` are provably disjoint.
307
+
Intuitively, that notion is based on the following properties of the Scala language:
308
+
309
+
* Single inheritance of classes
310
+
* Final classes cannot be extended
311
+
* Sealed traits have a known set of direct children
312
+
* Constant types with distinct values are nonintersecting
313
+
* Singleton paths to distinct `enum` case values are nonintersecting
314
+
315
+
However, a precise definition of provably-disjoint is complicated and requires some helpers.
316
+
We start with the notion of "simple types", which are a minimal subset of Scala types that capture the concepts mentioned above.
317
+
318
+
A "simple type" is one of:
319
+
320
+
*`Nothing`
321
+
*`AnyKind`
322
+
*`p.C[...Xi]` a possibly parameterized class type, where `p` and `...Xi` are arbitrary types (not just simple types)
323
+
*`c` a literal type
324
+
*`p.C.x` where `C` is an `enum` class and `x` is one of its value `case`s
325
+
*`X₁ & X₂` where `X₁` and `X₂` are both simple types
326
+
*`X₁ | X₂` where `X₁` and `X₂` are both simple types
327
+
328
+
We define `⌈X⌉` a function from a full Scala type to a simple type.
329
+
Intuitively, it returns the "smallest" simple type that is a supertype of `X`.
330
+
It is defined as follows:
331
+
332
+
*`⌈X⌉ = X` if `X` is a simple type
333
+
*`⌈X⌉ = ⌈U⌉` if `X` is a stable type but not a simple type and its underlying type is `U`
334
+
*`⌈X⌉ = ⌈H⌉` if `X` is a non-class type designator with upper bound `H`
335
+
*`⌈X⌉ = AnyKind` if `X` is a type lambda
336
+
*`⌈X⌉ = ⌈Y⌉` if `X` is a match type that reduces to `Y`
337
+
*`⌈X⌉ = ⌈H⌉` if `X` is a match type that does not reduce and `H` is its upper bound
338
+
*`⌈X₁ & X₂⌉ = ⌈X₁⌉ & ⌈X₂⌉`
339
+
*`⌈X₁ | X₂⌉ = ⌈X₁⌉ | ⌈X₂⌉`
340
+
341
+
The following properties hold about `⌈X⌉` (we have paper proofs for those):
342
+
343
+
*`X <: ⌈X⌉` for all type `X`.
344
+
* If `S <: T`, and the subtyping derivation does not use the "lower-bound rule" of `<:` anywhere, then `⌈S⌉ <: ⌈T⌉`.
345
+
346
+
The "lower-bound rule" states that `S <: T` if `T = q.X `and `q.X` is a non-class type designator and `S <: L` where `L` is the lower bound of the underlying type definition of `q.X`".
347
+
That rule is known to break transitivy of subtyping in Scala already.
348
+
349
+
Second, we define the relation `⋔` on *classes* (including traits and hidden classes of objects) as:
350
+
351
+
*`C ⋔ D` if `C ∉ baseClasses(D)` and `D` is `final`
352
+
*`C ⋔ D` if `D ∉ baseClasses(C)` and `C` is `final`
353
+
*`C ⋔ D` if there exists `class`es `C' ∈ baseClasses(C)` and `D' ∈ baseClasses(D)` such that `C' ∉ baseClasses(D')` and `D' ∉ baseClasses(C')`.
354
+
*`C ⋔ D` if `C` is `sealed` without anonymous child and `Ci ⋔ D` for all direct children `Ci` of `C`
355
+
*`C ⋔ D` if `D` is `sealed` without anonymous child and `C ⋔ Di` for all direct children `Di` of `D`
356
+
357
+
We can now define `⋔` for *types*.
358
+
359
+
For arbitrary types `X` and `Y`, we define `X ⋔ Y` as `⌈X⌉ ⋔ ⌈Y⌉`.
360
+
361
+
Two simple types `S` and `T` are provably disjoint if there is a finite derivation tree for `S ⋔ T` using the following rules.
362
+
Most rules go by pair, which makes the whole relation symmetric:
363
+
364
+
*`Nothing` is disjoint from everything (including itself):
365
+
*`Nothing ⋔ T`
366
+
*`S ⋔ Nothing`
367
+
* A union type is disjoint from another type if both of its parts are disjoint from that type:
368
+
*`S ⋔ T1 | T2` if `S ⋔ T1` and `S ⋔ T2`
369
+
*`S1 | S2 ⋔ T` if `S1 ⋔ T` and `S2 ⋔ T`
370
+
* An intersection type is disjoint from another type if at least one of its parts is disjoint from that type:
371
+
*`S ⋔ T1 & T2` if `S ⋔ T1` or `S ⋔ T2`
372
+
*`S1 & S2 ⋔ T` if `S1 ⋔ T` or `S1 ⋔ T`
373
+
* An `enum` value case is disjoint from any other `enum` value case (identified by either not being in the same `enum` class, or having a different name):
374
+
*`p.C.x ⋔ q.D.y` if `C != D` or `x != y` (recall: these are enum value cases)
375
+
* Two literal types are disjoint if they are different:
376
+
*`c ⋔ d` if `c != d` (where they are literal types)
377
+
* An `enum` value case is always disjoint from a literal type:
378
+
*`c ⋔ q.D.y`
379
+
*`p.C.x ⋔ d`
380
+
* An `enum` value case or a constant is disjoint from a class type if it does not extend that class (because it's essentially final):
381
+
*`p.C.x ⋔ q.D[...Ti]` if `baseType(p.C.x, D)` is not defined
382
+
*`p.C[...Si] ⋔ q.D.y` if `baseType(q.D.y, C)` is not defined
383
+
*`c ⋔ q.D[...Ti]` if `baseType(c, D)` is not defined
384
+
*`p.C[...Si] ⋔ d` if `baseType(d, C)` is not defined
385
+
* Two class types are disjoint if the classes themselves are disjoint, or if there exist a common super type with conflicting type arguments.
386
+
*`p.C[...Si] ⋔ q.D[...Ti]` if `C ⋔ D`
387
+
*`p.C[...Si] ⋔ q.D[...Ti]` if there exists a class `E` such that `baseType(p.C[...Si], E) = a.E[...Ai]` and `baseType(q.D[...Ti], E) = b.E[...Bi]` and there exists a pair `(Ai, Bi)` such that
388
+
*`Ai ⋔ Bi` and it is in invariant position, or
389
+
*`Ai ⋔ Bi` and it is in covariant position and there exists a field of that type parameter in `E`
390
+
391
+
It is worth noting that this definition disregards prefixes entirely.
392
+
`p.C` and `q.C` are never provably disjoint, even if `p` could be proven disjoint from `q`.
393
+
394
+
We have a proof sketch of the following property for `⋔`:
395
+
396
+
* If `S <: T` and `T ⋔ U`, then `S ⋔ U`.
397
+
398
+
This is a very desirable property, which does not hold at all for the current implementation of match types.
399
+
It means that if we make the scrutinee of a match type more precise (a subtype) through substitution, and the match type previously reduced, then the match type will still reduce to the same case.
400
+
401
+
Note: if `⋔` were a "true" disjointness relationship, and not a *provably*-disjoint relationship, that property would trivially hold based on elementary set theoretic properties.
402
+
It would amount to proving that if `S ⊆ T` and `T ⋂ U = ∅`, then `S ⋂ U = ∅`.
0 commit comments