我有以下情况:
mli文件ds.mli只包含一个类型声明:
type t = A of int | B of string
现在我想在另一个模块user.ml:
中为Ds创建一个别名module D = Ds
在编译时,我从链接器收到以下错误消息:
$ ocamlc ds.mli user.ml
File "user.ml", line 1:
Error: Error while linking user.cmo:
Reference to undefined global `Ds'
如果我将ds.mli复制到ds.ml并调用
$ ocamlc ds.mli ds.ml user.ml
汇编完成。
是否有可能避免创建ds.ml文件?
备注:我知道实现和接口文件之间的区别,但据我所知,只要接口只包含类型定义,就不需要实现文件。假设我们将以下模块签名添加到ds.mli:
module T : sig
type t = C | D
end
然后在用户中定义bar:
let bar = function
| Ds.T.C -> true
| Ds.T.D -> false
通过
编译没有问题$ ocamlc ds.mli user.ml
将别名扩展到签名不应该是问题。
编辑:忘了将ds.ml添加到第二个ocamlc调用的参数中。 编辑:添加了关于使用mli文件的评论。
答案 0 :(得分:3)
type t = A | B
文件m.ml
内的文件基本相同
module M = struct type t = A | B end
。
文件m.mli
中的同一行对应于
module M : sig type t = A | B end
前者实现了一个模块。后者只是一个模块签名。
签名可用于声明类型,但更常见的是,它用于将模块实现的某些部分屏蔽到外部世界。即使模块只包含类型声明,签名也从未实际实现模块。
模块就像一个值,而不是一个类型。例如,模块可以作为语言中的值进行包装和操作(“第一类模块”),并且可以用作模块语言中仿函数的参数。
功能栏仅与.mli文件一起使用的事实与此并不矛盾;它是类型上的模式匹配,并且不需要存在用于定义函数的类型的值。如果您在用户中添加类似let c = Ds.T.C
的内容,那么您已经构建了这样的值,但这已经发生在实现文件user.ml
中。
答案 1 :(得分:2)
你弄错了这一行是
module D = Ds
在这里,你试图"影响"模块的模块类型,并且不会发生。
但是,您可以对模块类型执行相同的操作:
module type D = module type of Ds