-
01-10-2019 - |
题
OCAML中的引用链接工作如何?
例如,假设我将3个模块声明为
A.ml
B.ml
C.ml
其中
A
需要B
和C
B
需要A
我应该如何进行编译?
由于订单是相关的 ocamlc
或者 ocamlopt
我如何修复之间的交叉参考 B
和 A
?
我正在尝试首先将它们全部编译成 .cmo
和 ocamlc -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
进入 (A
和 A'
), 在哪里 A'
包含由 B
, ,并在 A
. 。您的依赖性变得
A
需要A'
和B
和C
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.ml
和 B.ml
需要 A.ml
. 。如果您可以轻松地做到这一点,则最好进行重构以删除递归,但假设您不能。
如Nlucaroni所述,一种解决方案是将两个模块收集到同一文件中并使用 module rec
. 。有时另一个解决方案是将一个模块变成一个函子,例如,转 A
进入函子 F
与 B
的签名,然后首先编译文件定义 F
, , 然后 B
, ,然后一个刚刚定义的文件 module A = F(B)
.
OcamlyACC确实使事情变得更加复杂,但是您可以欺骗它!你可以写 module A = functor (...) -> struct
在里面 .mly
标题和匹配 end
在页脚中。但是您必须重写生成的 .mli
加上 module A : functor (...) -> sig
和 end
作为构建过程的一部分。 (我知道我之前已经做过了,解决了您遇到的同样的问题,尽管我不记得在哪里,所以我不能举一个真实的例子。)
值得调查的另一种可能性是从ocamlyacc转换为 Menhir, ,这是一个替换ocamlyAcc(语法是相同的几乎不需要移植),具有一些可以帮助您的功能,例如支持参数化的解析器模块(IE函数)。