Предположим, я хочу составить список целых чисел. И предположим, ради аргумента, что add
реализован без обобщений.
def add(element: A): List[A]
Ради этого примера предположим, что у нас есть какой-то способ создания «пустого» списка.
def emptyList[A]: List[A] = /* some magic */
Теперь я хочу составить список целых чисел.
(1 to 10).foldRight(emptyList) { (x, acc) => acc.add(x) }
Oops! У нас есть проблемы! Когда я звоню emptyList
, Scala собирается вывести самый общий тип , и, поскольку A
является ковариантным, он будет предполагать Nothing
. Это означает, что я просто попытался добавить целое число в список ничего. Мы могли бы исправить эту проблему с помощью явной сигнатуры типа,
(1 to 10).foldRight(emptyList[Int]) { (x, acc) => acc.add(x) }
Но, на самом деле, это не решает проблему. Это ничего не добавляет к удобочитаемости и просто требует от пользователя дополнительной работы. Реально я должен иметь возможность добавить номер в список ничего. Просто если я решу это сделать, я больше не смогу назвать это списком Nothing
. Следовательно, если мы определим
def add[B >: A](element: B): List[B]
Теперь я могу начать с List[Nothing]
и добавить к нему Int
. То, что я выхожу, больше не List[Nothing]
; это List[Int]
, но я могу это сделать. Если я возьму это List[Int]
и приду позже и добавлю к нему String
, я тоже могу это сделать, но теперь у меня есть практически бесполезный List[Any]
.