F# 部分应用程序函数包装器不保留 return 参数函数的通用类型

F# partial application function wrapper does not keep return type generic for parameter function

TL DR:我正在尝试创建一个函数包装器。包装函数不带参数,但 returns 一个值。

我想这样做的原因是创建一个类似于“锁”的函数,但用于信号量。

在这里

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

签名:(string -> (unit -> 'a) -> 'a)
到目前为止,一切都很好

现在让我们用一个参数来丰富 functionWrapper

let functionWrapperFoo = functionWrapper "Foo"

签名:((unit -> obj) -> obj)
由于某种原因,'a 在这里变为 obj..

现在终于想用这个功能再包裹一个
但是类型推断会将 functionWrapperFoo 的签名更改为 ((unit -> int) -> int),这会阻止之后将函数与其他类型一起使用。

let resultFooInt =
    functionWrapperFoo (
        fun _ ->
            (42)
        )


// Because I am using a string as a return type now, this one bellow won't compile
let resultFooString =
    functionWrapperFoo (
        fun _ ->
            "42"
        )

为什么我不能在第一次部分应用后保持此函数通用?

我确实找到了解决方法,但是我真的不明白这样做的原因

我看到一些人 post 提到了关于部分应用的类似内容,但在我的例子中,我的包装函数没有参数
Keeping partially applied function generic

完整原码

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

let functionWrapperFoo = functionWrapper "Foo"
let resultFooInt =
    functionWrapperFoo (
        fun _ ->
            (42)
        )
let resultFooString =
    functionWrapperFoo (
        fun _ ->
            "42"
        )

解决方法

let functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

let functionWrapperFoo() = functionWrapper "Foo"

let resultFooInt =
    functionWrapperFoo() (
        fun _ ->
            (42)
        )
    
let resultFooString =
    functionWrapperFoo() (
        fun _ ->
            "42"
        )

如果您在第一个 full code 部分中将 f 定义为一个值,则 f 将成为完全类型化的,因​​此不再是通用的。编译器需要推断某种类型。

如果您使它保持 workaround 部分中的功能。它仍然是一个(通用)函数,这就是它起作用的原因。

你也可以再次指定函数参数,这样调用函数就没那么丑了:

let inline functionWrapper (param1) (f:(unit -> 'a)) =
    printfn "Calling function %s" param1
    f()

let functionWrapperFoo f = functionWrapper "Foo" f

let resultFooInt =
    functionWrapperFoo (
        fun _ ->
            (42)
        )
let resultFooString =
    functionWrapperFoo (
        fun _ ->
            "42"
        )

这是一个称为值限制的问题。在 this article on automatic generalization in F# and there are also good existing answers on SO.

中对此有相当详细的解释

基本问题是 F# 不允许您定义通用语法值。您的 functionWrapperFoo 被定义为一个值,即使用 let value = <expr>。在这种情况下,该值不能是通用的,即使表达式 <expr> 实际上计算为一个函数。

您的解决方法有效,因为它使用 let func arg = <expr> 将句法声明转换为函数声明。在这里,编译器肯定知道它将成为一个函数。

正如 Tom 所提到的,更好的解决方法是在 functionWrapperFoo 定义中添加一个参数并将其传递给 functionWrapper - 这样,它将在语法上被定义为一个函数并自动泛化将使它通用。