6

引用链接在 OCaml 中如何工作?

例如,假设我有 3 个模块声明为

  • A.ml
  • B.ml
  • C.ml

其中

  • A需求BC
  • B需要A

我应该如何进行编译?

由于 order 是相关的,ocamlc或者ocamlopt我如何修复 and 之间的交叉B引用A

我试图首先将它们全部编译成.cmowithocamlc -c然后将它们链接在一起但没有成功,因为交换参数只会将问题从一个模块转移到另一个模块。

具体错误是:

错误:链接 A.cmo 时出错:引用未定义的全局“B”

(反之亦然,如果我交换 args 的顺序)

我认为这是一个简单的问题,但我无法解决它..提前谢谢

4

2 回答 2

7

您必须将模块组合到一个文件中并使它们成为递归的。我不相信有办法从两个单独文件的编译过程中做到这一点。

module rec A : 
    sig
        val f : int -> int
        val g : int -> int
    end = 
    struct
        let f x = (B.g x) + 1
        let g x = x + 1
    end
and B :
    sig
        val f : int -> int
        val g : int -> int
    end = 
    struct
        let f x = (A.g x) + 1
        let g x = x + 1
    end

编辑:根据您的评论,我猜您有解析器的类型定义以及在同一文件中处理/操作该类型的函数。我同意你的观点,这是有道理的。但是就像您所经历的那样,如果该文件不仅要对类型进行操作,还要调用解析器来生成数据,那么解析器将如何构造它呢?我的解决方案是将类型分离到它自己的模块中,并在执行操作的模块中打开该模块。

因此,您将拆分A为 (AA'),其中A'包含由 生成B并在 中使用的类型A。你的依赖变成了,

  • A需要A'BC
  • B需要A'

例如,我有一个配置文件解析器,用于启动我编写的任何应用程序。

ConfType   --contains the type t  
Conf       --calls parser, and contains helper functions for type ConfType.t  
ConfParser --for ocamlyacc
ConfLexer  --for ocamllex

所有这些的替代方法是使用多态变体。通过这种方式,您可以删除依赖项,因为它们是临时定义的。当然,解析器生成的类型可能与 Conf 中的不同,编译器将无法帮助您解决错误。

于 2010-07-01T17:11:27.690 回答
3

Ocaml 语言支持模块之间的递归,但编译器不支持编译单元之间的递归。所以你不能有A.ml需要B.mlB.ml需要A.ml。如果您可以轻松地做到这一点,那么重构以删除递归是最好的,但我们假设您不能。

正如 nlucaroni 所解释的,一种解决方案是将两个模块收集到同一个文件中并使用module rec. 有时另一种解决方案是将一个模块变成一个函子,例如,A变成一个函子F,它接受一个带有B's 签名的参数,并首先编译定义的文件F,然后编译一个B刚刚定义的文件module A = F(B)

Ocamlyacc 确实让事情变得更复杂,但你可以欺骗它!您可以module A = functor (...) -> struct在页眉中写入,在页脚中写入.mly匹配项。end但是,您必须重写生成的内容.mli以添加module A : functor (...) -> sigend作为构建过程的一部分。(我知道我以前做过,为了解决你遇到的同样的问题,虽然我不记得在哪里,所以我不能给出一个真实的例子。)

另一个值得研究的可能性是从 Ocamlyacc 切换到Menhir,这是 Ocamlyacc 的替代品(几乎不需要移植,因为语法相同)具有一些可以帮助您的不错的功能,例如支持参数化解析器模块(即函子) .

于 2010-07-08T22:26:40.157 回答