无法仅在mli文件

时间:2016-06-26 01:01:47

标签: exception module ocaml signature

好的,这主要是关于好奇心,但我发现它太奇怪了。

假设我有这段代码

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上测试了它们,所有这些都给出了相同的错误)

2 个答案:

答案 0 :(得分:7)

与变体不同,异常不是纯类型,需要在.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!)))..,这意味着分配存储在模块2的{​​{1}}位置的值。这是Pervasives的值。例外有其值,因此需要Exit

变体不需要实现。因为他们的价值观可以纯粹根据他们的类型信息来构建:构造者&#39;标记整数。我们不能将标记整数分配给异常(及其通用版本,开放类型构造函数),因为它们是公开定义的。相反,他们在.ml中定义了他们的身份识别值。

答案 1 :(得分:2)

要获得异常的实现,您需要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文件之间复制类型和例外会很好。当前的设置具有简单明了的优点。 (我不喜欢编译器过于聪明并且在我背后做事。)