You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of another type, *lower type bounds* declare a type to be a supertype of another type. The term `T >: A` expresses that the type parameter `T` or the abstract type `T` refer to a supertype of type `A`.
15
+
While [upper type bounds](upper-type-bounds.html) limit a type to a subtype of another type, *lower type bounds* declare a type to be a supertype of another type. The term `T >: A` expresses that the type parameter `T` or the abstract type `T` refer to a supertype of type `A`. In most cases, `A` will be the type parameter of the class and `T` will be the type parameter of a method.
15
16
16
17
Here is an example where this is useful:
17
18
18
-
```tut
19
-
case class ListNode[T](h: T, t: ListNode[T]) {
20
-
def head: T = h
21
-
def tail: ListNode[T] = t
22
-
def prepend(elem: T): ListNode[T] =
23
-
ListNode(elem, this)
19
+
```tut:fail
20
+
trait Node[+T] {
21
+
def prepend(elem: T)
24
22
}
25
-
```
26
23
27
-
The program above implements a linked list with a prepend operation. Unfortunately, this type is invariant in the type parameter of class `ListNode`; i.e. `ListNode[String]` is not a subtype of `ListNode[Any]`. With the help of [variance annotations](variances.html) we can express such a subtype semantics:
24
+
case class Nil[+T]() extends Node[T] {
25
+
def prepend(elem: T) = ListNode[T](elem, this)
26
+
}
28
27
28
+
case class ListNode[+T](h: T, t: Node[T]) extends Node[T] {
29
+
def prepend(elem: T) = ListNode[T](elem, this)
30
+
def head: T = h
31
+
def tail = t
32
+
}
29
33
```
30
-
case class ListNode[+T](h: T, t: ListNode[T]) { ... }
31
-
```
34
+
This program implements a singly-linked list. `Nil` represents an empty element (i.e. an empty list). `class ListNode` is a node which contains an element of type `T` (`head`) and a reference to the rest of the list (`tail`). The `class Node` and its subtypes are covariant because we have `+T`.
32
35
33
-
Unfortunately, this program does not compile, because a covariance annotation is only possible if the type variable is used only in covariant positions. Since type variable `T` appears as a parameter type of method `prepend`, this rule is broken. With the help of a *lower type bound*, though, we can implement a prepend method where `T` only appears in covariant positions.
36
+
However, this program does _not_ compile because the parameter `elem`in `prepend` is of type `T`, 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.
34
37
35
-
Here is the corresponding code:
38
+
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 `T` as a lower type bound.
36
39
37
40
```tut
38
-
case class ListNode[+T](h: T, t: ListNode[T]) {
39
-
def head: T = h
40
-
def tail: ListNode[T] = t
41
-
def prepend[U >: T](elem: U): ListNode[U] =
42
-
ListNode(elem, this)
41
+
trait Node[+T] {
42
+
def prepend[U >: T](elem: U)
43
43
}
44
-
```
45
44
46
-
_Note:_ the new `prepend` method has a slightly less restrictive type. It allows, for instance, to prepend an object of a supertype to an existing list. The resulting list will be a list of this supertype.
0 commit comments