如果我为高阶函数指定(我认为)正确的类型,则OCaml编译器拒绝该函数的 second 使用。
代码
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 -> string
。我能理解。但我没有得到的是,省略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
为什么第一个例子不能编译而后者呢?
答案 0 :(得分:9)
让我用一个更简单的例子说明问题。
# let cons0 (x : 'a) (y : 'a list) = x :: y;;
val cons0 : 'a -> 'a list -> 'a list = <fun>
# let cons1 (x : 'a) (y : 'a list) = x :: y in cons1 1 [];;
- : int list = [1]
# let cons2 (x : 'a) (y : 'a list) = x :: y in (cons2 1 [], cons2 true []);;
This expression has type bool but is here used with type int
# let cons3 x y = x :: y in (cons3 1 [], cons3 true []);;
- : int list * bool list = ([1], [true])
cons0
是一个多态函数定义,在全局范围内定义。它只是::
运算符的一个简单包装器。不出所料,该定义有效。 cons1
与cons0
几乎相同,只是其范围仅限于in
正文中的表达式。范围的变化看起来是无害的,而且肯定的是,它的变形。 cons3
也是同一个函数,没有类型注释,我们可以在in
正文中以多态方式使用它。
那么cons2
出了什么问题?问题是'a
的范围:它是整个顶级短语。定义cons2
的短语的语义是
for some type 'a, (let cons2 (x : 'a) (y : 'a list) = ... in ...)
由于'a
必须与int
(由于cons3 1 []
)和bool
(由于cons3 true []
兼容,因此无法实例化'a
{1}}。因此这句话是错误的。
如果您想根据其通常的类型推断算法考虑ML类型,则每个显式用户变量在统一算法中引入一组约束。这里的约束是'a = "type of the parameter x"
和' = "type of the parameter y"
。但是'a
的范围是整个短语,它并没有在任何内在范围中推广。因此,int
和bool
最终统一为非广义'a
。
最新版本OCaml引入了作用域类型变量(如Niki Yoshiuchi's answer中所述)。在早期版本(≥2.0)中使用本地模块可以实现相同的效果:
let module M = struct
let cons2 (x : 'a) (y : 'a list) = x :: y
end in
(M.cons2 1 [], M.cons2 true []);;
(标准MLers注意:这是OCaml和SML不同的地方。)
答案 1 :(得分:4)
这是一个真正令人头疼的问题,如果它是一个编译器错误我也不会感到惊讶。也就是说,您可以通过明确命名类型来执行您想要的操作:
let f (type t) (g: t->string) (v: t) = g v in
来自手册:http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#htoc108
编辑:
这也有效:
let f g v:string = g v in
具有您要查找的类型签名:('a -> string) -> 'a -> string
奇怪的是,当您注释参数的类型时,它不起作用。
编辑:
多态类型注释具有特殊语法:
let f: 'a. ('a -> string)-> 'a -> string = fun g v -> g v in
类型必须是多态的:http://caml.inria.fr/pub/docs/manual-ocaml/manual021.html#toc79
答案 2 :(得分:-2)
作为参考点,等效F#
let foo ():string =
let f: ('a -> string) -> 'a -> string = fun g v -> g v
let h = string
let i = string
let x = f h 23
let y = f i 23.0
x ^ y
编译。