Skip to content

Commit 2063e85

Browse files
authored
Merge pull request #5233 from dotty-staging/fix-intersection
Add spec for intersection types
2 parents 9e2b058 + 4d6aa11 commit 2063e85

File tree

3 files changed

+138
-31
lines changed

3 files changed

+138
-31
lines changed
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
---
2+
layout: doc-page
3+
title: "Intersection Types - More Details"
4+
---
5+
6+
## Syntax
7+
8+
Syntactically, an intersection type `S & T` is similar to an infix type, where
9+
the infix operator is `&`. `&` is treated as a soft keyword. That is, it is a
10+
normal identifier with the usual precedence. But a type of the form `A & B` is
11+
always recognized as an intersection type, without trying to resolve `&`.
12+
13+
```
14+
Type ::= ...| InfixType
15+
InfixType ::= RefinedType {id [nl] RefinedType}
16+
```
17+
18+
## Subtyping Rules
19+
20+
```
21+
T <: A T <: B
22+
----------------
23+
T <: A & B
24+
25+
A <: T
26+
----------------
27+
A & B <: T
28+
29+
B <: T
30+
----------------
31+
A & B <: T
32+
```
33+
34+
From the rules above, we can show that `&` is _commutative_: `A & B <: B & A` for any type `A` and `B`.
35+
36+
```
37+
B <: B A <: A
38+
---------- -----------
39+
A & B <: B A & B <: A
40+
---------------------------
41+
A & B <: B & A
42+
```
43+
44+
In another word, `A & B` is the same type as `B & A`, in the sense that the two types
45+
have the same values and are subtypes of each other.
46+
47+
If `C` is a type constructor, the join `C[A] & C[B]` is simplified by pulling the
48+
intersection inside the constructor, using the following two rules:
49+
50+
- If `C` is covariant, `C[A] & C[B] ~> C[A & B]`
51+
- If `C` is contravariant, `C[A] & C[B] ~> C[A | B]`
52+
53+
When `C` is covariant, `C[A & B] <: C[A] & C[B]` can be derived:
54+
55+
```
56+
A <: A B <: B
57+
---------- ---------
58+
A & B <: A A & B < B
59+
--------------- -----------------
60+
C[A & B] <: C[A] C[A & B] <: C[B]
61+
------------------------------------------
62+
C[A & B] <: C[A] & C[B]
63+
```
64+
65+
When `C` is contravariant, `C[A | B] <: C[A] & C[B]` can be derived:
66+
67+
```
68+
A <: A B <: B
69+
---------- ---------
70+
A <: A | B B < A | B
71+
------------------- ----------------
72+
C[A | B] <: C[A] C[A | B] <: C[B]
73+
--------------------------------------------------
74+
C[A | B] <: C[A] & C[B]
75+
```
76+
77+
## Erasure
78+
79+
The erased type for `S & T` is the erased _glb_ (greatest lower bound) of the
80+
erased type of `S` and `T`. The rules for erasure of intersection types are given
81+
below in pseudocode:
82+
83+
```
84+
|S & T| = glb(|S|, |T|)
85+
86+
glb(JArray(A), JArray(B)) = JArray(glb(A, B))
87+
glb(JArray(T), _) = JArray(T)
88+
glb(_, JArray(T)) = JArray(T)
89+
glb(A, B) = A if A extends B
90+
glb(A, B) = B if B extends A
91+
glb(A, _) = A if A is not a trait
92+
glb(_, B) = B if B is not a trait
93+
glb(A, _) = A
94+
```
95+
96+
In the above, `|T|` means the erased type of `T`, `JArray` refers to
97+
the type of Java Array.
98+
99+
## Relationship with Compound Type (`with`)
100+
101+
Intersection types `A & B` replace compound types `A with B` in Scala 2. For the
102+
moment, the syntax `A with B` is still allowed and interpreted as `A & B`, but
103+
its usage as a type (as opposed to in a `new` or `extends` clause) will be
104+
deprecated and removed in the future.

docs/docs/reference/intersection-types.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ title: "Intersection Types"
55

66
Used on types, the `&` operator creates an intersection type.
77

8+
## Type Checking
9+
10+
The type `S & T` represents values that are of the type `S` and `T` at the same time.
11+
812
```scala
913
trait Resettable {
1014
def reset(): this.type
@@ -19,19 +23,14 @@ def f(x: Resettable & Growable[String]) = {
1923
```
2024

2125
The value `x` is required to be _both_ a `Resettable` and a
22-
`Growable[String]`. Intersection types `A & B` replace compound types
23-
`A with B` in Scala 2. For the moment, `A with B` is still allowed, but
24-
its usage as a type (as opposed to in a `new` or `extends` clause) will be deprecated and removed in the future.
26+
`Growable[String]`.
2527

26-
The members of an intersection type `A & B` are all the members of `A`
27-
and all the members of `B`. For instance `Resettable & Growable[String]`
28+
The members of an intersection type `A & B` are all the members of `A` and all
29+
the members of `B`. For instance `Resettable & Growable[String]`
2830
has member methods `reset` and `add`.
2931

30-
`&` is _commutative_: `A & B` is the same type as `B & A`, in that sense that the two types
31-
have the same values and are subtypes of each other.
32-
33-
If a member appears in both `A` and `B`, its type in `A & B` is the
34-
intersection of its type in `A` and its type in `B`. For instance, assume the definitions:
32+
If a member appears in both `A` and `B`, its type in `A & B` is the intersection
33+
of its type in `A` and its type in `B`. For instance, assume the definitions:
3534

3635
```scala
3736
trait A {
@@ -63,3 +62,6 @@ class C extends A with B {
6362
def children: List[A & B] = ???
6463
}
6564
```
65+
66+
67+
[More details](./type-lambdas-spec.html)
Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,36 @@
11
package intersectionTypes
22

33
object t1 {
4+
trait Resetable {
5+
def reset(): this.type
6+
}
47

5-
trait Resetable {
6-
def reset(): this.type
7-
}
8-
trait Growable[T] {
9-
def add(x: T): this.type
10-
}
11-
def f(x: Resetable & Growable[String]) = {
12-
x.reset()
13-
x.add("first")
14-
}
8+
trait Growable[T] {
9+
def add(x: T): this.type
10+
}
1511

12+
def f(x: Resetable & Growable[String]) = {
13+
x.reset()
14+
x.add("first")
15+
}
1616
}
1717

1818
object t2 {
1919

20-
trait A {
21-
def children: List[A]
22-
}
23-
trait B {
24-
def children: List[B]
25-
}
26-
val x: A & B = new C
27-
val ys: List[A & B] = x.children
20+
trait A {
21+
def children: List[A]
22+
}
2823

24+
trait B {
25+
def children: List[B]
26+
}
2927

30-
class C extends A with B {
31-
def children: List[A & B] = ???
32-
}
28+
val x: A & B = new C
29+
val ys: List[A & B] = x.children
3330

31+
class C extends A with B {
32+
def children: List[A & B] = ???
33+
}
3434

35+
val z: B & A = x // commutative
3536
}

0 commit comments

Comments
 (0)