Function 什么时候可以调用带有带标签参数但没有标签的函数?

Function 什么时候可以调用带有带标签参数但没有标签的函数?,function,arguments,ocaml,Function,Arguments,Ocaml,似乎在某些情况下,如果函数的顺序正确,您可以使用带标签的参数调用函数,而不使用标签;e、 g 设f~x~y=Format.sprintf“%d%s”x y;; f2“试验”;; 成功运行,但 f“测试”3;; 失败并显示错误消息 Line 1, characters 2-8: Error: This expression has type string but an expression was expected of type int Line 1, character

似乎在某些情况下,如果函数的顺序正确,您可以使用带标签的参数调用函数,而不使用标签;e、 g

设f~x~y=Format.sprintf“%d%s”x y;;
f2“试验”;;
成功运行,但

f“测试”3;;
失败并显示错误消息

Line 1, characters 2-8:
Error: This expression has type string but an expression was expected of type
         int
Line 1, characters 4-10:
Error: The function applied to this argument has type
         ?x:int -> y:string -> string
This argument cannot be applied without label
对于具有可选参数的函数,如果不传入可选参数,则它似乎可以工作:

设f?(x=1)~y()=Format.sprintf“%d%s”xy;;
f“帮助”();;
成功了,但是

f2“帮助”(;);;
失败并显示错误消息

Line 1, characters 2-8:
Error: This expression has type string but an expression was expected of type
         int
Line 1, characters 4-10:
Error: The function applied to this argument has type
         ?x:int -> y:string -> string
This argument cannot be applied without label

什么时候可以这样做有一般规则吗?

是的,有一般规则。发件人:

形式参数和参数根据各自的标签进行匹配,没有标签将被解释为空标签。这允许在应用程序中交换参数。还可以对任何参数部分应用函数,创建剩余参数的新函数

如果一个函数的多个参数具有相同的标签(或没有标签),则它们之间不会相互转换,并且顺序也很重要。但他们仍然可以用其他理由来交换意见

作为上述参数匹配规则的一个例外,如果应用程序是total(省略所有可选参数),则可以省略标签。在实践中,许多应用程序是total,因此标签通常可以省略


如果应用程序为total(即提供了所有必需的参数),并且函数的返回类型不是类型变量,则可以省略标签。可选参数的参数必须始终通过标签传递

我们来举几个例子

let example1 ?(opt=0) ~a ~b ~c unlabeled = 
  opt + a + b + c + unlabeled;;

example1 1 2 3 4;;
- : int = 10
在这里,我们能够应用所有不带标签的参数,因为我们已经提供了所有必需的参数(可选参数不是必需的,因此命名),并且生成的类型不是多态的。但是,如果我们从Core或ListLabels获取
List.fold
函数,它具有

'a list -> init:'accum -> f:('accum -> 'a -> 'accum) -> 'accum
'a list -> f:('a -> 'b) -> 'b list
然后我们会得到

List.fold [1;2;3;4] 0 (+);;
- : init:(int -> (int -> int -> int) -> '_weak1) ->
    f:((int -> (int -> int -> int) -> '_weak1) ->
       int -> int -> (int -> int -> int) -> '_weak1) ->
    '_weak1
而不是人们所期望的
10
。这是因为生成的类型
'acum
是一个类型变量,因此它也可以是一个函数,例如
int->int
string->int->unit
等——所有这些类型都与
'acum
类型匹配。这基本上意味着该函数接受可能无限多的位置参数。因此,我们提供的所有参数都被解释为位置参数,因此,我们永远无法填写标记的参数,因此,我们的应用程序不能成为完整的。这实际上使我们在发布开始时对规则的原始定义变得多余,因为使用类型变量表示的应用程序永远不可能是完整的

请注意,此问题仅在返回类型是类型变量时发生,而不仅仅包括一些类型变量,例如,我们可以很容易地省略带有
List.map
函数的标签,例如,给定Core中的
List.map
,它具有类型

'a list -> init:'accum -> f:('accum -> 'a -> 'accum) -> 'accum
'a list -> f:('a -> 'b) -> 'b list
我们可以很容易地应用它

# List.map [1;2;3] ident;;
- : int Core_kernel.List.t = [1; 2; 3]

尽管如此,人们通常认为省略标签是一种不好的做法。主要是因为多态返回类型的警告,以及因为它使得标记参数的顺序变得重要,这有点违反直觉

有趣的是,对于fun~y~z->Format.sprintf“%d%d%s%s”x s y z;中的
let f~x=let s=1这样的函数,它似乎是这样的您还必须在第二个函数中应用参数。我猜您必须应用参数,直到返回的值不再是函数?是的,因为OCaml是一种通用语言,所以只有单参数(1元)函数。看起来是多参数函数的东西实际上只是
fun a->fun b->…
的语法糖。因此,Total application意味着参数一直被应用,直到知道返回值不是函数为止;e、 如果其中一个参数也是一个函数,它似乎并不总是成立的<设f~s~t=s~r:t然后
f(fun~r->r)3不等于f~s:(fun~r->r)~t:3
,但更确切地说,它似乎部分地将参数应用于
s
,因此
s
的类型为
r:'a->(r:'b->'b)->int->'c
。是的,这确实是一个有趣的问题,但似乎有意义。您似乎认为编译器想要实现整个应用程序,并且您可能还认为
fun~r->r
应该与
~s
匹配,因为
~s
需要
~r
,但他们不必这样做,在这里也不需要。因为有可能
~s
可以返回函数,所以total application规则不适用。由于应用的参数没有标记为
~s
~t
,因此它们必须属于
s
返回的函数。因此
~s
是一个函数,它接受一个
~r
,并将其作为
~t
传递给最外层的函数,还有一个不同的函数,它也恰好接受一个
~r
,和一个
int
。这里有一个应用程序符合这一点:
f(fun~r->r)3~s:r)~t:4
。这将返回最后一个函数返回的任何值,在本例中为
int
。只要参数以正确的顺序传递,通常可以忽略标签。