在F#交互式shelldotnet fsi
中,我正在尝试像在Haskell、中一样测试flip
功能
flip :: (a -> b -> c) -> b -> a -> c
> let flip = fun f -> fun a -> fun b -> f(b)(a);;
val flip: f: ('a -> 'b -> 'c) -> a: 'b -> b: 'a -> 'c
然后,研究内置管道操作员
> (|>);;
val it: ('a -> ('a -> 'b) -> 'b)
到目前为止还不错。
现在,
> flip (|>);;
flip (|>);;
^^^^^^^^^
/..... : error FS0030: Value restriction. The value 'it' has been inferred to have generic type
val it: (('_a -> '_b) -> '_a -> '_b)
Either make the arguments to 'it' explicit or, if you do not intend for it to be generic, add a type annotation.
有人能解释一下F#类型系统中的这个错误是怎么回事吗?
对我来说,
val it: (('_a -> '_b) -> '_a -> '_b)
实际上应该是预期的结果,我该如何解决这个问题?谢谢
我之前的回答解释了什么是值限制以及为什么会发生这种情况。但现在你的下一个问题是:好吧,我该怎么办?
正如错误消息本身所暗示的那样,有两种可能的方法:(1(为其提供显式参数;或者(2(如果不希望它是泛型的,则添加类型注释。
1.显式自变量
let flippedPipe x = flip (|>) x
尽管从逻辑上讲,这与let flippedPipe = flip (|>)
相同,但从语法上讲,它现在是一个函数,而不是一个值。语法对于值限制来说很重要。
2.类型注释
let flippedPipe : (int -> int) -> int -> int = flip (|>)
这是因为该函数不再是泛型函数,因此不适用"值限制"。在许多情况下,这是一个理想的选择,但从您在这里使用的功能类型来看,我认为这不是您在这种情况下想要的。
3.显式类型参数
错误消息没有提到这一点,公平地说,它可以被视为选项(2(的变体。这个想法是,你可以给你的函数显式的类型参数如下:
let flippedPipe<'a, 'b> = flip (|>)
这使得值限制消失了,因为即使它在技术上仍然适用,添加类型参数的事实可能表明你知道自己在做什么,所以编译器会关闭。
然而,尽管乍一看这似乎有效,但它的做法是错误的。如果你看看这个函数的推断类型,你会看到:
val flippedPipe : (obj -> obj) -> obj -> obj
发生这种情况是因为,即使添加了类型参数,也没有指定它们的确切位置。对于编译器所知道的,它们可能根本不被使用(也称为"幻影类型"(。
因此,为了使其正常工作,这应该是定义:
let flippedPipe<'a, 'b> : ('a -> 'b) -> 'a -> 'b = flip (|>)