Канонический пример исправления другого ковариантного класса выглядит следующим образом:
abstract class Stack[+A] {
def push[B >: A]( x: B ) : Stack[B]
def top: A
def pop: Stack[A]
Теперь, если я удаляю неявную ковариацию и вручную аннотирую класс, я получаю следующее:
abstract class Stack[A] {
def push[B >: A]( x: B ) : Stack[B]
def top [B >: A]: B
def pop [B >: A]: Stack[B]
def cast[B >: A]: Stack[B]
}
(Быстрое доказательство корректности: Stack[A]
имеет элементы типа A
, поэтому, если B
является более допустимым, мы всегда можем вернуть A
вместо B
. Аналогично, для любого стека A
, мы можем использовать его вместо стека B
, если B может принять A.)
Но теперь я немного запутался: где-то здесь должна быть противоположность, но все подтипыотношения здесь кажутся одинаковыми.Что случилось?
Чтобы уточнить, мы определяем контравариантный функтор F
такой, что (a -> b) -> (F b -> F a)
.В частности, функтор F a
на a -> r
является контравариантным, как и (a -> b) -> ((b -> r) -> (a -> r))
просто путем составления функций.С точки зрения формализма, я ожидаю, что стрелки будут переворачиваться.Таким образом, с чисто синтаксической точки зрения, я запутываюсь, когда стрелки не переворачиваются (но так должно быть!). Мой аннотированный способ написания Scala - просто «естественное» представление контравариантности функций, такое, что вы даже не замечаетеЭто?Мой абстрактный класс не так?Что-то вводит в заблуждение во второй презентации?