f# - 无法在 F# 中创建函数的通用部分应用程序

标签 f#

我正在寻找一种方法来解决这种非常特定的情况:我有一个函数工厂toF,它接受一个函数参数g,并基于它创建一个结果函数 f

let toF g = 
    let f x = g x
    f
let f = toF id

问题是我得到了一个

error FS0030: Value restriction. The value 'f' has been inferred to have generic type    val f : ('_a -> '_a)    Either make the arguments to 'f' explicit or, if you do not intend for it to be generic, add a type annotation.

我可以添加类型注释(我并不急于这样做)或者我可以像这样重写它:

let f' g x = g x
let f x = f' id x

我不喜欢这样做,因为如果我这样做,那么每次调用 f 时,我都会再次调用 f' 并指定 g一路走来。虽然第一个示例将 g 保留在闭包中并且只需要一次调用。

更新(对于 Tomas)

我已经尝试了你的建议。

let toF g = 
    printfn "Creating f using g"
    let f x =
        printfn "x: %A" x
        g x
    f
let f x = toF id x

let ``test``() =
    1 |> f |> f |> ignore

基本上发生的事情是,每次我调用函数 f 时,它首先调用 toF id 获取一个组合函数,然后才调用该组合函数x

Creating f using g
x: 1
Creating f using g
x: 1

所以本质上,组合是在每次调用 f 时通过随后调用 toF 创建的。但这正是我试图避免的。通过定义 let f = toF id 我希望一次性获得一个闭包然后能够立即调用它。所以我期望的输出是:

Creating f using g
x: 1
x: 1

更新 2

出于同样的原因,以下内容也不起作用:

let toF g = 
    printfn "Creating f using g"
    let f x =
        printfn "x: %A" x
        g x
    f
let f() = toF id
let fg = f()

最佳答案

你只需要让f成为一个句法函数:

let toF g = 
    let f x = g x
    f
let f x = toF id x

f 在语法上不是一个函数(带参数)而是一个值时,您会遇到“值限制”错误。我不打算在这里解释它,因为在以前的帖子中已经有很好的信息,例如:Understanding F# Value Restriction Errors

编辑 - 如果你想确保 g 只被调用一次(但仍然希望代码是通用的)那么最简单的方法是添加未使用的 unit 参数(使其成为一个函数),然后调用它一次(确定通用参数)并多次使用结果:

let toF g = 
    let f x = g x
    f
let f () = toF id

let fg = f ()
fg 1
fg 2

遗憾的是,这是必需的,因为拥有一个通用的函数,但由某些计算返回实际上会在类型系统中创建一个微妙的漏洞 - 这就是“值限制”的原因。

关于f# - 无法在 F# 中创建函数的通用部分应用程序,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19572242/

相关文章:

F# Powerpack 的元数据无法将 FSharp.Core 识别为 F# 库

f# - F# 中的 ComImport

.net - F# 将内部模块视为私有(private)模块

f# - f# 如何决定在启动时运行哪个文件?

visual-studio - 如何在 Visual Studio 中组织大型项目(>300 个类)的 F# 源代码?

f# - 嵌套函数调用中的 printfn 未在控制台上打印任何内容

visual-studio - 使用 F# 自动化 Excel 2010

f# - F# 相当于 C#'s ' out'

f# - Pause Monad - monadic 类型应该是什么样的?

F#如何将类型添加到列表中?