我试图理解函数类型的方差规则。似乎他们不会对输入和输出进行相同的处理(达到二元性)。考虑这个计划。
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
好吗?(*)对于理论家:我期望elaborator在函数空间类型构造函数的输入和输出位置上表现出双重行为。这是错的吗?
答案 0 :(得分:4)
你有这个:
let mk4 = mk3 : unit -> seq<int>
哪个不会编译,上传将自动发生在输入参数中,但从不在函数的输出中。这是spec第14.4.2节对函数和成员使用的灵活性的隐式插入。
这意味着推断类型在参数位置包含未密封类型的F#函数可以在调用时传递子类型,而不需要显式的向上转换。
这样就可以定义另一个限制为子类型的函数版本,这是你要显示的其他情况。
答案 1 :(得分:0)
有趣的是,您可以使用灵活类型来定义mk4
,如下所示:
let mk4 = mk3 : unit -> #seq<int>
它会编译但会自动将mk4
的类型提升到unit -> int list