Как определить дисперсию параметра типа?

Вдохновленный Реальные примеры совпадения и контравариантности в Scala Я подумал, что лучший вопрос:

При разработке библиотеки существует ли определенный набор вопросов, которые вы должны задать себе при определении того, должен ли параметр типа быть ковариантным или контравариантным? Или вы должны сделать все неизменным, а затем изменить по мере необходимости?

Ответ 1

Ну, просто, это имеет смысл? Подумайте о замене Лискова.

ковариационной

Если A <: B, имеет смысл передать a C[A], где ожидается a C[B]? Если да, сделайте это C[+T]. Классическим примером является неизменяемый List, где a List[A] можно передать любому ожидающему List[B], предполагая, что A является подтипом B.

Два примера счетчика:

Переменные последовательности являются инвариантными, так как в противном случае возможно иметь нарушения безопасности типа (на самом деле Java-вариант Array уязвим только для таких вещей, поэтому он инвариантен в Scala).

Неизменяемый Set инвариантен, хотя его методы очень похожи на методы неизменяемого Seq. Разница заключается в contains, который набирается на множествах и нетипизируется (т.е. Принимает Any) на последовательности. Таким образом, несмотря на то, что в противном случае было бы возможно сделать его ковариантным, стремление к повышенной безопасности типов по конкретному методу привело к выбору инвариантности по сравнению с коразмерностью.

Контра-дисперсия

Если A <: B, имеет смысл передать a C[B], где ожидается a C[A]? Если да, сделайте это C[-T]. Классическим примером является Ordering. Хотя некоторые несвязанные технические проблемы не позволяют Ordering быть противоречивыми, интуитивно понятно, что все, что может заказать суперкласс класса A, также может заказать A. Из этого следует, что Ordering[B], который упорядочивает все элементы типа B, супертип A, может быть передан чему-то ожидающему Ordering[A].

Пока Scala Ordering не является контравариантным, Scalaz Order является противоречивым, как ожидалось. Другим примером из Scalaz является его Equal.

Смешанная разность?

Наиболее заметным примером смешанной дисперсии в Scala является Function1 (и 2, 3 и т.д.). Это противоречиво в параметре, который он получает, и ко-вариант в том, что он возвращает. Обратите внимание, однако, что Function1 - это то, что используется для многих закрытий, а замыкания используются во многих местах, и эти места обычно используются там, где Java использует (или использует) классы Single Abstract Method.

Итак, если у вас есть ситуация, когда применяется класс SAM, это, скорее всего, место для смешанной противоречивости и ковариации.