【问题标题】:module alias of mli filemli 文件的模块别名
【发布时间】:2017-12-22 03:22:38
【问题描述】:

我有以下情况:

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 文件的备注。

【问题讨论】:

  • 不,mli 文件实际上不能定义除签名之外的任何内容,您需要使用 ml 文件来实现它们
  • 这让我很困惑——我的 mli 文件只包含一个类型(签名)。通过match x with | Ds.A i -> i 直接引用它就像一个魅力。只有当我创建模块别名时,我才突然需要一个用于创建 ds.cmo 的实现文件。
  • 如果你的模块类型只包含公共类型签名,它不需要实现,因为没有什么要实现的。但这并不意味着它也可以作为一个模块使用。

标签: module ocaml alias


【解决方案1】:

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 文件这一事实与此不矛盾;它是类型的模式匹配,并且不需要存在类型的值来定义函数。如果您在 user 中添加 let c = Ds.T.C 之类的内容,那么您已经构造了这样一个值,但这发生在 user.ml 中,一个实现文件。

【讨论】:

  • 您可以在没有实现文件的情况下使用类型定义,请参阅我在更新问题中的评论。错误消息意味着链接器需要目标文件(从实现创建)。我认为它背后有工程原因,来自别名的间接性。但我的问题是我是否可以解决这个问题,或者我是否需要将复制作为 .ml 文件进行处理:-)(这就是为什么我给出 +1 作为答案但不接受它作为解决方案)
  • 嗯,我不知道 ds.mli 文件如何足够。但你可以反过来做:只创建一个内容相同的 ds.ml 文件。那么签名是自动推断出来的,你没有重复?
  • 这就是我最终做的。
【解决方案2】:

你弄错的那一行是

module D = Ds

在这里,您试图将模块类型“影响”到模块,这不会发生。

但是,您可以对模块类型执行相同操作:

module type D = module type of Ds

【讨论】:

  • 我只是在阅读模块类型,但是当我将功能栏中的 D 替换为 D 时,我得到一个未绑定的模块错误(对于 D)。我想我现在需要做更多的事情?
  • 嗯,还是模块类型,不是模块。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多