我有以下情况:
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 文件?
备注:我知道实现文件和接口(interface)文件之间的区别,但据我所知,只要接口(interface)只包含类型定义,就不需要实现文件。假设我们将以下模块签名添加到 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 文件的注释。
最佳答案
类型 t = A | B
在文件 m.ml
中基本上与
模块 M = 结构类型 t = A | B 结束
。
文件 m.mli
中的同一行对应于
模块 M:信号类型 t = A | B端
前者实现了一个模块。后者只是一个模块签名。
签名可用于声明类型,但更常见的是它用于向外界屏蔽模块实现的某些部分。签名从未实际实现模块,即使模块仅包含类型声明也是如此。
模块就像一个值,而不是一个类型。例如,模块可以作为语言中的值进行包装和操作(“一流模块”),并且可以用作模块语言中仿函数的参数。
功能栏仅适用于存在的 .mli 文件这一事实与此并不矛盾;它是类型上的模式匹配,并且不需要存在类型值来定义函数。如果您在 user 中添加类似 let c = Ds.T.C
的内容,那么您已经构造了这样一个值,但这发生在 user.ml
中,一个实现文件。
关于mli 文件的模块别名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46080978/