pom*_*a89 9 generics f# types inference seq
我想我不太明白F#如何推断序列表达式中的类型以及为什么类型不能正确识别,即使我直接从"seq"指定了元素的类型.
在下面的F#代码中,我们有一个基类A和两个派生类,B和C:
type A(x) =
member a.X = x
type B(x) =
inherit A(x)
type C(x) =
inherit A(x)
Run Code Online (Sandbox Code Playgroud)
如果我尝试用简单的序列表达式"产生"它们的实例,我会得到两个错误:
// Doesn't work, but it makes sense.
let testSeq = seq {
yield A(0)
yield B(1) // Error, expected type: A
yield C(2) // Error, expected type: A
}
Run Code Online (Sandbox Code Playgroud)
这是有道理的,因为推断"常见"类型(界面,我认为,可以使这项工作更加困难)可能并不那么简单.但是,可以使用安全转换来修复这些错误:
// Works fine :)
let testSeqWithCast = seq {
yield A(0)
yield B(1) :> A
yield C(2) :> A
}
Run Code Online (Sandbox Code Playgroud)
如果我不想使用演员怎么办?我试图直接从"seq"指定序列类型,但事情似乎不起作用:
// Should work, I think...
let testGenSeq = seq<A> {
yield A(0)
yield B(1) // Error, expected type: A
yield C(2)
}
Run Code Online (Sandbox Code Playgroud)
所以,我的问题是:有没有办法避免演员表?如果没有,是否有一个原因,即使指定类型不会使代码工作?
我尝试通过以下链接挖掘:
http://msdn.microsoft.com/en-us/library/dd233209.aspx http://lorgonblog.wordpress.com/2009/10/25/overview-of-type-inference-in-f/
但我发现没有什么有用的......
提前感谢您提出的任何答案:)
这是一个很好的问题,答案可能比你到目前为止所得到的答案更复杂.例如,这确实有效:
let l : A list = [A(0); B(1); C(2)]
Run Code Online (Sandbox Code Playgroud)
但这个看似类似的代码不会:
let s : A seq = seq { yield A(0); yield B(1); yield C(2) }
Run Code Online (Sandbox Code Playgroud)
原因其实非常微妙.第二种情况涉及到基本上更复杂的版本:
let s : A seq =
Seq.append (Seq.singleton (A(0)))
(Seq.append (Seq.singleton (B(1)))
(Seq.singleton (C(2)))))
Run Code Online (Sandbox Code Playgroud)
所以有什么问题?最终,问题是Seq.singleton具有泛型类型'x -> 'x seq,但我们希望在第二次调用中传递B并返回一个A seq(通过隐式地向上转换实例).F#将隐式地将一个具体类型的函数输入向上转换为具体的基类型(例如,如果Seq.singleton签名A -> A seq我们可以传递一个B!).不幸的是,通用函数不会发生这种情况(泛型,继承和类型推断不能很好地协同工作).
为了解您混淆的原因,您不应该进一步,而不是您提到的链接的第一个声明:
序列是一种类型的逻辑系列元素.
你可以返回一个只有一个,相同类型的序列seq<A>,或者seq<obj>.OOP是事实,类型B和C继承A是不相关的.以下内容可能会有所帮助:您的所有实例也都是从中继承的obj,但是为了从中创建,seq<obj>您应该明确地转换:
// Works fine
let testSeq = seq<obj> {
yield A(0) :> obj
yield B(1) :> obj
yield C(2) :> obj
}
Run Code Online (Sandbox Code Playgroud)
或者只是box像下面这样:
// Works fine too
let testSeq = seq {
yield box (A(0))
yield box (B(1))
yield box (C(2))
}
Run Code Online (Sandbox Code Playgroud)
编辑:为了理解F#中显式转换背后的原因,以下(简单化)考虑可能会有所帮助.类型推断不做猜测; 除非它可以seq确定性地推导出类型,或者明确声明它,否则它会抱怨.
如果你这样做
let testSeq = seq {
yield A(0)
yield B(1)
yield C(2)
}
Run Code Online (Sandbox Code Playgroud)
编译器带有不确定性 - testSeq可以是seq<A>,或者seq<obj>,所以它抱怨.当你这样做
let testSeq = seq {
yield A(0)
yield upcast B(1)
yield upcast C(2)
}
Run Code Online (Sandbox Code Playgroud)
它根据第一个成员的类型推断testSeq,seq<A>并且在A不抱怨的情况下向上倾斜B和C. 同样,如果你这样做
let testSeq = seq {
yield box A(0)
yield upcast B(1)
yield upcast C(2)
}
Run Code Online (Sandbox Code Playgroud)
它将推断testSeq为seq<obj>基于所述第一构件的第二向上转型此时和第三成员的类型obj,而不是A.