高阶函数的类型
如果我为高阶函数指定(我认为)正确的类型,OCaml 编译器将拒绝该函数的第二次使用。
该代码
let foo ():string =
let f: ('a -> string) -> 'a -> string = fun g v -> g v
in let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
导致以下错误消息
File "test.ml", line 6, characters 14-15: Error: This expression has type float -> string but an expression was expected of type int -> string
因此 f
的第一次使用似乎将其第一个参数的类型固定为 int ->;字符串
。我能理解这一点。但我不明白的是,省略 f
的类型限制可以解决问题。
let foo ():string =
let f g v = g v
in let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
将 f
移动到全局范围也解决了这个问题:
let f: ('a -> string) -> 'a -> string = fun g v -> g v
let foo ():string =
let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
为什么第一个示例无法编译,而后面的示例可以编译?
if I specify the (I think) correct type for a high order function the OCaml compiler rejects the second usage of that function.
The code
let foo ():string =
let f: ('a -> string) -> 'a -> string = fun g v -> g v
in let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
leads to the following error message
File "test.ml", line 6, characters 14-15: Error: This expression has type float -> string but an expression was expected of type int -> string
So the first usage of f
seems to fix the type of its first parameter to int -> string
. I could understand that. But what I don't get is that omitting the type restriction on f
fixes the problem.
let foo ():string =
let f g v = g v
in let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
And moving f
to global scope fixes the problem, too:
let f: ('a -> string) -> 'a -> string = fun g v -> g v
let foo ():string =
let h = string_of_int
in let i = string_of_float
in let x = f h 23
in let y = f i 23.0
in x ^ y
Why is it that the first example does not compile while the later ones do?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
让我用一个更简单的例子来说明这个问题。
cons0 是一个多态函数定义,在全局范围内定义。它只是
::
运算符的一个简单包装。毫不奇怪,这个定义是有效的。cons1
与cons0
几乎相同,只是其范围仅限于in
主体中的表达式。范围的改变看起来无害,而且果然,它进行了类型检查。 cons3 又是相同的函数,没有类型注释,我们可以在in
主体中多态地使用它。那么
cons2
有什么问题吗?问题在于'a
的范围:它是整个顶级短语。定义cons2
的短语的语义是因为
'a
必须与int
兼容(由于cons3 1 [])并使用
bool
(由于cons3 true []
,不可能实例化'a
。因此该短语是错误的- 。如果您喜欢根据其通常的类型推断算法来考虑 ML 类型,则每个显式用户变量都会在统一算法中引入一组约束,这里的约束是
'a =“参数 x 的类型”
和' =“参数y的类型”
。但是'a
的范围是整个短语,它没有在任何内部范围内推广。 code>int 和bool
最终都统一为非通用的'a
最新版本的 OCaml 引入了作用域类型变量(如 Niki Yoshiuchi 的回答)。在早期版本中使用本地模块可以达到相同的效果(≥2.0):(
标准 MLers 注意:这是 OCaml 和 SML 不同的地方。)
Let me use a simpler example that illustrates the issue.
cons0
is a polymorphic function definition, defined at global scope. It's just a trivial wrapper to the::
operator. Unsurprisingly, the definition works.cons1
is almost the same ascons0
, except that its scope is limited to the expression in thein
body. The change of scope looks innocuous, and sure enough, it typechecks.cons3
is again the same function, with no type annotation, and we can use it polymorphically in thein
body.So what's wrong with
cons2
? The problem is the scope of'a
: it's the whole toplevel phrase. The semantics of the phrase that definescons2
isSince
'a
must be compatible withint
(due tocons3 1 []
) and withbool
(due tocons3 true []
, there is no possible instantiation of'a
. Therefore the phrase is ill-typed.If you like to think about ML typing in terms of its usual type inference algorithm, each explicit user variable introduces a set of constraints in the unification algorithm. Here the constraints are
'a = "type of the parameter x"
and' = "type of the parameter y"
. But the scope of'a
is the whole phrase, it's not generalized in any inner scope. Soint
andbool
both end up unified to the non-generalized'a
.Recent versions OCaml introduce scoped type variables (as in Niki Yoshiuchi's answer). The same effect could have been achieved with a local module in earlier versions (≥2.0):
(Standard MLers note: this is one place where OCaml and SML differ.)
这真是一个令人头疼的问题,如果这是一个编译器错误,我不会感到惊讶。也就是说,您可以通过显式命名类型来执行您想要的操作:
来自手册: http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#htoc108
编辑:
这也有效:
它具有您正在寻找的类型签名:
('a -> 字符串) -> '一-> string
奇怪的是,当你注释参数的类型时它不起作用。
编辑:
多态类型注释有一个特殊的语法:
并且类型需要是多态的: http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#toc79
This is a real head-scratcher and I wouldn't be surprised if it's a compiler bug. That said, you can do what you want by explicitly naming the type:
From the manual: http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#htoc108
Edit:
This also works:
which has the type signature you are looking for:
('a -> string) -> 'a -> string
Strange that it doesn't work when you annotate the type of the arguments.
EDIT:
Polymorphic type annotations have a special syntax:
And the type is required to be polymorphic: http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#toc79
作为参考点,
可以编译等效的 F#。
As a reference point, the equivalent F#
compiles.