Skip to content

Commit e6f52d9

Browse files
committed
Lower bound in tour includes dangers
1 parent 9d9f096 commit e6f52d9

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

_tour/lower-type-bounds.md

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,26 +42,40 @@ trait Node[+B] {
4242
def prepend[U >: B](elem: U): Node[U]
4343
}
4444

45-
case class ListNode[+B](h: B, t: Node[B]) extends Node[B] {
45+
case class ListNode[+B](head: B, tail: Node[B]) extends Node[B] {
4646
def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
47-
def head: B = h
48-
def tail: Node[B] = t
4947
}
5048

51-
case class Nil[+B]() extends Node[B] {
52-
def prepend[U >: B](elem: U): ListNode[U] = ListNode(elem, this)
49+
object Nil extends Node[Nothing] {
50+
def prepend[U >: Nothing](elem: U): ListNode[U] = ListNode(elem, this)
5351
}
5452
```
5553

54+
We have also simplified `ListNode` to leverage its `case class` fields, and `Nil` to be a singleton object; it is a "node of nothing" because it does not hold an element. The type parameter for `Node` is `B` to suggest we want to store birds at each node.
55+
5656
Now we can do the following:
5757
```scala mdoc
5858
trait Bird
5959
case class AfricanSwallow() extends Bird
6060
case class EuropeanSwallow() extends Bird
6161

62+
val africanSwallows: Node[AfricanSwallow] = ListNode[AfricanSwallow](AfricanSwallow(), Nil)
63+
val swallowsFromAntarctica: Node[Bird] = Nil
6264

63-
val africanSwallowList = ListNode[AfricanSwallow](AfricanSwallow(), Nil())
64-
val birdList: Node[Bird] = africanSwallowList
65-
birdList.prepend(EuropeanSwallow())
65+
// assign swallows to birds
66+
val birds: Node[Bird] = africanSwallows
67+
68+
// add a swallow to birds
69+
val moreBirds = birds.prepend(EuropeanSwallow())
70+
71+
// add disparate swallows together to get birds
72+
val allBirds = africanSwallows.prepend(EuropeanSwallow())
73+
74+
// but this is a mistake! adding a Node to birds widens the type arg too much. -Xlint will warn!
75+
val error = moreBirds.prepend(swallowsFromAntarctica)
6676
```
67-
The `Node[Bird]` can be assigned the `africanSwallowList` but then accept `EuropeanSwallow`s.
77+
The covariant type parameter allows `birds` to get the value of `africanSwallows`.
78+
79+
The type bound on the type parameter for `prepend` allows adding different varieties of swallows and getting a wider type: instead of `Node[AfricanSwallow]`, we get a `Node[Bird]`.
80+
81+
The canary in the coal mine is `-Xlint`, which will warn if the type arg is widened too much.

0 commit comments

Comments
 (0)