OCAML中的引用链接工作如何?

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

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

其中

  • A 需要 BC
  • B 需要 A

我应该如何进行编译?

由于订单是相关的 ocamlc 或者 ocamlopt 我如何修复之间的交叉参考 BA?

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

具体错误是:

错误:链接A.CMO时错误:引用未定义的全局`b'

(或Viceversa,如果我交换Args的顺序)

我认为这是一个简单的问题,但我无法解决。

有帮助吗?

解决方案

你必须结合 模块成一个文件并使它们递归. 。我不认为有两个单独文件的汇编过程可以做到这一点。

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中的类型不同,并且编译器无法帮助您解决错误。

其他提示

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

如Nlucaroni所述,一种解决方案是将两个模块收集到同一文件中并使用 module rec. 。有时另一个解决方案是将一个模块变成一个函子,例如,转 A 进入函子 FB的签名,然后首先编译文件定义 F, , 然后 B, ,然后一个刚刚定义的文件 module A = F(B).

OcamlyACC确实使事情变得更加复杂,但是您可以欺骗它!你可以写 module A = functor (...) -> struct 在里面 .mly 标题和匹配 end 在页脚中。但是您必须重写生成的 .mli 加上 module A : functor (...) -> sigend 作为构建过程的一部分。 (我知道我之前已经做过了,解决了您遇到的同样的问题,尽管我不记得在哪里,所以我不能举一个真实的例子。)

值得调查的另一种可能性是从ocamlyacc转换为 Menhir, ,这是一个替换ocamlyAcc(语法是相同的几乎不需要移植),具有一些可以帮助您的功能,例如支持参数化的解析器模块(IE函数)。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top