无法仅在 mli 文件中定义异常
Can't define an exception only in a mli file
好吧,这主要是出于好奇,但我觉得它太奇怪了。
假设我有这个代码
sig.mli
type t = A | B
main.ml
let f =
let open Sig in
function A | B -> ()
如果我编译,一切都会正常。
现在,让我们尝试修改sig.mli
sig.mli
type t = A | B
exception Argh
和main.ml
main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
让我们尝试编译它:
> ocamlc -o main sig.mli main.ml
File "main.ml", line 1:
Error: Error while linking main.cmo:
Reference to undefined global `Sig'
嗯,是不是因为我添加了异常?也许这意味着异常就像函数或模块,你需要一个适当的实现。
但是,如果我写
会怎样
main.ml
let f =
let open Sig in
function A | B -> ()
并尝试编译?
> ocamlc -o main sig.mli main.ml
>
成功了!如果我不使用异常,它会编译 !
这种行为没有任何理由,对吧? (我在不同的编译器上测试过,3.12.0、4.00.0、4.02.3 和 4.03.0,它们都给出了相同的错误)
要实现异常,您需要 sig.ml
。 .mli
文件是接口文件,.ml
文件是实现文件。
对于这个简单的示例,您可以将 sig.mli 重命名为 sig.ml:
$ cat sig.ml
type t = A | B
exception Argh
$ cat main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
$ ocamlc -o main sig.ml main.ml
我看不出这种行为有什么问题,不过最好不要在 .ml
和 .mli
文件之间复制类型和异常。当前设置的优点是简单明了。 (我不喜欢编译器太聪明和在我背后做事。)
与变体不同,异常不是纯类型,需要在 .ml
文件中实现。使用 ocamlc -dlambda -c x.ml
编译以下代码:
let x = Exit
-- the output --
(setglobal X!
(seq (opaque (global Pervasives!))
(let (x/1199 = (field 2 (global Pervasives!)))
(pseudo _none_(1)<ghost>:-1--1 (makeblock 0 x/1199)))))
可以看到(let (x/1999 = (field 2 (global Pervasives!)))..
,意思是给模块Pervasives
第2
位置存储的值赋值。这是Exit
的值。异常有它们的值,因此需要 .ml
.
变体不需要实现。这是因为它们的值可以完全根据它们的类型信息构造:构造函数的标记整数。我们不能将标记整数分配给异常(及其通用版本,开放类型构造函数),因为它们是公开定义的。相反,他们在 .ml
.
中定义用于标识的值
好吧,这主要是出于好奇,但我觉得它太奇怪了。
假设我有这个代码
sig.mli
type t = A | B
main.ml
let f =
let open Sig in
function A | B -> ()
如果我编译,一切都会正常。
现在,让我们尝试修改sig.mli
sig.mli
type t = A | B
exception Argh
和main.ml
main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
让我们尝试编译它:
> ocamlc -o main sig.mli main.ml
File "main.ml", line 1:
Error: Error while linking main.cmo:
Reference to undefined global `Sig'
嗯,是不是因为我添加了异常?也许这意味着异常就像函数或模块,你需要一个适当的实现。
但是,如果我写
会怎样main.ml
let f =
let open Sig in
function A | B -> ()
并尝试编译?
> ocamlc -o main sig.mli main.ml
>
成功了!如果我不使用异常,它会编译 !
这种行为没有任何理由,对吧? (我在不同的编译器上测试过,3.12.0、4.00.0、4.02.3 和 4.03.0,它们都给出了相同的错误)
要实现异常,您需要 sig.ml
。 .mli
文件是接口文件,.ml
文件是实现文件。
对于这个简单的示例,您可以将 sig.mli 重命名为 sig.ml:
$ cat sig.ml
type t = A | B
exception Argh
$ cat main.ml
let f =
let open Sig in
function
| A -> ()
| B -> raise Argh
$ ocamlc -o main sig.ml main.ml
我看不出这种行为有什么问题,不过最好不要在 .ml
和 .mli
文件之间复制类型和异常。当前设置的优点是简单明了。 (我不喜欢编译器太聪明和在我背后做事。)
与变体不同,异常不是纯类型,需要在 .ml
文件中实现。使用 ocamlc -dlambda -c x.ml
编译以下代码:
let x = Exit
-- the output --
(setglobal X!
(seq (opaque (global Pervasives!))
(let (x/1199 = (field 2 (global Pervasives!)))
(pseudo _none_(1)<ghost>:-1--1 (makeblock 0 x/1199)))))
可以看到(let (x/1999 = (field 2 (global Pervasives!)))..
,意思是给模块Pervasives
第2
位置存储的值赋值。这是Exit
的值。异常有它们的值,因此需要 .ml
.
变体不需要实现。这是因为它们的值可以完全根据它们的类型信息构造:构造函数的标记整数。我们不能将标记整数分配给异常(及其通用版本,开放类型构造函数),因为它们是公开定义的。相反,他们在 .ml
.