Definition in Standard ML implementation file is externally visible (but not in the signature file)

215 views Asked by At

I'm converting SML starter code from the Tiger book to OCaml.

What's confusing me is that, in the signature file table.sig (below), there is no mention of IntMapTable, but the functor is accessible in another file without any qualification.

(* table.sig *)
signature TABLE =
sig
    ...
end

(* table.sml *)
functor IntMapTable (...) : TABLE =
struct
...
end

(* symbol.sml *)
...
structure Table = IntMapTable(...)
...

My understanding was that only code in the .sig file could be accessed to external modules, but not code in the .sml file. Is this not the case?

Also, what might the equivalent code look like in OCaml? It's awkward, because the functor IntMapTable's result type is Table, which is the enclosing module of the file.

1

There are 1 answers

0
Andreas Rossberg On BEST ANSWER

With SML, neither files nor their names have much meaning. They are just a way to chop up a larger source into smaller units. A program consisting of multiple files is equivalent to the concatenation of these files in some suitable order.

This is different in OCaml, where every .ml file is itself treated as a structure of a name derived from the file's name, and every associated .mli file, if present, as an opaque signature annotation on that structure.

(Moscow ML, being based on an early implementation of the OCaml runtime, is the one SML implementation that uses a similar file model.)

So when porting over an SML file to OCaml you have two basic choices:

  1. Treat it as if it was wrapped into an extra structure. For example, your table.sig could become table_sig.ml (not .mli!) and the signature would be referred to as Table_sig.TABLE, while table.ml would become table_fn.ml with the functor being referred to as Table_fn.IntMapTable.

    Sometimes you want to reorganise the file structure a little. For example, instead of having two modules Table_sig and Table_fn, you may choose to have just one Table which contains both the signature and the functor. OCaml then tends to use naming conventions like calling the signature S and the functor Make, thus intuitively referring to them as Table.S and Table.Make elsewhere.

  2. In some cases, you can flatten away this extra wrapper structure. In particular:

    a. If the file consists of a single structure declaration whose RHS is a struct expression then you can just put its body in an .ml file named after the declared structure.

    b. If the file consists of a single signature declaration whose RHS is a sig expression, and that is only used once to annotate a structure as per case (a), then you can just put its body in an .mli file named after the corresponding structure.

The second tends to be the common case.