高阶函数的类型

时间:2010-12-14 19:32:29

标签: types ocaml higher-order-functions

如果我为高阶函数指定(我认为)正确的类型,则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

为什么第一个例子不能编译而后者呢?

3 个答案:

答案 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是一个多态函数定义,在全局范围内定义。它只是::运算符的一个简单包装器。不出所料,该定义有效。 cons1cons0几乎相同,只是其范围仅限于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的范围是整个短语,它并没有在任何内在范围中推广。因此,intbool最终统一为非广义'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 

编译。