OCaml 交叉链接

发布于 2024-09-07 11:20:05 字数 651 浏览 5 评论 0原文

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

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

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

其中

  • A需要 BC
  • B 需要 A

我该如何进行编译?

由于使用 ocamlcocamlopt 顺序相关,如何修复 BA 之间的交叉引用?

我尝试首先使用 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 needs B and C
  • B needs A

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

赏烟花じ飞满天 2024-09-14 11:20:05

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

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 需要< 。

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

ConfType --包含类型 t  
Conf --调用解析器,并包含 ConfType.t 类型的辅助函数  
ConfParser——用于 ocamlyacc
ConfLexer——用于 ocamllex

所有这一切的替代方法是使用多态变体。通过这种方式,您可以删除依赖关系,因为它们是临时定义的。当然,解析器生成的类型有可能与 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.

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

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 and A'), where A' contains the type produced by B, and used in A. Your dependencies become,

  • A needs A' and B and C
  • B needs A'

For example, I have a parser for configuration files I use to start up any application I write.

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

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.

吝吻 2024-09-14 11:20:05

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 (...) -> sigend 作为构建过程的一部分。 (我知道我以前做过,为了解决你遇到的同样的问题,尽管我不记得在哪里,所以我无法给出现实生活中的例子。)

另一种值得研究的可能性是从 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 needing B.ml and B.ml needing A.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., turn A into a functor F which takes an argument with B's signature, and compile first the file defining F, then B, then a file that just defines module 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 matching end in the footer. However you'll have to rewrite the generated .mli to add module A : functor (...) -> sig and end 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文