简体   繁体   中英

Can't define an exception only in a mli file

Ok, this is mostly about curiosity but I find it too strange.

Let's suppose I have this code

sig.mli

type t = A | B

main.ml

 let f = 
   let open Sig in
   function A | B -> ()

If I compile, everything will work.

Now, let's try to modify sig.mli

sig.mli

type t = A | B
exception Argh

and main.ml

main.ml

 let f = 
   let open Sig in
   function 
     | A -> ()
     | B -> raise Argh

And let's try to compile it :

> ocamlc -o main sig.mli main.ml
  File "main.ml", line 1:
  Error: Error while linking main.cmo:
  Reference to undefined global `Sig'

Well, is it just because I added the exception ? Maybe it means that exceptions are like functions or modules, you need a proper implementation.

But then, what if I write

main.ml

 let f = 
   let open Sig in
   function A | B -> ()

And try to compile ?

> ocamlc -o main sig.mli main.ml
>

It worked ! If I don't use the exception, it compiles !

There is no reason to this behaviour, right ? (I tested it on different compilers, 3.12.0, 4.00.0, 4.02.3 and 4.03.0 and all of them gave the same error)

Unlike variants, exception is not a pure type and requires its implementation in .ml file. Compile the following code with 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)))))

You can see (let (x/1999 = (field 2 (global Pervasives!))).. which means assigning the value stored in the 2 nd position of module Pervasives . This is the value of Exit . Exceptions have their values and therefore need .ml .

Variants do not require implementation. It is since their values can be constructed purely from their type information: constructors' tag integers. We cannot assign tag integers to exceptions (and their generalized version, open type constructors) since they are openly defined. Instead they define values for their identification in .ml .

To get an implementation of the exception, you need sig.ml . A .mli file is an interface file, a .ml file is an implementation file.

For this simple example you could just rename sig.mli to 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

I don't see a problem with this behavior, though it would be nice not to have to duplicate types and exceptions between .ml and .mli files. The current setup has the advantage of being simple and explicit. (I'm not a fan of compilers being too clever and doing things behind my back.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM