Skip to content

Commit de06e87

Browse files
authored
Add code tabs for _tour/lower-type-bounds (#2549)
1 parent 11c6ca3 commit de06e87

File tree

1 file changed

+35
-0
lines changed

1 file changed

+35
-0
lines changed

_tour/lower-type-bounds.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of a
1515

1616
Here is an example where this is useful:
1717

18+
{% tabs upper-type-bounds_1 class=tabs-scala-version %}
19+
{% tab 'Scala 2' for=upper-type-bounds_1 %}
1820
```scala mdoc:fail
1921
trait List[+A] {
2022
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
@@ -24,13 +26,27 @@ case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
2426

2527
object Nil extends List[Nothing]
2628
```
29+
{% endtab %}
30+
{% tab 'Scala 3' for=upper-type-bounds_1 %}
31+
```scala
32+
trait List[+A]:
33+
def prepend(elem: A): NonEmptyList[A] = NonEmptyList(elem, this)
34+
35+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
36+
37+
object Nil extends List[Nothing]
38+
```
39+
{% endtab %}
40+
{% endtabs %}
2741

2842
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`.
2943

3044
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.
3145

3246
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.
3347

48+
{% tabs upper-type-bounds_2 class=tabs-scala-version %}
49+
{% tab 'Scala 2' for=upper-type-bounds_2 %}
3450
```scala mdoc
3551
trait List[+A] {
3652
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
@@ -40,7 +56,23 @@ case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
4056

4157
object Nil extends List[Nothing]
4258
```
59+
{% endtab %}
60+
{% tab 'Scala 3' for=upper-type-bounds_2 %}
61+
```scala
62+
trait List[+A]:
63+
def prepend[B >: A](elem: B): NonEmptyList[B] = NonEmptyList(elem, this)
64+
65+
case class NonEmptyList[+A](head: A, tail: List[A]) extends List[A]
66+
67+
object Nil extends List[Nothing]
68+
```
69+
{% endtab %}
70+
{% endtabs %}
71+
4372
Now we can do the following:
73+
74+
{% tabs upper-type-bounds_3 %}
75+
{% tab 'Scala 2 and 3' for=upper-type-bounds_3 %}
4476
```scala mdoc
4577
trait Bird
4678
case class AfricanSwallow() extends Bird
@@ -65,6 +97,9 @@ val allBirds = africanSwallows.prepend(EuropeanSwallow())
6597
// but this is a mistake! adding a list of birds widens the type arg too much. -Xlint will warn!
6698
val error = moreBirds.prepend(swallowsFromAntarctica) // List[Object]
6799
```
100+
{% endtab %}
101+
{% endtabs %}
102+
68103
The covariant type parameter allows `birds` to get the value of `africanSwallows`.
69104

70105
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]`.

0 commit comments

Comments
 (0)