微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

F# 部分应用函数包装器不保留参数函数

如何解决F# 部分应用函数包装器不保留参数函数

TL DR:我正在尝试创建一个函数包装器。被包装的函数没有参数但返回一个值。

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

在这里

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"
        )

为什么我不能在第一个部分应用后保持这个函数的通用性?

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

我看到一些帖子提到了类似的部分应用程序,但在我的情况下,我的包装函数没有参数
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"
        )

解决方法

这是一个称为值限制的问题。在 this article on automatic generalization in F# 中有相当详细的解释,还有 good existing answers on SO

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

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

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

,

如果您在第一个 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"
        )

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。