После написания этого куска кода
module type TS = sig
type +'a t
end
module T : TS = struct
type 'a t = {info : 'a list}
end
Я понял, что мне нужна info чтобы быть изменчивым.
Я написал тогда:
module type TS = sig
type +'a t
end
module T : TS = struct
type 'a t = {mutable info : 'a list}
end
Но, удивительно,
Type declarations do not match:
type 'a t = { mutable info : 'a list; }
is not included in
type +'a t
Their variances do not agree.
О, я помню, что слышал о дисперсии. Это было что-то о ковариации и контравариантности. Я смелый человек, я найду о своей проблеме в одиночку!
Я нашел эти две интересные статьи (здесь и здесь) и понял!
я могу написать
module type TS = sig
type (-'a, +'b) t
end
module T : TS = struct
type ('a, 'b) t = 'a -> 'b
end
Но потом я удивился. Почему эти изменчивые типы данных являются инвариантными, а не просто ковариантными?
Я имею в виду, что я понимаю, что 'A list может рассматриваться как подтип 'A list ('A | 'B) list потому что мой список не может измениться. То же самое для функции, если у меня есть функция типа 'A | 'B → 'C 'A | 'B → 'C его можно рассматривать как подтип функции типа 'A → 'C | 'D 'A → 'C | 'D потому что, если моя функция может обрабатывать 'A и 'B она может обрабатывать только 'A и если я только возвращаю 'C я точно могу ожидать 'C или 'D (но я получу только 'C ').
Но для массива? Если у меня есть 'A array я не могу рассматривать его как 'A array ('A | 'B) array потому что, если я изменяю элемент в массиве, помещая 'B тогда мой тип массива неправильный, потому что он действительно является ('A | 'B) array а не 'A array больше. Но как насчет ('A | 'B) array как ('A | 'B) array 'A array? Да, это было бы странно, потому что мой массив может содержать 'B но странным образом я думал, что это то же самое, что и функция. Возможно, в конце концов, я не все понял, но я хотел изложить свои мысли здесь, потому что мне потребовалось много времени, чтобы понять это.
TL; DR:
постоянный:
+'aфункции:
-'aизменяемый: инвариант (
'a)? Почему я не могу заставить его быть-'a?