没有通用参数时的值限制

时间:2012-02-23 13:04:34

标签: f# value-restriction

我在以下代码中的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

3 个答案:

答案 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)

writeLineobj -> unit

let square x = 
  writeLine x
  x * x

由于x,上述函数int被推断为(*)。如果类型可能受obj约束,那么此函数将无效(x在使用obj之前会被推断为(*),这会导致错误以下行:类型obj不支持运算符(*) )。

我认为这种行为是件好事。没有必要将类型限制为obj,因为每种类型都已隐式转换为obj。这使您的程序更通用,并提供与.NET BCL的更好的互操作性。

简而言之,obj与类型推断没有关系(耶!)。