Types 高阶函数的类型

Types 高阶函数的类型,types,ocaml,higher-order-functions,Types,Ocaml,Higher Order Functions,如果我为高阶函数指定(我认为)正确的类型,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 导致以下错误消息

如果我为高阶函数指定(我认为)正确的类型,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
移动到全局范围也解决了这个问题:

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

为什么第一个示例不编译,而后面的示例编译?

这真是一个令人头痛的问题,如果是编译器错误,我也不会感到惊讶。也就是说,您可以通过显式命名类型来执行您想要的操作:

let f (type t) (g: t->string) (v: t) = g v in
从手册中:

编辑:

这也适用于:

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

类型必须是多态的:

让我用一个更简单的例子来说明这个问题

# 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])
由于
'a
必须与
int
(由于
cons3 1[]
)和
bool
(由于
cons3 true[]
)兼容,因此不可能实例化
'a
。因此该短语类型错误

如果您想根据其常用的类型推断算法来考虑ML类型,那么每个显式用户变量都会在统一算法中引入一组约束。这里的约束是
'a=“参数x的类型”
'=“参数y的类型”
。但是
'a
的范围是整个短语,它在任何内部范围内都不是广义的。因此
int
bool
最终都统一为非广义的
'a

最新版本的OCaml引入了作用域类型变量(如中所示)。早期版本中的本地模块也可以实现同样的效果(≥2.0):


(标准MLers注释:这是OCaml和SML不同的地方。)

作为参考点,等效的F#



编译。

我被你的问题难住了,但我可以说的是,
让f(g:'a->string)(v:'a):string=gv
不做你想做的事:
'a
在两种类型的注释中不一样。你最好写
让(f:('a->string)->'a->'string)=fungv->gv
(这会产生相同的类型错误)如果您想用相同的
'a
约束
g
v
。我建议将其发布到OCaml邮件列表中。@Pascal:谢谢,我在阅读了Gilles的回答后编辑了这个问题,我收回了我的评论:这两个注释中的
'a
是相同的,而使之相同的机制是Mechananism也会引起混淆。同样可以通过键入
let f(x:'a)(y:'a)=raise Not_found;
或阅读“type variables”部分来确认。为什么要在StackOverflow和上发布相同的问题?因为这两个问题都有知识渊博的Camler,所以您有两个正确的、不连贯的答案(因为大多数邮件列表阅读器不阅读StackOverflow),所以其中一个被助手浪费了宝贵的时间。您的回答让我怀疑这是否是OCaml 3.12中引入的回归(可以说,与规避方法同时使用).我只有3.12.0。有人试过旧版本吗?@Pascal Cuoq:3.11也一样。2@PascalCuoq与3.10.2oh相同,不,我应该更加注意,@Niki的exampe不适用于3.11(也不应该),否则我会复制@copton的行为。@Niki:根据caml邮件列表中的本地抽象类型(让f(类型a)(g:a->string)(v:a):string=gv)和多态递归(让f:'a.('a->string)->'a->string=fungv->gv)确实有两种可能的解决方案。它们都在OCaml 3.12中可用。人们会这样认为,因为
h
i
都是F#中相同的帽型函数,对吧?不,因为它们不是
内联的
,类型推断会根据用法为每种类型推断出单态类型。因此
h
int->string
i
float->string
(您始终可以添加显式类型或lambda以使其更加明显。)只有
inline
s可以有帽子类型;在内联上下文之外使用它们,它们必须根据使用情况获得“正常”类型实例化。有趣的是
f(x:'a)的方式
不同于
f x
,因为两者似乎具有相同的类型签名。全局作用域不同于
作用域中的
,这一事实是有意义的,不合理的是,将类型注释为泛型会将泛型解析为显式类型。如果注释参数和非注释参数的行为都相同,则它会就不会那么奇怪了。@Niki:问题是,你不是在用泛型来注释类型,而是在用变量来注释。变量的作用域就是整个短语。我认为如果你必须声明类型变量,这个问题会很自然。(还要记住,它们是类型变量,而不是类型方案变量。)谢谢你给出了这个有启发性的答案。你还记得什么时候决定像
'a
这样的类型变量的范围是短语吗?我几乎肯定我记得2000年左右的某个时候,范围只是类型(当时,我对这个问题的评论是有效的。现在,我想不是…)@Pascal:我不记得曾经见过一个ML实现,其中类型变量的作用域是一个单一的类型注释。而且,在OCaml中,
'a
的作用域一直是相同的(可能也在Caml中,可能不在Caml中),而不是通过本地模块和多态记录等功能显式分隔。@Gilles:谢谢您的回答。请明确说明:这是否意味着无法通过类型批注获得预期的行为?
# 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])
for some type 'a, (let cons2 (x : 'a) (y : 'a list) = ... in ...)
let module M = struct
    let cons2 (x : 'a) (y : 'a list) = x :: y
  end in
(M.cons2 1 [], M.cons2 true []);;
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