考虑以下代码:
let myFun
?(f: ('a -> int) = (fun x -> x))
(x: 'a)
: int =
f x
Run Code Online (Sandbox Code Playgroud)
看起来我不能用另一个参数调用int.当我尝试使用此代码时:
let usage = myFun ~f:String.length "abcdef"
Run Code Online (Sandbox Code Playgroud)
Ocaml发出以下错误消息:
Error: This expression has type string -> int
but an expression was expected of type int -> int
Type string is not compatible with type int
Run Code Online (Sandbox Code Playgroud)
看起来推理会'a = int因为默认参数而思考.这是语言的限制,还是有一种方法来编写它以便编译?
符号?(f: ('a -> int)并不意味着参数f具有类型'a -> int,它是一个告诉类型推断算法的类型约束,它f应该是一个返回类型值的函数int.在'a这里只是意味着- "我不在乎,自己推断它".它不会推广您的类型变量.因此,给定您的约束,编译器推断,(1)fis 的类型int -> int(因为这是唯一满足您的默认值的输入),(2)类型x也是int,因为您自己约束它(它将是无论如何推断为相同类型,因为类型f.
此外,这里的类型系统是正确的,通过禁止除参数之外int的任何东西x.请考虑以下示例:
myFun "hello"
Run Code Online (Sandbox Code Playgroud)
根据你的愿望,它应该是有效的,但在这种情况下OCaml应该做什么?
由于如何实现optionals参数,此问题是一个限制.
实质上,使用标准选项类型在类型检查阶段扩展带有可选参数的函数.例如,你的功能:
let f ?(conv=(fun x -> x)) x = f x
Run Code Online (Sandbox Code Playgroud)
变得或多或少,
let f opt_conv =
let conv =
match opt_conv with
| None -> fun x -> x
| Some f -> f in
fun x -> conv x
Run Code Online (Sandbox Code Playgroud)
因此,由于opt_conv有型'a->'a的None分支,F必须有类型?conv:('a->'a) -> 'a -> 'a.
查看扩展函数,问题来自于Some分支和None应该具有不同类型以获得所需功能的事实.
对于类型谜语,在模式匹配的不同分支中具有不同类型是GADT可能带来潜在解决方案的标志:可以将扩展选项类型定义为
type ('default,'generic) optional =
| Default: ('default,'default) optional
| Custom: 'a -> ('default,'a) optional
Run Code Online (Sandbox Code Playgroud)
然后可以将您的函数重写为
let f: type a. (int -> int, a -> int) optional -> a -> int =
fun conv x ->
match conv with
| Default -> x
| Custom f -> f x
Run Code Online (Sandbox Code Playgroud)
导致预期的行为:
f Default "hi"产生类型错误,而f (Custom int_of_string) "2"返回2.
但是,如果没有可选的参数语法糖机制,这并不是真的有用.
没有人可以完全扩展OCaml以使用GADT-laded optional类型.然而,这很容易导致类型错误,并且相应的复杂性增加不会产生非常有吸引力的扩展.