Exception Can';t仅在mli文件中定义异常

Exception Can';t仅在mli文件中定义异常,exception,module,ocaml,signature,Exception,Module,Ocaml,Signature,好吧,这主要是出于好奇,但我觉得太奇怪了 让我们假设我有这个代码 sig.mli type t = A | B type t = A | B exception Argh main.ml let f = let open Sig in function A | B -> () let f = let open Sig in function | A -> () | B -> raise Argh let f =

好吧,这主要是出于好奇,但我觉得太奇怪了

让我们假设我有这个代码

sig.mli

type t = A | B
type t = A | B
exception Argh
main.ml

 let f = 
   let open Sig in
   function A | B -> ()
 let f = 
   let open Sig in
   function 
     | A -> ()
     | B -> raise Argh
 let f = 
   let open Sig in
   function A | B -> ()
如果我编译,一切都会工作

现在,让我们尝试修改
sig.mli

sig.mli

type t = A | B
type t = A | B
exception Argh
main.ml

main.ml

 let f = 
   let open Sig in
   function A | B -> ()
 let f = 
   let open Sig in
   function 
     | A -> ()
     | B -> raise Argh
 let f = 
   let open Sig in
   function A | B -> ()
让我们试着编译它:

> 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 -> ()
 let f = 
   let open Sig in
   function 
     | A -> ()
     | B -> raise Argh
 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
文件之间重复类型和异常。当前设置的优点是简单明了。(我不喜欢编译器太聪明,在我背后做事。)

与变体不同,exception不是纯类型,需要在
.ml
文件中实现。使用
ocamlc-dlambda-cx.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)))))
让x=退出
--输出--
(SETX!
(seq(不透明(全球渗透物!))
(设(x/1199=(字段2(全局渗透物!))
(伪u none_u1):-1--1(makeblock 0x/1199(()))
您可以看到
(让(x/1999=(字段2(全局渗透!))…
,这意味着分配存储在
2
模块
渗透
的nd位置中的值。这是
退出
的值。异常有其值,因此需要
.ml


变量不需要实现。这是因为它们的值可以完全从它们的类型信息构造:构造函数的标记整数。我们不能将标记整数分配给异常(以及它们的通用版本,开放类型构造函数)因为它们是公开定义的。相反,它们在
.ml

中定义了它们的标识值亲爱的Joffrey,这正是我所说的。你可以在
.mli
中输入一个类型,而不使用
.ml
,你就不会有任何问题。我实际上添加了
.ml
来克服这个问题,但从camlspotter的回答来看,你可以看,这不是一个简单的答案。;-)正如您所看到的,我知道错误是什么,但想知道异常和类型之间的行为为何不同。顺便说一下,很高兴您很高兴有更多的OCaml应答者。;-)谢谢你的回答。我仍然认为来自编译器的错误消息可以改进,因为完全不清楚问题出在哪里。;-)我仍然有一个问题,如果我不使用它,就不会有编译器错误。我认为这类似于C,在C中,你可以在头中声明一个函数,并且只会在你使用它时得到链接器错误。当然是不直观的。@Lhooq,它和普通值一样。在分离编译阶段,只在
mli
文件中声明它们而不给出它们的实现是没有问题的。链接是另一回事。