OCaml 交叉链接
OCaml 中的引用链接如何工作?
例如,假设我有 3 个模块声明为
A.ml
B.ml
C.ml
其中
A
需要B
和C
B
需要A
我该如何进行编译?
由于使用 ocamlc
或 ocamlopt
顺序相关,如何修复 B
和 A
之间的交叉引用?
我尝试首先使用 ocamlc -c
将它们全部编译为 .cmo
,然后将它们全部链接在一起,但没有成功,因为交换参数只会将问题从一个模块到另一个模块。
具体错误是:
错误:链接 A.cmo 时出错: 引用未定义的全局“B”
(或者反之亦然,如果我交换参数的顺序)
我认为这是一个简单的问题,但我无法解决它..提前致谢
how does referenced linking work in OCaml?
Example, let's assume I have 3 modules declared as
A.ml
B.ml
C.ml
of which
A
needsB
andC
B
needsA
How should I proceed in compiling?
Since order is relevant using ocamlc
or ocamlopt
how can I fix the cross reference between B
and A
?
I'm trying to first compile them all into .cmo
with ocamlc -c
and then link all of them together but with no success since swapping arguments will just move a problem from a module to another.
The specific error is:
Error: Error while linking A.cmo:
Reference to undefined global `B'
(or viceversa, if I swap the order of the args)
I thinks this is an easy question but I'm unable to solve it.. thanks in advance
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
您必须将模块合并到一个文件中并使它们递归< /a>.我不相信有办法从两个单独文件的编译过程中做到这一点。
编辑:从您的评论中,我猜测您拥有解析器的类型定义以及在同一文件中处理/操作该类型的函数。我同意你的观点,这是有道理的。但是就像您所经历的那样,如果该文件不仅要对类型进行操作还要调用解析器来生成数据,那么解析器将如何构造它?我的解决方案是将类型分离到它自己的模块中,并在执行操作的模块中打开该模块。
因此,您将
A
拆分为(A
和A'
),其中A'
包含由B
,并在A
中使用。您的依赖项变为,A
需要A'
和B
和C
B
需要< 。例如,我有一个用于启动我编写的任何应用程序的配置文件的解析器
所有这一切的替代方法是使用多态变体。通过这种方式,您可以删除依赖关系,因为它们是临时定义的。当然,解析器生成的类型有可能与 Conf 中的类型不同,并且编译器将无法帮助您解决错误。
You have to combine the modules into one file and make them recursive. I don't believe there is a way to do this from the compilation process of two separate files.
EDIT: From your comment, I am guessing you have the type definition of the parser and the functions that process/operate on the type in the same file. I agree with you, it makes sense. But like you've experienced if that file is to not only operate on the type but call the parser to produce the data, how is the parser going to construct it? My solution has been to separate the type into it's own module and open that module in the module that does the operations.
Therefore, you are splitting
A
into (A
andA'
), whereA'
contains the type produced byB
, and used inA
. Your dependencies become,A
needsA'
andB
andC
B
needsA'
For example, I have a parser for configuration files I use to start up any application I write.
An alternative to all this is to use polymorphic variants. In this way you remove the dependency since they are defined ad-hoc. Of course, the type produced by the parser has the potential to be different then the one in Conf, and the compiler wont be able to help you with the error.
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 (...) ->
.mly
标头中的 struct 以及页脚中匹配的end
。但是,您必须重写生成的.mli
以添加module A : functor (...) -> sig
和end
作为构建过程的一部分。 (我知道我以前做过,为了解决你遇到的同样的问题,尽管我不记得在哪里,所以我无法给出现实生活中的例子。)另一种值得研究的可能性是从 Ocamlyacc 切换到 Menhir,它是 Ocamlyacc 的替代品(几乎不需要移植,因为语法是相同的),并且有一些可以帮助您的好功能,例如对参数化解析器模块(即函子)的支持。
The Ocaml language supports recursion between modules, but the compiler doesn't support recursion between compilation units. So you can't have
A.ml
needingB.ml
andB.ml
needingA.ml
. Refactoring to remove the recursion is best if you can do this easily, but let's assume you can't.One solution, as explained by nlucaroni, is to gather both modules into the same file and use
module rec
. Another solution sometimes is to turn one module into a functor, e.g., turnA
into a functorF
which takes an argument withB
's signature, and compile first the file definingF
, thenB
, then a file that just definesmodule A = F(B)
.Ocamlyacc does make things more complicated, but you can trick it! You can write
module A = functor (...) -> struct
in the.mly
header and the matchingend
in the footer. However you'll have to rewrite the generated.mli
to addmodule A : functor (...) -> sig
andend
as part of your build process. (I know I've done it before, to solve the same problem you're having, though I don't remember where so I can't give a real life example.)Another possibility worth investigating is switching from Ocamlyacc to Menhir, which is an Ocamlyacc replacement (with little to no porting required as the syntax is the same) with some nice features that could help you, such as support for parametrized parser modules (i.e. functors).