9

试图了解 Ocaml 的命名参数机制。我了解基础知识,但文档显示了一个这样的示例:

# let f ~x ~y = x - y;;
val f : x:int -> y:int -> int = <fun>

# let x = 3 and y = 2 in f ~x ~y;;
- : int = 1

在应用程序中仅使用波浪号时到底发生了什么?它只是 的简写~x:x,类似于定义吗?如果是这样,有人可以解释为什么会这样:

# ListLabels.fold_left;;
- : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a = <fun>

# let add = (+) and i = 0 
in ListLabels.fold_left ~add ~i [1;2;3];;

生产

- : f:((add:(int -> int -> int) -> i:int -> 'a) ->
   int -> add:(int -> int -> int) -> i:int -> 'a) ->
init:(add:(int -> int -> int) -> i:int -> 'a) -> 'a = <fun>
4

1 回答 1

9

该男子说“请注意,像 ListLabels.fold_left 这样的结果类型是类型变量的函数永远不会被视为完全应用。”

这是您的示例中发生的情况。小心它有点牵扯。

# ListLabels.fold_left;;
- : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a = <fun>

只是经典用途:接受ListLabels.fold_left3 个参数,即一个函数标记f,一个初始化器init和一个列表。

现在,在

let add = (+) and i = 0 
in ListLabels.fold_left ~add ~i [1;2;3];;

该应用程序ListLabels.fold_left ~add ~i [1;2;3]被认为是不完整的(正如该男子所说)。这意味着`ListLabels.fold_left首先接收其未命名的参数,[1;2;3]并返回一个类型为 的函数f:('a -> int -> 'a) -> init:'a -> 'a。让我们称这个函数为 foo。

由于您提供了两个命名参数,标记为addi,因此类型'a被推断为函数类型,类型为add:'c -> ~i:'d -> 'e

根据变量 addi的类型,类型'c必须是int -> int -> int,而且'd必须是int

替换类型中的那些值'a,我们得出类型'aadd:(int -> int -> int) -> i:int -> 'e。并将其替换为 foo 的类型(我很高兴有复制粘贴 ;-),它的类型是

f:((add:(int -> int -> int) -> i:int -> 'e)
    -> int
    -> (add:(int -> int -> int) -> i:int -> 'e))
-> init:(add:(int -> int -> int) -> i:int -> 'e)
-> (add:(int -> int -> int) -> i:int -> 'e)

删除不必要的括号,并将 alpha 转换(即重命名)'e'a,我们得到

f:((add:(int -> int -> int) -> i:int -> 'a)
    -> int
    -> add:(int -> int -> int) -> i:int -> 'a)
-> init:(add:(int -> int -> int) -> i:int -> 'a)
-> add:(int -> int -> int) -> i:int -> 'a

那是 foo 的类型。但请记住,您正在向 foo 传递两个参数,分别是标记~add~i。所以你最后得到的值不是 typeadd:(int -> int -> int) -> i:int -> 'a而是 type 'a。编译器返回的示例的整个类型是

f:((add:(int -> int -> int) -> i:int -> 'a)
    -> int
    -> add:(int -> int -> int) -> i:int -> 'a)
-> init:(add:(int -> int -> int) -> i:int -> 'a)
-> 'a
于 2012-08-06T07:40:02.623 回答