OCaml线产生神秘的错误

时间:2015-09-29 08:12:55

标签: compiler-errors ocaml

当我执行代码时

let (a,p) = (2+2, Printf.printf) in p "abc"; p "%d" 3 ;;

我希望看到输出abc3,但改为

File "f.ml", line 1, characters 46-47:
Error: This function has type (unit, out_channel, unit) format -> unit
       It is applied to too many arguments; maybe you forgot a `;'.

有趣的是,如果我将2+2更改为2,则会运行。

为什么代码会产生错误,但不会删除+2

1 个答案:

答案 0 :(得分:6)

OCaml对printf的特殊输入技巧和值多态的组合。

您可能知道,Printf.printf不是string,而是数据类型format。 OCaml类型检查器有一个特殊规则,用于键入printf的字符串文字:如果上下文请求它被键入为format

# "%d";;
- : string = "%d"
# ("%d" : _format);;
- : (int -> 'a, 'b, 'a) format = ...

OCaml类型系统还有一个叫做值多态的技巧(更确切地说,它是宽松的值多态)。其目的是正确键入具有副作用的表达式。我没有解释它的细节,但它限制了多态性:一些称为“扩展”的表达式不能具有多态类型:

# fun x -> x;;
- : 'a -> 'a = <fun>
# (fun x -> x) (fun x -> x)
- : '_a -> '_a = <fun>

在上面,(fun x -> x) (fun x -> x)没有多态类型,而身份函数fun x -> x有。这是由于(fun x -> x) (fun x -> x)的表达形式:它是“膨胀的”。奇怪的类型变量'_a是单态类型变量:它只能被实例化为某种类型。另一方面,像'a这样的多态变量可以在每次使用vlaue时实例化为不同的类型。

让我们回到你的代码:

# let (a, p) = (2, Printf.printf);;
val a : int
val p : ('a, out_channel, unit) format -> 'a

此处,p具有多态类型('a, out_channel, unit) format -> 'a'a可以实例化为多个类型,因此p "abc"; p "%d" 3是典型的:多态类型可以在(unit, out_channel, unit) format -> unit首次使用时p实例化,(int -> unit, out_channel, unit) format -> int -> unit }第二次使用p

将常量2更改为2+2,这是一个扩展,整个表达式也变得庞大,输入更改:

# let (a, p) = (2+2, Printf.printf);;
val a : int
val p : ('_a, out_channel, unit) format -> '_a

此处,p不再具有多态变量'a,而是单态'_a。在第一次使用unit时,此单态变量被统一(实例化)为p,因此p的类型变为(unit, out_channel, unit) format -> unit。它只需要1个参数,因此第二次使用带有2个参数的p的类型失败。

避免这种情况的一种简单方法是将您的定义分成两部分:

let a = 2 + 2 in
let p = Printf.printf in
p "abc"; p "%d" 3