注意:这个问题实际上有两个重要方面:
~f:
,~init:
等); 我希望答案能解决这两个问题。
以下函数按预期编译并执行:
open Core.Std
let tally s =
let upd m k =
String.Map.change m k (function None -> Some 1 | Some n -> Some (n+1)) in
let re = Str.regexp "[^a-zA-Z0-9]+" in
let ws = List.map (Str.split re s) String.lowercase in
List.fold_left ws ~init:String.Map.empty ~f:upd
好的,现在:我想摆脱对List.fold_left
(最后一行)的调用中的签名标签。我的理解是,只要参数按照函数文档中显示的顺序给出,我就可以这样做。
List.fold_left
的签名(如this page中所示)是:
val fold_left : 'a t -> init:'b -> f:('b -> 'a -> 'b) -> 'b
如果我正确读取此内容,我对List.fold_left
的调用中的参数顺序相同,但如果我在最后一行中省略了签名标签,所以它变成
List.fold_left ws String.Map.empty upd
...函数不再编译,编译器的错误消息显示:
File "tally.ml", line 1:
Error: The implementation tally.ml
does not match the interface tally.cmi:
Values do not match:
val tally :
bytes ->
init:('a Core.Std.String.Map.t ->
(int Core.Std.String.Map.t ->
bytes -> int Core.Std.String.Map.t) ->
'b) ->
f:(('a Core.Std.String.Map.t ->
(int Core.Std.String.Map.t ->
bytes -> int Core.Std.String.Map.t) ->
'b) ->
bytes ->
'a Core.Std.String.Map.t ->
(int Core.Std.String.Map.t ->
bytes -> int Core.Std.String.Map.t) ->
'b) ->
'b
is not included in
val tally : bytes -> int Core.Std.String.Map.t
File "tally.ml", line 3, characters 4-14: Actual declaration
我的问题分为两部分:
List.fold_left
时省略签名标签吗?(我希望通过这两个问题的答案,我将能够弄清楚为什么OCaml需要这里的标签来确定函数的类型与其界面匹配。)
根据Jeffrey Scofield的评论,我尝试使用标准库的List
模块重写函数:
open Core.Std.String.Map
open Core.Std.String
let tally s =
let upd m k =
change m k (function None -> Some 1 | Some n -> Some (n+1)) in
let re = Str.regexp "[^a-zA-Z0-9]+" in
let ws = List.map lowercase (Str.split re s) in
List.fold_left upd ws empty
...但它仍然无法编译:
File "tally.ml", line 10, characters 17-20:
Error: This expression has type int Map.t -> bytes -> int Map.t
but an expression was expected of type ('a -> 'b) -> 'c -> 'a -> 'b
Type
int Map.t = (bytes, int, comparator_witness) Core_kernel.Core_map.t
is not compatible with type 'a -> 'b
这个List.fold_left
的签名没有标签(正如杰弗里斯科菲尔德指出的那样),所以添加它们是没有意义的。
我不能对此错误消息做任何正面或反面,即使它比前一个短得多。
注意:为了使用标准库List.map
和List.fold_left
,我必须更改参数的顺序。
顺便说一句:我绝对愤怒了解Core.Std.List.map
和Core.Std.List.fold_left
(以及谁知道还有什么)与标准库中相同命名的函数有不同的签名& #39; s List
模块。如果有一个神圣的规则来重新实现库是尊重它的接口。违反此规则只会造成混乱,而且,随着语言的发展,OCaml已经比它应得的更加混乱。在我的书中,这种混乱的播撒是一种不可饶恕的罪,而且无论它有什么其他优势,都可以自行推理Core
。我现在急于从我的OCaml工作中清除它。
答案 0 :(得分:0)
OCaml不需要标签来确定类型。为方便起见添加了标签,因此不应记住参数的顺序。所以标签只是一个关键字参数。您不应该删除它们,这是库实现者的决定,您应该使用它们。所以你应该使用标签,或选择其他库。实际上,没有标签的fold_left
的可读性要低得多,所以删除它们无论如何都会是一个坏主意。
关于第二个问题,我认为这是非常易于理解的。它明确声明您的函数类型错误。
标签不仅仅是具有额外信息的扩充类型,它是类型声明的一部分,它创建的类型与未标记的类型不同。
表达
let f x = x
有类型
'a -> 'a
表达
let g ~x = x
具有完全不同的类型
x:'a -> 'a
如果没有带标签的参数,则无法应用g
。
而且,我不想这么说,但除此之外你会发现它...你尽管如此,我上面说过,OCaml确实允许你在某些情况下省略标签。但规则很微妙,我认为这是一个不好的做法。我不认为你应该把时间花在学习那些被认为是坏事的事情上。