F#类型构造函数不像函数那样

时间:2017-02-01 20:53:40

标签: f# discriminated-union

如果我定义这样的类型:

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)

3 个答案:

答案 0 :(得分:3)

这是一个协方差问题。 类型构造函数Itemsseq<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

答案 1 :(得分:2)

这更像是一个猜测而不是答案,但我怀疑这个问题可能与C#中的类似行为有关,因为构造函数不能有类型参数。默认情况下,我理解F#函数是完全通用的,只能通过类型注释和推理变得专业化。如果构造函数无法具有类型参数,那么通常会将其引入CLR或.NET,那么它可以解释为什么F#类型构造函数可能无法按照默认行为执行与函数相同的通用操作。

答案 2 :(得分:1)

如果您将代码更改为:

> type Foo = Items of seq<int>;;
> Items;;
val it : arg0:seq<int> -> Foo = <fun:clo@18-5>

> let length (ints: int seq) = Items ints;;
> length;;
val it : (seq<int> -> Foo) = <fun:it@20-3>

问题变得更加明显。几乎相同的类型签名但仍然是同样的问题。我很确定这是使用构造函数作为第一类函数的错误。