Дисперсия типов функций по сравнению с интерфейсы
Я пытаюсь понять правила дисперсии для типов функций. Кажется, что они не обрабатывают ввод и вывод одинаково (вплоть до двойственности). Рассмотрим эту программу.
let mk1 s = s |> Seq.iter (fun _ -> ())
// val mk1 : s:seq<'a> -> unit
let mk2 = mk1 : list<int> -> unit // Ok.
let mk3 () = [1]
// val mk3 : unit -> int list
let mk4 = mk3 : unit -> seq<int> // Type mismatch.
Это ошибка:
Type mismatch. Expecting a
unit -> seq<int>
but given a
unit -> int list
The type 'seq<int>' does not match the type 'int list'
Я так понимаю seq<int>
это тип интерфейса, который int list
реализует, так что я ожидал, что этот актерский состав пройдет (*).
Два вопроса:
- Почему не так?
- Почему литой производства
mk2
ОК?
(*) Для теоретиков: я ожидал, что разработчик продемонстрирует двойственное поведение на входных и выходных позициях конструктора типа функционального пространства. Это неправильно?
2 ответа
У вас есть это:
let mk4 = mk3 : unit -> seq<int>
Что не скомпилируется, приведение произойдет автоматически во входном параметре, но никогда в выходе функции. Это в спецификации, раздел 14.4.2. Неявная вставка гибкости для использования функций и членов.
Это означает, что функциям F#, предполагаемый тип которых включает в себя незапечатанный тип в позиции аргумента, могут передаваться подтипы при вызове без необходимости явных повышений.
Это позволяет определить другую версию функции, которая ограничена подтипом, что и другие случаи, которые вы показываете.
Интересно, что вы можете использовать гибкие типы для определения mk4
следующее:
let mk4 = mk3 : unit -> #seq<int>
Компилируется, но автоматически поднимает тип mk4
вплоть до unit -> int list