如果我定义这样的类型:
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)
这是一个协方差问题。类型构造函数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
这与其说是答案,不如说是猜测,但我怀疑该问题可能与 C# 中的类似行为有关,因为构造函数不能具有类型参数。默认情况下,我的理解是 F# 函数是完全通用的,只有通过类型注释和推理才能变得专用。如果构造函数无法具有类型参数是一般烘焙到 CLR 或 .NET 中的原因,那么它可能会解释为什么 F# 类型构造函数可能无法遵循与函数相同的默认行为。
如果您将代码更改为如下所示:
> 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>
这个问题变得更加明显。 几乎相同的类型签名,但仍然是相同的问题。 我很确定这是使用构造函数作为一等函数的错误。