常見錯誤訊息

此頁面列出 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 的模式匹配有所誤解。請記住以下事項

  • 案例樹會從左到右線性遍歷。如同 regexp 匹配一樣,沒有回溯
  • 守衛(「when」子句)不是模式的一部分。它只是一個條件,最多會評估一次,並用作跳至下一個匹配案例的最後手段。
  • 小寫識別碼(例如上方的「y」)只是名稱,因此它們始終會匹配。

在我們的範例中,現在很清楚的是,只會測試配對的第一個項目。這會產生以下結果

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

此模式匹配不完整

OCaml 的模式匹配可以根據型別來檢查一組模式是否完整。因此,在以下範例中,編譯器不知道「mod」運算子會傳回哪個 int 範圍

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

這表示根據 OCaml 編譯器的目前版本,some_module.cmi 無效。大多數情況下,移除舊的已編譯檔案 (*.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

創新。社群。安全性。