常见错误信息

此页面列出了 OCaml 编译器发出的一些错误或警告消息的快速解释。更详细的解释通常在本文档的专用章节中给出。

类型错误

此表达式类型为 ... 但此处使用类型为 ...

当对象的类型与使用它的上下文不兼容时,经常会收到此类消息。

# 1 + 2.5;;
Line 1, characters 5-8:
Error: This expression has type float but an expression was expected of type
         int

“此表达式类型为 X 但此处使用类型为 Y”表示,如果将表达式的內容分离 (2.5),则其类型推断为 X (float)。但上下文,即周围的所有内容 (1 + ...) 表明,该位置需要类型为 Y (int) 的表达式,这与 X 不兼容。

更令人困惑的是以下消息

This expression has type my_type but is here used with type my_type

此错误在使用交互式顶层环境测试某些类型定义时经常发生。在 OCaml 中,完全可以定义一个与其他类型同名的类型。考虑以下会话

# type my_type = A | B;;
type my_type = A | B
# let a = A;;
val a : my_type = A
# type my_type = A | B;;
type my_type = A | B
# let b = B;;
val b : my_type = B
# a = b;;
Line 1, characters 5-6:
Error: This expression has type my_type/1
       but an expression was expected of type my_type/2
       Hint: The type my_type has been defined multiple times in this
         toplevel session. Some toplevel values still refer to old versions
         of this type. Did you try to redefine them?

对于编译器来说,my_type 的第二个定义与第一个定义完全独立。因此,我们定义了两个具有相同名称的类型。由于“a”是较早定义的,因此它属于第一个类型,而“b”属于第二个类型。在此示例中,在 my_type 的最后一个定义之后重新定义“a”可以解决问题。除非在同一个模块中使用相同名称表示相同类型,否则这种问题不应出现在实际程序中,这强烈不建议。

警告:此可选参数无法删除

具有可选参数的函数必须至少有一个非带标签的参数。例如,以下情况是不允许的

# let f ?(x = 0) ?(y = 0) = print_int (x + y);;
Line 1, characters 18-23:
Warning 16 [unerasable-optional-argument]: this optional argument cannot be erased.
Line 1, characters 9-14:
Warning 16 [unerasable-optional-argument]: this optional argument cannot be erased.
val f : ?x:int -> ?y:int -> unit = <fun>

解决方法是简单地添加一个 unit 类型的参数,如下所示

# let f ?(x = 0) ?(y = 0) () = print_int (x + y);;
val f : ?x:int -> ?y:int -> unit -> unit = <fun>

有关带标签参数函数的更多详细信息,请参阅标签部分。

此表达式的类型...包含无法泛化的类型变量

当编译器到达编译单元(文件)末尾时,在某些情况下,它不知道对象的完整类型,但由于某些原因它不能保持多态性。例如

# let x = ref None;;
val x : '_weak1 option ref = {contents = None}

在编译期间触发以下消息

The type of this expression, '_a option ref,
contains type variables that cannot be generalized

解决方法:使用类型注释帮助编译器,例如

# let x : string option ref = ref None;;
val x : string option ref = {contents = None}

# let x = ref (None : string option);;
val x : string option ref = {contents = None}

类型为 '_weak<n> 的数据可能被临时允许,例如在顶层环境会话期间。这意味着给定对象具有未知类型,但它不能是任何类型:它不是多态数据。在顶层环境中,我们的示例给出以下结果

# let x = ref None;;
val x : '_weak2 option ref = {contents = None}

编译器告诉我们 x 的类型尚未完全确定。但通过稍后使用 x,编译器可以推断出 x 的类型

# x := Some 0;;
- : unit = ()

现在 x 具有已知的类型

# x;;
- : int option ref = {contents = Some 0}

模式匹配警告和错误

此模式未被使用

此警告应视为错误,因为没有理由故意保留此类代码。当程序员无意中引入了一个通配模式时,可能会发生这种情况,例如在以下情况下

# let test_member x tup =
  match tup with
  | (y, _) | (_, y) when y = x -> true
  | _ -> false;;
Line 3, characters 14-20:
Warning 12 [redundant-subpat]: this sub-pattern is unused.
Line 3, characters 5-20:
Warning 57 [ambiguous-var-in-pattern-guard]: Ambiguous or-pattern variables under guard;
variable y appears in different places in different or-pattern alternatives.
Only the first match will be used to evaluate the guard expression.
(See manual section 11.5)
val test_member : 'a -> 'a * 'a -> bool = <fun>

显然,程序员对 OCaml 的模式匹配存在误解。请记住以下几点

  • 情况树是从左到右线性遍历的。与正则表达式匹配不同,没有回溯
  • 保护性条件(“when”子句)不是模式的一部分。它只是一个最多评估一次的条件,用作跳转到下一个匹配情况的最后手段。
  • 小写标识符(如上面的“y”)只是名称,因此它们始终匹配。

在我们的示例中,现在很清楚只有对的第一个元素会被测试。这导致以下结果

# test_member 1 (1, 0);;
- : bool = true
# test_member 1 (0, 1);;
- : bool = false

此模式匹配不完整

OCaml 的模式匹配可以仅根据类型检查一组模式是否完整。因此,在以下示例中,编译器不知道“mod”运算符将返回哪个范围的整数

let is_even x =
  match x mod 2 with
  | 0 -> true
  | 1 | -1 -> false

没有模式匹配的简短解决方案将是

# let is_even x = x mod 2 = 0;;
val is_even : int -> bool = <fun>

通常,这种简化是不可能的,最好的解决方法是添加一个永远不会到达的通配情况

# let is_even x =
  match x mod 2 with
  | 0 -> true
  | 1 | -1 -> false
  | _ -> assert false;;
val is_even : int -> bool = <fun>

重新编译有效程序的问题

x.cmi 不是编译的接口

在重新编译某些旧程序或编译来自未正确清理的外部源的程序时,可能会收到此错误消息。

some_module.cmi is not a compiled interface

这意味着 some_module.cmi 根据当前版本的 OCaml 编译器无效。大多数情况下,删除旧的编译文件(*.cmi、*.cmo、*.cmx 等)并重新编译足以解决此问题。

警告:字符串中存在非法的反斜杠转义

最新版本的 OCaml 会警告您字符串中未受保护的反斜杠,因为它们应该加倍。在编译旧程序时可能会显示此类消息,并且可以使用 -w x 选项将其关闭。

# "\e\n" (* bad practice *);;
File "_none_", line 1, characters 1-3:
Warning 14 [illegal-backslash]: illegal backslash escape in string.
- : string = "\\e\n"
# "\\e\n" (* good practice *);;
- : string = "\\e\n"

仍然需要帮助?

帮助改进我们的文档

所有 OCaml 文档都是开源的。发现错误或不清楚的地方?提交拉取请求。

OCaml

创新。社区。安全。