为什么我的ocaml模块没有定义?

为什么我的ocaml模块没有定义?,ocaml,Ocaml,我用ocamlex和menhir构建了一个lexer和parser,当我在顶层使用它们时,它们可以工作,但它们构成的模块仍然没有定义 ~: ocamlbuild -clean ~: ocamlbuild PhoebeParser.cma PhoebeLexer.cma ocamlopt.opt unix.cmxa -I /Users/Tim/.opam/system/lib/ocamlbuild /Users/Tim/.opam/system/lib/ocamlbuild/ocamlbuild

我用ocamlex和menhir构建了一个lexer和parser,当我在顶层使用它们时,它们可以工作,但它们构成的模块仍然没有定义

~: ocamlbuild -clean
~: ocamlbuild PhoebeParser.cma PhoebeLexer.cma 
ocamlopt.opt unix.cmxa -I /Users/Tim/.opam/system/lib/ocamlbuild /Users/Tim/.opam/system/lib/ocamlbuild/ocamlbuildlib.cmxa myocamlbuild.ml /Users/Tim/.opam/system/lib/ocamlbuild/ocamlbuild.cmx -o myocamlbuild
menhir --infer --raw-depend --ocamldep 'ocamldep.opt -modules' PhoebeParser.mly > PhoebeParser.mly.depends
ocamldep.opt -modules PhoebeAST.ml > PhoebeAST.ml.depends
ocamlc.opt -c -o PhoebeAST.cmo PhoebeAST.ml
menhir --ocamlc ocamlc.opt --infer PhoebeParser.mly
ocamldep.opt -modules PhoebeParser.mli > PhoebeParser.mli.depends
ocamlc.opt -c -o PhoebeParser.cmi PhoebeParser.mli
ocamldep.opt -modules PhoebeParser.ml > PhoebeParser.ml.depends
ocamlc.opt -c -o PhoebeParser.cmo PhoebeParser.ml
ocamlc.opt -a PhoebeAST.cmo PhoebeParser.cmo -o PhoebeParser.cma
ocamldep.opt -modules PhoebeLexer.mli > PhoebeLexer.mli.depends
ocamlc.opt -c -o PhoebeLexer.cmi PhoebeLexer.mli
ocamllex.opt -q PhoebeLexer.mll
ocamldep.opt -modules PhoebeLexer.ml > PhoebeLexer.ml.depends
ocamlc.opt -c -o PhoebeLexer.cmo PhoebeLexer.ml
ocamlc.opt -a PhoebeAST.cmo PhoebeParser.cmo PhoebeLexer.cmo -o PhoebeLexer.cma
~: cd _build/
~/_build: ocaml
        OCaml version 4.04.0

# PhoebeParser.phoebe_spec;;
Characters -1--1:
  PhoebeParser.phoebe_spec;;

Error: Reference to undefined global `PhoebeParser'
# PhoebeLexer.phoebe_lexer;;
Characters -1--1:
  PhoebeLexer.phoebe_lexer;;

Error: Reference to undefined global `PhoebeLexer'
# 

我做错了什么?

您的模块被编译并归档在
PhoebeLexer.cma
PhoebeParser.cma
文件中。每个模块还附带一个描述其接口的
.cmi
文件。要将模块加载到顶层,可以使用
#load
#load#u rec
#use
指令没有用处,因为它在源代码级别上运行(可以将其视为复制粘贴的快捷方式)

因此,在您的情况下,顶级交互应该如下所示(假设您在
\u build
文件夹中启动顶级交互):

我可以加载的模块和我可以打开的模块有什么区别

我喜欢具体的问题

打开模块
M
时,顶层将在当前目录、安装OCaml的目录以及使用
#directory
指令明确添加的所有目录中搜索文件
M.cmi
cmi
文件包含一个机器可读的压缩模块接口(您可能认为它是一个编译模块接口)。此文件定义已加载模块的类型。您甚至可以在不加载模块的情况下访问模块的类型。要获得模块的定义,需要加载实现。存储在
cmo
(编译模块对象文件)或
cma
(编译模块存档)中的。
cma
文件只是多个
cmo
的容器。
cmo
文件包含实际代码,可以加载这些代码并与主程序链接(在本例中是顶级程序)

正如您可能注意到的,接口和实现是完全不同的实体,可以独立加载。界面是隐式查找的,您不需要手动加载它,但有时您需要更改目录(通过在特定目录中加载顶级目录,或使用
#cd
指令),或使用
#directory
指令将目录添加到搜索路径。应始终使用
#load
指令显式加载实现

如果cmi可用,但未加载实现,则会得到一些未定义的值。如果cmi不可用,则尝试访问在缺少cmi文件的模块接口中声明的值时,将发出
未绑定值
错误(即使您加载了模块存档本身)


总而言之:接口描述了什么是可用的,实现定义了它在哪里可用。如果该值不在接口中,则该值是未绑定的;如果该值在接口中,但未找到定义,则该值未定义

你不是说加载PhoebeLexer.cma吗(当然还有
解析器
)在你的答案的最后?该死,它发生了)谢谢,@Virgile!只要我在做顶级交互,加载就行了。但我还是不明白为什么PhoebeParser和PhoebeLexer是未定义的全局变量。我可以加载的模块和我可以打开的模块有什么区别?好问题!我已经用详细的解释更新了答案。
# #load "PhoebeLexer.cma";;
# #load "PhoebeParser.cma";;