尝试理解Ocaml的命名参数机制。我理解基础知识,但doc显示了这样的示例:
# 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>
答案 0 :(得分:8)
男人说 “请注意,ListLabels.fold_left等结果类型为类型变量的函数永远不会被视为完全应用。”
以下是您的示例中发生的情况。请注意它有点牵扯。
# ListLabels.fold_left;;
- : f:('a -> 'b -> 'a) -> init:'a -> 'b list -> 'a = <fun>
只是经典用法:ListLabels.fold_left
包含3个参数,即标记为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。
由于您提供了两个名为add
和i
的命名参数,因此类型'a
被推断为类型为add:'c -> ~i:'d -> 'e
的函数类型。
根据变量 add
和i
的类型,'c
类型必须为int -> int -> int
,'d
必须是int
。
替换'a
类型中的这些值,我们得出类型'a
为add:(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
。因此,您在结尾处获得的值不是add:(int -> int -> int) -> i:int -> 'a
类型,而是类型'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