Skip to content

Commit f69fb47

Browse files
committed
Normalize type param and add clarity per review
1 parent 640c86c commit f69fb47

File tree

1 file changed

+16
-15
lines changed

1 file changed

+16
-15
lines changed

_tour/lower-type-bounds.md

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,33 +16,30 @@ While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of a
1616
Here is an example where this is useful:
1717

1818
```scala mdoc:fail
19-
trait List[+B] {
20-
def prepend(elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
19+
trait List[+A] {
20+
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
2121
}
2222

23-
case class NonEmptyList[+B](head: B, tail: List[B]) extends List[B]
23+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
2424

2525
object Nil extends List[Nothing]
2626
```
2727

28-
This program implements a singly-linked list. `Nil` represents an empty list with no elements. `class NonEmptyList` is a node which contains an element of type `B` (`head`) and a reference to the rest of the list (`tail`). The `trait List` and its subtypes are covariant because we have `+B`.
28+
This program implements a singly-linked list. `Nil` represents an empty list with no elements. `class NonEmptyList` is a node which contains an element of type `A` (`head`) and a reference to the rest of the list (`tail`). The `trait List` and its subtypes are covariant because we have `+A`.
2929

30-
However, this program does _not_ compile because the parameter `elem` in `prepend` is of type `B`, which we declared *co*variant. This doesn't work because functions are *contra*variant in their parameter types and *co*variant in their result types.
30+
However, this program does _not_ compile because the parameter `elem` in `prepend` is of type `A`, which we declared *co*variant. This doesn't work because functions are *contra*variant in their parameter types and *co*variant in their result types.
3131

32-
To fix this, we need to flip the variance of the type of the parameter `elem` in `prepend`. We do this by introducing a new type parameter `U` that has `B` as a lower type bound.
32+
To fix this, we need to flip the variance of the type of the parameter `elem` in `prepend`. We do this by introducing a new type parameter `B` that has `A` as a lower type bound.
3333

3434
```scala mdoc
35-
trait List[+B] {
36-
def prepend[U >: B](elem: U): NonEmptyList[U] = NonEmptyList(elem, this)
35+
trait List[+A] {
36+
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
3737
}
3838

39-
case class NonEmptyList[+B](head: B, tail: List[B]) extends List[B]
39+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
4040

4141
object Nil extends List[Nothing]
4242
```
43-
44-
The type parameter for `List` is `B` to suggest we want to keep lists of birds.
45-
4643
Now we can do the following:
4744
```scala mdoc
4845
trait Bird
@@ -51,21 +48,25 @@ case class EuropeanSwallow() extends Bird
5148

5249
val africanSwallows: List[AfricanSwallow] = Nil.prepend(AfricanSwallow())
5350
val swallowsFromAntarctica: List[Bird] = Nil
51+
val someBird: Bird = EuropeanSwallow()
5452

5553
// assign swallows to birds
5654
val birds: List[Bird] = africanSwallows
5755

56+
// add some bird to swallows, `B` is `Bird`
57+
val someBirds = africanSwallows.prepend(someBird)
58+
5859
// add a swallow to birds
5960
val moreBirds = birds.prepend(EuropeanSwallow())
6061

61-
// add disparate swallows together to get birds
62+
// add disparate swallows together, `B` is `Bird` because that is the supertype common to both swallows
6263
val allBirds = africanSwallows.prepend(EuropeanSwallow())
6364

6465
// but this is a mistake! adding a list of birds widens the type arg too much. -Xlint will warn!
65-
val error = moreBirds.prepend(swallowsFromAntarctica)
66+
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
6667
```
6768
The covariant type parameter allows `birds` to get the value of `africanSwallows`.
6869

6970
The type bound on the type parameter for `prepend` allows adding different varieties of swallows and getting a wider type: instead of `List[AfricanSwallow]`, we get a `List[Bird]`.
7071

71-
The canary in the coal mine is `-Xlint`, which will warn if the type arg is widened too much.
72+
Use `-Xlint` to warn if the inferred type arg is widened too much.

0 commit comments

Comments
 (0)