2

我正在寻找一种方法来解决这个非常确定的情况:我有一个函数工厂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在闭包中,只需要一次调用。

更新(托马斯)

我已经尝试过你的建议。

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获取一个组合函数,然后才调用该组合函数 on 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()
4

2 回答 2

7

您只需要创建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

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

于 2013-10-24T17:35:35.817 回答
1

最简单的解决方案是只添加一个类型注释。假设您只关心一种真实类型,这完全简单:

let toF g = 
    let f x = g x
    f
let f : _ -> int = toF id

如果你真的需要调用f不同的类型,那么你可以将它包装在一个泛型类型中:

type F<'t>() =
    static member val f : _ -> 't = toF id

let blah = F.f "blah"
let one = F.f 1
于 2013-10-25T21:40:52.810 回答