Конструктор типа F # не действует как функция

Если я определяю такой тип:

type Foo = Items of seq<int>

Я могу создать Foo следующим образом:

Items [1;2;3]

Однако следующее не работает:

[1;2;3] |> Items

Сообщение об ошибке:

Type mismatch. Expecting a
    int list -> 'a    
but given a
    seq<int> -> Foo

Разве компилятор не может преобразовать int list в seq<int>? Если конструктор Items был нормальной функцией, я мог бы вызвать его в любом случае:

let length ints = Seq.length ints
printfn "%A" (length [1;2;3])
printfn "%A" ([1;2;3] |> length)

Ответ 1

Это ковариационная проблема. Функция конструктора типов Items равна seq<int> -> Items, но ей присваивается List<int>, который вам придется явно понижать, поскольку F # не выполняет автоматическое преобразование подтипа.

type Foo = Items of int list
[1;2;3] |> Items //compiles

или используйте соответствующий модуль

type Foo = Items of int seq
[1;2;3] |> Seq.ofList |> Items //compiles

Ответ 2

Это скорее предположение, чем ответ, но я подозреваю, что проблема может быть связана с аналогичным поведением на С# в том, что конструкторы не могут иметь параметры типа. По умолчанию я понимаю, что функции F # являются полностью родовыми и становятся специализированными только для аннотаций и выводов типов. Если неспособность конструкторов иметь параметры типа - это что-то, что запекается в CLR или .NET вообще, то это может объяснить, почему конструкторы типа F # не могут следовать этому же родовому по умолчанию, как это делается для функций.

Ответ 3

Если вы измените свой код так:

> type Foo = Items of seq<int>;;
> Items;;
val it : arg0:seq<int> -> Foo = <fun:[email protected]>

> let length (ints: int seq) = Items ints;;
> length;;
val it : (seq<int> -> Foo) = <fun:[email protected]>

Проблема становится еще более очевидной. Почти идентичная подпись типа, но все та же проблема. Я уверен, что это ошибка с использованием конструкторов в качестве первоклассных функций.