Skip to content

Commit 693f2e6

Browse files
committed
Add spec for intersection types
1 parent b5d9ef3 commit 693f2e6

File tree

3 files changed

+142
-60
lines changed

3 files changed

+142
-60
lines changed
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
---
2+
layout: doc-page
3+
title: "Intersection Types - More Details"
4+
---
5+
6+
## Syntax
7+
8+
Syntactically, an intersection type is similar to an infix type,
9+
where the infix operator is `&`.
10+
11+
```
12+
Type ::= ...| InfixType
13+
InfixType ::= RefinedType {id [nl] RefinedType}
14+
RefinedType ::= WithType {[nl] Refinement}
15+
WithType ::= AnnotType {‘with’ AnnotType}
16+
```
17+
18+
## Type Checking
19+
20+
The type `S & T` represents values that are of the type `S` and `T` at the same time.
21+
22+
```scala
23+
trait Resettable {
24+
def reset(): this.type
25+
}
26+
trait Growable[T] {
27+
def add(x: T): this.type
28+
}
29+
def f(x: Resettable & Growable[String]) = {
30+
x.reset()
31+
x.add("first")
32+
}
33+
```
34+
35+
The value `x` is required to be _both_ a `Resettable` and a
36+
`Growable[String]`.
37+
38+
The members of an intersection type `A & B` are all the members of `A` and all
39+
the members of `B`. For instance `Resettable & Growable[String]`
40+
has member methods `reset` and `add`.
41+
42+
If a member appears in both `A` and `B`, its type in `A & B` is the intersection
43+
of its type in `A` and its type in `B`. For instance, assume the definitions:
44+
45+
```scala
46+
trait A {
47+
def children: List[A]
48+
}
49+
trait B {
50+
def children: List[B]
51+
}
52+
val x: A & B = new C
53+
val ys: List[A & B] = x.children
54+
```
55+
56+
The type of `children` in `A & B` is the intersection of `children`'s
57+
type in `A` and its type in `B`, which is `List[A] & List[B]`. This
58+
can be further simplified to `List[A & B]` because `List` is
59+
covariant.
60+
61+
An intersection type `A & B` may not be inhabited, e.g. `Int & String` is not inhabited.
62+
`A & B` is just a type that represents a set of requirements for
63+
values of the type. At the point where a value is _constructed_, one
64+
must make sure that all inherited members are correctly defined.
65+
So if one defines a class `C` that inherits `A` and `B`, one needs
66+
to give at that point a definition of a `children` method with the required type.
67+
68+
```scala
69+
class C extends A with B {
70+
def children: List[A & B] = ???
71+
}
72+
```
73+
74+
## Subtyping Rules
75+
76+
```
77+
T <: A T <: B
78+
----------------
79+
T <: A & B
80+
81+
A <: T
82+
----------------
83+
A & B <: T
84+
85+
B <: T
86+
----------------
87+
A & B <: T
88+
```
89+
90+
From the rules above, we can show that `&` is _commutative_: `A & B <: B & A`.
91+
In another word, `A & B` is the same type as `B & A`, in that sense that the two types
92+
have the same values and are subtypes of each other.
93+
94+
## Erasure
95+
96+
The erased type for `S & T` is the erased _glb_ (greatest lower bound) of the
97+
erased type of `S` and `T`. The rules for erasure of intersection types are given
98+
below in pseudocode:
99+
100+
```
101+
|S & T| = glb(|S|, |T|)
102+
103+
glb(JArray(A), JArray(B)) = JArray(glb(A, B))
104+
glb(JArray(T), _) = JArray(T)
105+
glb(_, JArray(T)) = JArray(T)
106+
glb(A, B) = A if A extends B
107+
glb(A, B) = B if B extends A
108+
glb(A, _) = A if A is not a trait
109+
glb(_, B) = B if B is not a trait
110+
glb(A, _) = A
111+
```
112+
113+
## Relationship with Compound Type (`with`)
114+
115+
Intersection types `A & B` replace compound types `A with B` in Scala 2. For the
116+
moment, the syntax `A with B` is still allowed and interpreted as `A & B`, but
117+
its usage as a type (as opposed to in a `new` or `extends` clause) will be
118+
deprecated and removed in the future.

docs/docs/reference/intersection-types.md

Lines changed: 2 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,47 +19,10 @@ def f(x: Resettable & Growable[String]) = {
1919
```
2020

2121
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.
22+
`Growable[String]`.
2523

2624
The members of an intersection type `A & B` are all the members of `A`
2725
and all the members of `B`. For instance `Resettable & Growable[String]`
2826
has member methods `reset` and `add`.
2927

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:
35-
36-
```scala
37-
trait A {
38-
def children: List[A]
39-
}
40-
trait B {
41-
def children: List[B]
42-
}
43-
val x: A & B = new C
44-
val ys: List[A & B] = x.children
45-
```
46-
47-
The type of `children` in `A & B` is the intersection of `children`'s
48-
type in `A` and its type in `B`, which is `List[A] & List[B]`. This
49-
can be further simplified to `List[A & B]` because `List` is
50-
covariant.
51-
52-
One might wonder how the compiler could come up with a definition for
53-
`children` of type `List[A & B]` since all its is given are `children`
54-
definitions of type `List[A]` and `List[B]`. The answer is it does not
55-
need to. `A & B` is just a type that represents a set of requirements for
56-
values of the type. At the point where a value is _constructed_, one
57-
must make sure that all inherited members are correctly defined.
58-
So if one defines a class `C` that inherits `A` and `B`, one needs
59-
to give at that point a definition of a `children` method with the required type.
60-
61-
```scala
62-
class C extends A with B {
63-
def children: List[A & B] = ???
64-
}
65-
```
28+
[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)