【问题标题】:F# partial application function wrapper does not keep return type generic for parameter functionF# 部分应用程序函数包装器不保留参数函数的返回类型泛型
【发布时间】:2021-09-04 15:36:51
【问题描述】:

TL DR:我正在尝试创建一个函数包装器。包装后的函数不接受参数,但返回一个值。

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

在这里

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

【问题讨论】:

    标签: generics f# partial-application


    【解决方案1】:

    这是一个称为值限制的问题。 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 - 这样,它将在语法上定义为一个函数,并且自动泛化将使其通用。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-02-23
        • 2021-10-06
        • 2021-11-10
        • 2023-04-02
        • 2020-10-26
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多