Дисперсия типов функций по сравнению с интерфейсы

Я пытаюсь понять правила дисперсии для типов функций. Кажется, что они не обрабатывают ввод и вывод одинаково (вплоть до двойственности). Рассмотрим эту программу.

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 реализует, так что я ожидал, что этот актерский состав пройдет (*).

Два вопроса:

  1. Почему не так?
  2. Почему литой производства mk2 ОК?

(*) Для теоретиков: я ожидал, что разработчик продемонстрирует двойственное поведение на входных и выходных позициях конструктора типа функционального пространства. Это неправильно?

2 ответа

У вас есть это:

let mk4 = mk3  : unit -> seq<int>

Что не скомпилируется, приведение произойдет автоматически во входном параметре, но никогда в выходе функции. Это в спецификации, раздел 14.4.2. Неявная вставка гибкости для использования функций и членов.

Это означает, что функциям F#, предполагаемый тип которых включает в себя незапечатанный тип в позиции аргумента, могут передаваться подтипы при вызове без необходимости явных повышений.

Это позволяет определить другую версию функции, которая ограничена подтипом, что и другие случаи, которые вы показываете.

Интересно, что вы можете использовать гибкие типы для определения mk4 следующее:

let mk4 = mk3 : unit -> #seq<int>

Компилируется, но автоматически поднимает тип mk4 вплоть до unit -> int list

Другие вопросы по тегам