2

当我尝试将带有最后一个参数的函数作为泛型类型进行柯里化时,编译器会将泛型类型强制转换为它找到的第一个具体类型,从而在范围内的后续调用中失去其泛型属性。例子:

type Bar =
    | String
    | Integer
    | Boolean

let foo (a: String)(b: 'm) =
    printfn "%A - %A" a b

let foobar (a: String)(bar: Bar) =
    let fooWithA= foo a
    match bar with
    | String -> fooWithA "String"
    | Integer -> fooWithA 42
    | Boolean -> fooWithA true

这里最后两行给了我一个编译器错误,说明该函数需要一个字符串。但是,如果我用辅助类包装函数,我可以让它像这样工作:

type FooHelper(a:String) =
    member this.foo (b: 'm) =
        printfn "%A - %A" a b

let foobar (a: String)(bar: Bar) =
    let fooHelperWithA= FooHelper a
    match bar with
    | String -> fooHelperWithA.foo "String"
    | Integer -> fooHelperWithA.foo 42
    | Boolean -> fooHelperWithA.foo true

这不会给我一个编译器错误。然后,如果我直接尝试使用该函数,我会再次遇到编译错误:

let foobar (a: String)(bar: Bar) =
    let fooHelperWithA= FooHelper(a).foo
    match bar with
    | String -> fooHelperWithA "String"
    | Integer -> fooHelperWithA 42
    | Boolean -> fooHelperWithA true

最后两行抛出编译错误。

这是预期的行为还是错误?如果这是它应该如何工作,有人可以解释为什么吗?这真的让我很困惑。

4

1 回答 1

4

所以这是一个经典的价值限制问题。如果您只执行此代码块,则更明显:

let foo (a: String)(b: 'm) =
    printfn "%A - %A" a b 
let fooWithA= foo  "hello"

这将给出以下错误:

错误 FS0030:值限制。'fooWithA' 的值已被推断为具有泛型类型 val fooWithA : ('_a -> unit)
要么使 'fooWithA' 的参数显式,或者,如果您不打算使其成为泛型,则添加类型注释。

问题是这fooWithA不是一个正确的函数,因此不允许它具有泛型类型。

所以在第一个例子中,编译器看到你首先用一个字符串调用它,所以它进行了专门化,然后对于整数和布尔值情况失败。

在第二个示例中,您隐式创建了一个可以通用的函数,所以一切都很好。

在第三个示例中,您遇到与第一个示例相同的问题,您将函数绑定到一个值,因此它不能是通用的。

最简单的解决方案是始终使用这样的函数

let fooWithA a = foo "hello" a
于 2013-11-06T00:33:33.910 回答