F# 错误 FS0030:值限制。值 'it' 已被推断为具有泛型类型

F# error FS0030: Value restriction. The value 'it' has been inferred to have generic type

let pair a b = fun select -> select a b;
let isPair(x) = x.GetType().BaseType.Name = "FSharpFunc`2"
exception OuterError of string
let first(p) = 
    if isPair(p) then p(fun a b -> a)
    else raise (OuterError("Not a pair"))
let second(p) = 
    if isPair(p) then p(fun a b -> b)
    else raise (OuterError("Not a pair"))
let p1 = fun f -> pair 2 (pair 4 5) f
second(p1);

我想得到“(pair 4 5)”,但它有问题:

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

let pair a b = fun select -> select a b;
let first(p) = p(fun a b -> a)
let second(p) = p(fun a b -> b)
let p1 = fun f -> pair 2 (pair 4 5) f
second(p1);

我删除了 isPair 功能,但没有任何改善。

我可以这样在 javascript 中写对:

var pair = (a, b) => select => select(a, b)
var first = p => p((a, b) => a)
var second = p => p((a, b) => b)
var p1 = pair(2, pair(4, 5))
second(p1)

我可以使用 second(p1) 的值 pair(4, 5)

var p2 = second(p1)
first(p2)
second(p2)

但这在 F# 中不起作用,我猜是因为 F# 是静态类型的。

这不起作用的原因是最终表达式 second(p1) 具有通用类型。

更具体地说,它的类型是这样的:

second(p1) : (int -> int -> 'a) -> 'a

这是一个将另一个函数作为参数并 returns 该函数的结果的函数。这个结果可以是任何类型。函数 pair 没有指定结果应该是什么,因此假定它是通用的,如上面签名中的 'a 所示。

现在的问题是泛型类型的值不能存在。泛型类型的函数可以存在,但值不能。在这里,我谈论的是 syntactic 值——即 let(或类似的)不带参数的声明。

这叫做“值限制”,其原因有点微妙,与不受控制的效果和突变有关。有关深入讨论,请参阅 this article

当您在 FSI 中执行 second(p1) 时,您正试图创建这样一个值。具有通用类型的值(不是函数)。 FSI 将此值称为 it,这就是您在错误消息中看到的值。


您可以稍后使用结果值来解决此问题,类似于您的 JavaScript 示例:

let p2 = second(p1)
second p2

这个☝️会编译运行,只要你同时执行这两行,所以FSI可以同时看到它们。

在这里,编译器可以使用表达式 second p2 来推断 p2 的类型是非泛型 (int -> int -> int) -> int,并且值限制不会起作用。

解决该限制的另一种方法是显式声明您的值应具有通用类型:

let p2<'a> : (int -> int -> 'a) -> 'a = second(p1)

这将自行编译,即使没有像 second p2.

这样的后续用法

实际上,在现实世界的程序中,这个问题永远不会出现。这是因为在真实程序中,所有值最终都用于某种 input/output 或其他效果,此时它们的类型才为人所知。所以价值观永远不会像这样“悬而未决”。

出现此问题的唯一背景是探索和实验,就像您正在做的那样,中间值可能会自行探索。