有没有一种方法可以使可选参数f足够灵活以具有 type 'a -> 'b,但仍然使其默认为identity,前提是identity具有 type 'a -> 'a?
较早的问题 首先陈述我的问题,确切地说:
我想定义一个接受可选参数的函数,该参数是一个函数 ('a -> 'b)。默认值应该是身份,实际上是 ('a -> 'a),但我认为没有理由它不应该与更通用的 ('a -> 'b) 兼容。
然而,该问题随后包含一个示例来说明较小的问题。该问题的答案回应了更狭窄的问题。这是一般问题的简单说明:
# let g1 ~f x = f x;;
val g1 : f:('a -> 'b) -> 'a -> 'b = <fun>
Run Code Online (Sandbox Code Playgroud)
好吧,这就是我想要的类型。但我想f默认为一个identity函数。这应该是可能的,因为identityhas 类型'a -> 'bwhere 'bis 'a。但它不起作用:
# let g2 ?(f=identity) x = f x;;
val g2 : ?f:('a -> 'a) -> 'a -> 'a = <fun>
Run Code Online (Sandbox Code Playgroud)
添加类型规范identity没有帮助:
# let g3 ?(f=(identity:'a -> 'b)) x = f x;;
val g3 : ?f:('b -> 'b) -> 'b -> 'b = <fun>
Run Code Online (Sandbox Code Playgroud)
编辑:在我发布这个问题后,我发现了这个问题,它确实与我的问题非常接近。因此,如果您愿意,请将我的问题标记为重复。然而,这个问题的答案意味着我想做的事情没有什么用处,事实并非如此。详细信息如下:
实际用例是一个从列表中选择一些元素的函数。可选f参数允许从每个元素中提取一点数据,并使用该数据来决定是否在结果中包含该特定列表元素。当元素被整个元素选择时,f应该是identity。我试图定义的实际函数是针对惰性列表的。这是列表的简化版本,其L别名为List:
let select ?(accessor=identity) keys vals =
let rec sel ks vs =
if ks = [] || vs = [] then []
else let k, v = L.hd ks, L.hd vs in
let v_key = accessor v in
if k = v_key then v::(sel (L.tl ks) (L.tl vs))
else if k > v_key then sel ks (L.tl vs) (* let vs catch up *)
else sel (L.tl ks) vs (* let ks catch up *)
in sel keys vals
Run Code Online (Sandbox Code Playgroud)
简单使用:
# let vs = Batteries.List.range 1 `To 100;;
# let ks = [4; 10];;
# select ks vs;;
- : int list = [4; 10]
Run Code Online (Sandbox Code Playgroud)
更一般的用途是当 的元素是ks具有整数键字段的记录时。然后该accessor函数将从记录类型映射到int.
hd(是的,我知道and的使用tl有点不寻常。它可以更好地转换为惰性列表上下文。)
OCaml 根本不支持这一点。您不能编写具有根据是否传递可选参数而进行细化的类型的函数。
与链接问题中的一些答案不同,我同意这是一件合理的事情。事实上,输入像 Common Lisp 的序列函数(使用:key和:test)这样的东西似乎需要这样的东西。然而,OCaml 并不是一种可以做到这一点的语言。
最合理的方法可能是编写两个函数,其中一个将访问器作为非可选参数,另一个将其identity作为该参数提供:
let g f x = f x
let g_default x = g identity x
Run Code Online (Sandbox Code Playgroud)
这只是有点笨拙,而且你不需要分g两次实现逻辑。然而,将这种方法应用于多个可选参数的组合将会变得丑陋。