我在以下代码中的let makeElem
上获得了值限制错误:
let elemCreator (doc: XmlDocument) =
fun name (value: obj) ->
let elem = doc.CreateElement(name)
match value with
| :? seq<#XmlNode> as childs ->
childs |> Seq.iter (fun c -> elem.AppendChild(c) |> ignore)
elem
| _ -> elem.Value <- value.ToString(); elem
let doc = new XmlDocument()
let makeElem = elemCreator doc
如果从elemCreator
返回的匿名函数没有任何通用参数,为什么会出现值限制错误?
编译器声明makeElem的impred类型是(string -> 'a -> XmlNode)
。但是,如果我将其声明为'a
,它为什么会将第二个参数推断为obj
?
答案 0 :(得分:1)
我相信这可能是&#34;期待&#34;行为(虽然在这种情况下是不幸的),作为编译器的泛化和缩合过程的结果。考虑托马斯的例子:
let foo (s:string) (a:obj) = a
如果你要定义
let bar a = foo "test" a
然后编译器将推断类型bar : 'a -> obj
,因为它概括了第一个参数的类型。在您的情况下,您具有等效的
let bar = foo "test"
所以bar
是一个值而不是一个句法函数。编译器基本上执行相同的推理过程,除非现在适用值限制。这在你的情况下是不幸的,因为这意味着你必须使用类型注释显式注释makeElem
(或使其成为语法函数)。
答案 1 :(得分:0)
这对我来说似乎是一种意想不到的行为。它可以使用更简单的函数进行演示:
let foo (s:string) (a:obj) = a
let bar = foo "bar" // Value restriction
一种可能的解释可能是F#编译器允许您使用任何子类型的参数调用某种类型的参数的函数。因此,您可以在未明确将foo "hi" (new A())
投射到A
的情况下调用obj
(过去曾经需要这样做)。
这种隐式转换可能意味着编译器实际上将bar
解释为:
let bar a = foo "bar" (a :> obj)
......所以它认为这个论点是通用的。无论如何,这只是一个推测,所以你可以尝试将此作为错误报告发送到 microsoft dot com 的 fsbugs 。
答案 2 :(得分:0)
(以下仅基于观察。)
如果你有一个函数obj -> 'a
,那么对该函数的调用不会用于推断/解决其参数的类型。举例说明:
let writeLine (arg: obj) = System.Console.WriteLine(arg)
writeLine
是obj -> unit
let square x =
writeLine x
x * x
由于x
,上述函数int
被推断为(*)
。如果类型可能受obj
约束,那么此函数将无效(x
在使用obj
之前会被推断为(*)
,这会导致错误以下行:类型obj
不支持运算符(*)
)。
我认为这种行为是件好事。没有必要将类型限制为obj
,因为每种类型都已隐式转换为obj
。这使您的程序更通用,并提供与.NET BCL的更好的互操作性。
简而言之,obj
与类型推断没有关系(耶!)。