常见的错误信息

这一章给大家一个关于OCaml编译器的一些错误或者警告信息的简要解释。详尽解释在本教程的 相应章节可以找到。

类型错误

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

当一个对象的类型与上下文要求的不匹配,你就会看到这个信息:

# 1 + 2.5;;
Error: This expression has type float but an expression was expected of type int

"This expression has type X but is here used with type Y" 的意思是 如果一个表达式的内容是隔离的 (2.5),那么它的类型可以推导成 X (float)。但是上下文 告诉我们 (1 + ...) 应该是类型 Y (int) 的,因而与 X 不匹配。

下面的信息更加糟糕:

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

这个错误往往发生在一些toplevel中的类型定义上。在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;;
Error: This expression has type my_type/1 but an expression was expected of type my_type/2 Line 1, characters 0-20: Definition of type my_type/1 Line 1, characters 0-20: Definition of type my_type/2

对于编译器来说第二个定义是完全独立于第一个的。所以我们是定义了两个相同名称的类型。 由于a在早前就定义了,因此它是属于第一个类型。但b是属于第二个。在这个例子中,可以 重新定义a使得它是属于第二个类型的。这个错误是不应该在实际编程中碰到的,除非你在 同一个模块用同一个名字定义不同类型,这显然是自找麻烦。

警告: This optional argument cannot be erased

带可选参数的函数至少有一个未标签的参数。下面这个例子是有警告的:

# let f ?(x = 0) ?(y = 0) = print_int (x + y);;
Warning 16: 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>

更多关于标签化参数请参见 标签 一章。

The type of this expression... contains type variables that cannot be generalized

当某些情况下,编译器在到了编译单元(文件)结尾时,也无法得知类型的整个定义,并且也无法 是多态时,这个警告会产生:

# 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}

'_a的数据类型在toplevel有可能会暂时被允许。它代表这是某个类型,但它 不是所有类型,它不是多态的(译注:也就是说它不是真的多态的,只是toplevel中的一个占位表示而已)。 在toplevel,我们的例子有下面的结果:

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

编译器告诉我们x的类型还不完全知道,但是随着x的使用,编译器可以推导出它的类型:

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

现在 x 的类型是已知的:

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

更多细节在 OCaml FAQ

模式匹配警告和错误

This pattern is unused

这个警告其实是一个错误,因为这种代码没理由存在。这当程序员粗心地引入一个匹配所有的模式 是会产生,如下:

# let test_member x tup =
    match tup with
    | (y, _) | (_, y) when y = x -> true
    | _ -> false;;
Warning 12: this sub-pattern is unused. Line 3, characters 4-19: Warning 57: Ambiguous or-pattern variables under guard; variable y may match different arguments. (See manual section 9.5) val test_member : 'a -> 'a * 'a -> bool = <fun>

显然,这里程序员完全曲解了OCaml的模式匹配。记住以下几点:

  • 模式匹配的遍历是线性的,由上至下,从左到右。回溯是根本不存在的。
  • 一个guard 不是模式的一部分。它只是一个条件语句,并且最多使用一次。它只是让你跳到下一个模式的一个手段。
  • 小写的名字只是名字,它们会匹配任何东西的。

在我们的例子中,显然只有第一个模式才被匹配,这导致下面的结果:

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

This pattern-matching is not exhaustive

OCaml's pattern matching can check whether a set of patterns is exhaustive or not, based on the type only. So in the following example, the compiler doesn't know what range of ints the "mod" operator would return:

# let is_even x =
    match x mod 2 with
    | 0 -> true
    | 1 | -1 -> false;;
Warning 8: this pattern-matching is not exhaustive. Here is an example of a case that is not matched: 2 val is_even : int -> bool = <fun>

A short solution without pattern matching would be:

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

In general, that kind of simplification is not possible and the best solution is to add a catch-all case which should never be reached:

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

Problems recompiling valid programs

x.cmi is not a compiled interface

When recompiling some old program or compiling a program from an external source that was not cleaned properly, it is possible to get this error message:

some_module.cmi is not a compiled interface

It means that some_module.cmi is not valid according to the current version of the OCaml compiler. Most of the time, removing the old compiled files (*.cmi, *.cmo, *.cmx, ...) and recompiling is sufficient to solve this problem.

Warning: Illegal backslash escape in string

Recent versions of OCaml warn you against unprotected backslashes in strings since they should be doubled. Such a message may be displayed when compiling an older program, and can be turned off with the -w x option.

# "\e\n" (* bad practice *);;
Warning 14: illegal backslash escape in string. - : string = "\\e\n" # "\\e\n" (* good practice *);;
- : string = "\\e\n"