После написания этого куска кода
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
?