第 14 章 顶层系统或 REPL (ocaml)

本章描述 ocaml,OCaml 的顶层系统,它允许通过读取-求值-打印循环 (REPL) 以交互方式使用 OCaml 系统。在这种模式下,系统重复从输入读取 OCaml 短语,然后对其进行类型检查、编译和求值,然后打印推断出的类型和结果值(如果有)。标准输入上的文件结尾终止 ocaml

顶层输入可以跨越多行。它在系统打印的 #(井号)提示符之后开始,并以 ;;(双分号)结尾,后跟可选的空格和注释以及行尾。顶层输入由一个或多个顶层短语组成,语法如下

顶层输入::={ 定义 } ;;
 表达式;;
 #标识符 [ 指令参数 ] ;;
 
指令参数::= 字符串字面量
 整数字面量
 值路径
 true ∣ false

一个短语可以由一系列定义组成,就像在编译单元的实现或 structend 模块表达式中找到的那样。这些定义可以绑定值名称、类型名称、异常、模块名称或模块类型名称。顶层系统执行这些绑定,然后打印由此定义的名称的类型和值(如果有)。

一个短语也可以由值表达式组成(第 ‍11.7 节)。它只是简单地求值而无需执行任何绑定,并打印其值。

最后,一个短语也可以由一个顶层指令组成,以 #(井号)开头。这些指令控制顶层的行为;它们在下面的第 ‍14.2 节中列出。

Unix:  顶层系统由以下命令启动 ocaml
        ocaml options objects                # interactive mode
        ocaml options objects scriptfile        # script mode
选项 在下面描述。 对象 是以 .cmo.cma 结尾的文件名;在设置 选项 后,它们会立即加载到解释器中。 脚本文件 是任何不以 .cmo.cma 结尾的文件名。

如果命令行上没有给出 脚本文件,则顶层系统进入交互模式:从标准输入读取短语,结果打印到标准输出,错误打印到标准错误。标准输入上的文件结尾终止 ocaml(另请参见第 ‍14.2 节中的 #quit 指令)。

在启动时(在读取第一个短语之前),初始化文件的内容被读取为一系列 OCaml 短语并根据第 ‍14.2 节中描述的 #use 指令执行。每个短语的求值结果代码不会显示。

初始化文件是首先找到的

  1. 当前目录中的 .ocamlinit
  2. 如果 XDG_CONFIG_HOME 是绝对路径,则为 XDG_CONFIG_HOME/ocaml/init.ml
  3. 否则,在 Unix 上,HOME/ocaml/init.ml 或在 Windows 上,LocalAppData 下的 ocaml\init.ml(例如 C:\Users\Bactrian\AppData\Local\ocaml\init.ml);
  4. XDG_CONFIG_DIRS 中任何绝对路径下的 ocaml/init.mlXDG_CONFIG_DIRS 中的路径在 Unix 上以冒号分隔,在 Windows 上以分号分隔;
  5. 如果 XDG_CONFIG_DIRS 不包含任何绝对路径,则为 Unix 上的 /usr/xdg/ocaml/init.ml 或 Windows 上 LocalAppData(例如 C:\Users\Bactrian\AppData\Local)、RoamingAppData(例如 C:\Users\Bactrian\AppData\Roaming)或 ProgramData(例如 C:\ProgramData)下的 ocaml\init.ml
  6. 如果 HOME 非空,则为 HOME/.ocamlinit

顶层系统不执行行编辑,但可以轻松地与外部行编辑器(如 leditrlwrap)一起使用。还提供了改进的顶层系统 utop。另一种选择是在 Gnu Emacs 下使用 ocaml,它提供了 Emacs 的全部编辑功能(来自库 inf-caml 的命令 run-caml)。

在任何时候,都可以通过按下 ctrl-C(或更准确地说,通过向 ocaml 进程发送 INTR 信号)来中断当前短语的解析、编译或求值。然后顶层立即返回到 # 提示符。

如果在命令行上向 ocaml 给出了 脚本文件,则顶层系统进入脚本模式:文件的内容被读取为一系列 OCaml 短语并执行,如同 #use 指令(第 ‍14.2 节)一样。求值结果不会打印。到达文件末尾时,ocaml 命令立即退出。不会从标准输入读取任何命令。 Sys.argv 会被转换,忽略所有 OCaml 参数,并在 Sys.argv.(0) 中以脚本文件名开头。

在脚本模式下,如果脚本的第一行以 #! 开头,则会被忽略。因此,应该可以使脚本本身可执行,并将 #!/usr/local/bin/ocaml 作为第一行,从而在运行脚本时自动调用顶层系统。但是,在大多数 OCaml 安装中,ocaml 本身就是一个 #! 脚本,并且 Unix 内核通常不处理嵌套的 #! 脚本。更好的解决方案是将以下内容作为脚本的第一行

        #!/usr/local/bin/ocamlrun /usr/local/bin/ocaml

1 选项

以下命令行选项被 ocaml 命令识别。

-absname
强制错误消息显示文件名的绝对路径。
-no-absname
不要尝试在错误消息中显示绝对文件名。
-args 文件名
文件名 中读取其他以换行符结尾的命令行参数。无法通过文件将 脚本文件 传递到顶层。
-args0 文件名
文件名 中读取其他以空字符结尾的命令行参数。无法通过文件将 脚本文件 传递到顶层。
-I 目录
将给定目录添加到用于搜索源文件和编译文件的目录列表中。默认情况下,首先搜索当前目录,然后搜索标准库目录。使用 -I 添加的目录在当前目录之后搜索,按照它们在命令行上给出的顺序,但在标准库目录之前。另请参见选项 -nostdlib

如果给定目录以 + 开头,则将其视为相对于标准库目录。例如,-I +unix 将标准库的子目录 unix 添加到搜索路径。

顶层运行后,也可以使用 #directory 指令(第 ‍14.2 节)将目录添加到列表中。

-init 文件
加载给定文件而不是默认初始化文件。默认初始化文件是首先找到的
  1. 当前目录中的 .ocamlinit
  2. 如果 XDG_CONFIG_HOME 是绝对路径,则为 XDG_CONFIG_HOME/ocaml/init.ml
  3. 否则,在 Unix 上,HOME/ocaml/init.ml 或在 Windows 上,LocalAppData 下的 ocaml\init.ml(例如 C:\Users\Bactrian\AppData\Local\ocaml\init.ml);
  4. XDG_CONFIG_DIRS 中任何绝对路径下的 ocaml/init.mlXDG_CONFIG_DIRS 中的路径在 Unix 上以冒号分隔,在 Windows 上以分号分隔;
  5. 如果 XDG_CONFIG_DIRS 不包含任何绝对路径,则为 Unix 上的 /usr/xdg/ocaml/init.ml 或 Windows 上 LocalAppData(例如 C:\Users\Bactrian\AppData\Local)、RoamingAppData(例如 C:\Users\Bactrian\AppData\Roaming)或 ProgramData(例如 C:\ProgramData)下的 ocaml\init.ml
  6. 如果 HOME 非空,则为 HOME/.ocamlinit
-labels
在类型中不忽略标签,标签可以在应用程序中使用,并且可以在任何顺序给出带标签的参数。这是默认设置。
-no-app-funct
停用函子的应用行为。使用此选项,每个函子应用在其结果中生成新的类型,并且将同一个函子应用两次到同一个参数会产生两个不兼容的结构。
-noassert
不编译断言检查。请注意,特殊形式 assert false 始终编译,因为它具有特殊的类型。
-nolabels
在类型中忽略非可选标签。标签不能在应用程序中使用,参数顺序变得严格。
-noprompt
等待输入时不显示任何提示符。
-nopromptcont

在多行输入等待续行时,不要显示辅助提示。例如,在 emacs 窗口中运行 ocaml 时应该使用此选项。
-nostdlib
不要将标准库目录包含在搜索源文件和编译文件的目录列表中。
-ppx 命令
解析后,将抽象语法树通过预处理器 命令 传递。模块 Ast_mapper(在第 30 Ast_mapper 中描述)实现了预处理器的外部接口。
-principal
在类型检查期间检查信息路径,以确保所有类型都以主要方式导出。当使用带标签的参数和/或多态方法时,此标志是必需的,以确保编译器的未来版本能够正确推断类型,即使内部算法发生变化。所有在 -principal 模式下接受的程序也在默认模式下接受,具有等效的类型但不同的二进制签名,这可能会减慢类型检查速度;但是,在发布源代码之前使用它是一个好主意。
-rectypes
在类型检查期间允许任意递归类型。默认情况下,仅支持递归通过对象类型进行的递归类型。
-safe-string
强制执行类型 stringbytes 之间的分离,从而使字符串只读。这是默认设置,从 OCaml 5.0 开始强制执行。
-safer-matching
不要使用类型信息来优化模式匹配。这允许检测匹配失败,即使模式匹配被错误地认为是详尽的。这仅影响 GADT 和多态变体编译。
-short-paths
当一个类型在多个模块路径下可见时,在打印推断的接口以及错误和警告消息中的类型名称时,使用最短的路径。以下划线 _ 开头或包含双下划线 __ 的标识符名称在计算其长度时会产生 +10 的惩罚。
-stdin
将标准输入作为脚本文件读取,而不是启动交互式会话。
-strict-sequence
强制每个序列的左侧部分具有类型 unit。
-strict-formats
拒绝在旧版格式实现中接受的无效格式。您应该使用此标志来检测和修复此类无效格式,因为它们将在未来的 OCaml 版本中被拒绝。
-unsafe
关闭数组和字符串访问的边界检查(v.(i)s.[i] 构造)。因此,使用 -unsafe 编译的程序速度更快,但也不安全:如果程序访问数组或字符串超出其边界,任何事情都可能发生。
-unsafe-string
识别类型 stringbytes,从而使字符串可写。这旨在与旧的源代码兼容,不应与新软件一起使用。此选项从 OCaml 5.0 开始无条件地引发错误。
-v
打印编译器的版本号和标准库目录的位置,然后退出。
-verbose
在执行之前打印所有外部命令,有助于调试 C 库问题。
-version
打印版本字符串并退出。
-vnum
打印简短版本号并退出。
-no-version
启动时不打印版本横幅。
-w 警告列表
启用、禁用或将参数 警告列表 指定的警告标记为致命。每个警告都可以启用禁用,并且每个警告都可以致命非致命。如果禁用警告,则不会显示它,也不会以任何方式影响编译(即使它是致命的)。如果启用了警告,则编译器会在源代码触发它时正常显示它。如果它已启用且为致命,则编译器还会在显示它后停止并显示错误。

警告列表 参数是警告说明符的序列,它们之间没有分隔符。警告说明符是以下之一

+数字
启用警告编号 数字
-数字
禁用警告编号 数字
@数字
启用并标记为致命警告编号 数字
+数字1..数字2
启用给定范围内的警告。
-数字1..数字2
禁用给定范围内的警告。
@数字1..数字2
启用并标记为给定范围内的致命警告。
+字母
启用与 字母 对应的警告集。字母可以是大写或小写。
-字母
禁用与 字母 对应的警告集。字母可以是大写或小写。
@字母
启用并标记为与 字母 对应的警告集的致命警告。字母可以是大写或小写。
大写字母
启用与 大写字母 对应的警告集。
小写字母
禁用与 小写字母 对应的警告集。

或者, 警告列表 可以使用其助记符名称(见下文)指定单个警告,如下所示

+名称
启用警告 名称
-名称
禁用警告 名称
@名称
启用并标记为致命警告 名称

当前未定义的警告编号、字母和名称将被忽略。警告如下(每个编号后面的名称指定该警告的助记符)。

1 comment-start
可疑的注释开始标记。
2 comment-not-end
可疑的注释结束标记。
3
“已弃用”警报的已弃用同义词。
4 fragile-match
脆弱的模式匹配:即使将其他构造函数添加到匹配的变体类型之一中,匹配也将保持完整。
5 ignored-partial-application
部分应用函数:结果具有函数类型且被忽略的表达式。
6 labels-omitted
在函数应用中省略标签。
7 method-override
覆盖方法。
8 partial-match
部分匹配:模式匹配中缺少的案例。
9 missing-record-field-pattern
记录模式中缺少字段。
10 non-unit-statement
序列左侧的表达式类型不是 unit(并且不是函数,请参见警告编号 5)。
11 redundant-case
模式匹配中的冗余情况(未使用匹配情况)。
12 redundant-subpat
模式匹配中的冗余子模式。
13 instance-variable-override
覆盖实例变量。
14 illegal-backslash
字符串常量中的非法反斜杠转义。
15 implicit-public-methods
私有方法隐式公开。
16 unerasable-optional-argument
不可擦除的可选参数。
17 undeclared-virtual-method
未声明的虚方法。
18 not-principal
非主要类型。
19 non-principal-labels
没有主要性的类型。
20 ignored-extra-argument
未使用的函数参数。
21 nonreturning-statement
非返回语句。
22 preprocessor
预处理器警告。
23 useless-record-with
无用的记录 with 子句。
24 bad-module-name
错误的模块名称:源文件名不是有效的 OCaml 模块名称。
25
已忽略:现在是警告 8 的一部分。
26 unused-var
可疑的未使用变量:使用 letas 绑定的未使用变量,并且不以下划线 (_) 字符开头。
27 unused-var-strict
无害的未使用变量:未使用 letas 绑定的未使用变量,并且不以下划线 (_) 字符开头。
28 wildcard-arg-to-constant-constr
通配符模式作为常量构造函数的参数给出。
29 eol-in-string
字符串常量中未转义的行尾(不可移植的代码)。
30 duplicate-definitions
在两个相互递归的类型中定义了两个相同名称的标签或构造函数。
31 module-linked-twice
同一个可执行文件中链接了同一个模块两次。
I
已忽略:现在是硬错误(从 5.1 开始)。
32 unused-value-declaration
未使用的值声明。(从 4.00 开始)

33 unused-open
未使用的 open 语句。(自 4.00 版本起)
34 unused-type-declaration
未使用的类型声明。(自 4.00 版本起)
35 unused-for-index
未使用的 for 循环索引。(自 4.00 版本起)
36 unused-ancestor
未使用的祖先变量。(自 4.00 版本起)
37 unused-constructor
未使用的构造函数。(自 4.00 版本起)
38 unused-extension
未使用的扩展构造函数。(自 4.00 版本起)
39 unused-rec-flag
未使用的 rec 标志。(自 4.00 版本起)
40 name-out-of-scope
构造函数或标签名称在作用域外使用。(自 4.01 版本起)
41 ambiguous-name
构造函数或标签名称不明确。(自 4.01 版本起)
42 disambiguated-name
消歧的构造函数或标签名称(兼容性警告)。(自 4.01 版本起)
43 nonoptional-label
非可选标签用作可选标签。(自 4.01 版本起)
44 open-shadow-identifier
open 语句隐藏了已定义的标识符。(自 4.01 版本起)
45 open-shadow-label-constructor
open 语句隐藏了已定义的标签或构造函数。(自 4.01 版本起)
46 bad-env-variable
环境变量错误。(自 4.01 版本起)
47 attribute-payload
属性有效负载非法。(自 4.02 版本起)
48 eliminated-optional-arguments
隐式消除可选参数。(自 4.02 版本起)
49 no-cmi-file
在查找模块别名时缺少 cmi 文件。(自 4.02 版本起)
50 unexpected-docstring
意外的文档注释。(自 4.03 版本起)
51 wrong-tailcall-expectation
函数调用使用不正确的 @tailcall 属性进行注释。(自 4.03 版本起)
52 fragile-literal-pattern (参见 13.5.3)
脆弱的常量模式。(自 4.03 版本起)
53 misplaced-attribute
属性不能出现在此上下文中。(自 4.03 版本起)
54 duplicated-attribute
属性在一个表达式中使用了多次。(自 4.03 版本起)
55 inlining-impossible
内联不可能。(自 4.03 版本起)
56 unreachable-case
模式匹配中无法到达的 case(基于类型信息)。(自 4.03 版本起)
57 ambiguous-var-in-pattern-guard (参见 13.5.4)
保护下的歧义或模式变量。(自 4.03 版本起)
58 no-cmx-file
缺少 cmx 文件。(自 4.03 版本起)
59 flambda-assignment-to-non-mutable-value
对不可变值进行赋值。(自 4.03 版本起)
60 unused-module
未使用的模块声明。(自 4.04 版本起)
61 unboxable-type-in-prim-decl
基本类型声明中不可装箱的类型。(自 4.04 版本起)
62 constraint-on-gadt
GADT 类型声明上的类型约束。(自 4.06 版本起)
63 erroneous-printed-signature
错误的打印签名。(自 4.08 版本起)
64 unsafe-array-syntax-without-parsing
-unsafe 与返回语法树的预处理器一起使用。(自 4.08 版本起)
65 redefining-unit
类型声明定义新的 '()' 构造函数。(自 4.08 版本起)
66 unused-open-bang
未使用的 open! 语句。(自 4.08 版本起)
67 unused-functor-parameter
未使用的函子参数。(自 4.10 版本起)
68 match-on-mutable-state-prevent-uncurry
依赖于可变状态的模式匹配阻止剩余的参数被展开。(自 4.12 版本起)
69 unused-field
未使用的记录字段。(自 4.13 版本起)
70 missing-mli
缺少接口文件。(自 4.13 版本起)
71 unused-tmc-attribute
未使用的 @tail_mod_cons 属性。(自 4.14 版本起)
72 tmc-breaks-tailcall
@tail_mod_cons 变换将尾调用转换为非尾调用。(自 4.14 版本起)
73 generative-application-expects-unit
生成式函子应用于空结构体(struct end)而不是 ()。(自 5.1 版本起)
A
所有警告
C
警告 1、2。
D
警告 3 的别名。
E
警告 4 的别名。
F
警告 5 的别名。
K
警告 32、33、34、35、36、37、38、39。
L
警告 6 的别名。
M
警告 7 的别名。
P
警告 8 的别名。
R
警告 9 的别名。
S
警告 10 的别名。
U
警告 11、12。
V
警告 13 的别名。
X
警告 14、15、16、17、18、19、20、21、22、23、24、30。
Y
警告 26 的别名。
Z
警告 27 的别名。

默认设置是 -w +a-4-6-7-9-27-29-32..42-44-45-48-50-60。它由 -help 显示。请注意,警告 5 和 10 并非始终触发,具体取决于类型检查器的内部机制。

-warn-error 警告列表
将参数 警告列表 中指定的警告标记为致命错误。当发出其中一个警告时,编译器将停止并报错。警告列表-w 选项的含义相同:+ 号(或大写字母)将相应的警告标记为致命错误,- 号(或小写字母)将其恢复为非致命警告,@ 号同时启用并将其标记为致命警告。

注意:不建议在生产代码中使用警告集(即字母)作为 -warn-error 的参数,因为这可能会在 OCaml 的未来版本添加一些新的警告时破坏您的构建。

默认设置是 -warn-error -a(没有警告是致命的)。

-warn-help
显示所有可用警告编号的描述。
- 文件
即使文件名称以连字符 (-) 开头,也使用 文件 作为脚本文件名。
-help--help
显示简短的使用摘要并退出。
Unix:  还将参考以下环境变量
OCAMLTOP_INCLUDE_PATH
搜索编译后的目标代码文件(.cmi.cmo.cma)的其他目录。在搜索通过 -I 在命令行上指定的包含目录后,将从左到右考虑指定的目录。自 OCaml 4.08 版本起可用。
OCAMLTOP_UTF_8
打印字符串值时,如果 OCAMLTOP_UTF_8 设置为 false,则非 ASCII 字节(> \0x7E)将打印为十进制转义序列。否则,它们将打印为未转义的。
TERM
打印错误消息时,顶级系统尝试在视觉上为错误位置添加下划线。它会查询 TERM 变量以确定输出终端的类型并在终端数据库中查找其功能。
XDG_CONFIG_HOMEHOMEXDG_CONFIG_DIRS
初始化文件查找过程(见上文)。

2 顶级指令

以下指令控制顶级行为、加载内存中的文件并跟踪程序执行。

注意:所有指令都以 #(井号)符号开头。此 # 必须在指令之前键入,并且不能与交互式循环显示的 # 提示符混淆。例如,键入 #quit;; 将退出顶级循环,但键入 quit;; 将导致“未绑定值 quit”错误。

常规
#help;;
打印所有可用指令的列表,并在适当情况下提供相应的参数类型。
#quit;;
退出顶级循环并终止 ocaml 命令。
加载代码
#cd "目录名称";;
更改当前工作目录。
#directory "目录名称";;
将给定目录添加到搜索源文件和编译文件的目录列表中。
#remove_directory "目录名称";;
从搜索源文件和编译文件的目录列表中删除给定目录。如果列表不包含给定目录,则不执行任何操作。
#load "文件名";;
将由批处理编译器ocamlc生成的字节码对象文件(.cmo文件)或库文件(.cma文件)加载到内存中。
#load_rec "文件名";;
将由批处理编译器ocamlc生成的字节码对象文件(.cmo文件)或库文件(.cma文件)加载到内存中。加载依赖于尚未加载的其他模块的对象文件时,会递归搜索并加载这些模块的 .cmo 文件。加载顺序未指定。
#use "文件名";;
读取、编译并执行给定文件中的源代码片段。这是文本包含:片段的处理方式与在标准输入中键入它们一样。在遇到第一个错误时,文件读取停止。
#use_output "命令";;
执行命令并评估其输出,就好像它已捕获到文件并传递给#use一样。
#mod_use "文件名";;
类似于#use,但还将代码包装到一个顶级模块中,该模块的名称与不带扩展名的文件名的首字母大写相同,遵循编译器的语义。

对于将文件名作为参数的指令,如果给定的文件名未指定目录,则将在以下目录中搜索文件

  1. 在脚本模式下,包含当前正在执行的脚本的目录;在交互模式下,当前工作目录。
  2. 使用#directory指令添加的目录。
  3. 使用-I选项在命令行上给出的目录。
  4. 标准库目录。
环境查询
#show_class 类路径;;
#show_class_type 类路径;;
#show_exception 标识符;;
#show_module 模块路径;;
#show_module_type 模块类型路径;;
#show_type 类型构造器;;
#show_val 值路径;;
打印相应组件的签名。
#show 标识符;;
打印所有上述类别中名称为标识符的组件的签名。
漂亮打印
#install_printer 打印机名称;;
此指令将名为打印机名称(值路径)的函数注册为打印机的函数,用于类型与函数的参数类型匹配的值。也就是说,当顶级循环需要打印此类值时,它将调用打印机名称

打印函数打印机名称的类型应为Format.formatter -> t -> unit,其中t是要打印的值的类型,并且应使用Format库提供的函数在给定的格式化程序上输出类型为t的值的文本表示形式。为了向后兼容,打印机名称也可以具有类型t -> unit,然后应在标准格式化程序上输出,但此用法已弃用。

#print_depth n;;
将值的打印限制为最大深度n。深度超过n的值的部分将打印为...(省略号)。
#print_length n;;
将打印的值节点数量限制为最多n个。值的其余部分将打印为...(省略号)。
#remove_printer 打印机名称;;
从顶级打印机表中删除指定的函数。
跟踪
#trace 函数名称;;
执行此指令后,对名为函数名称的函数的所有调用都将被“跟踪”。也就是说,将显示每次调用的参数和结果,以及从函数中跳出的异常,这些异常是由函数本身或它调用的另一个函数引发的。如果函数是柯里化的,则每个参数在传递给函数时都会被打印。
#untrace 函数名称;;
停止跟踪给定的函数。
#untrace_all;;
停止跟踪迄今为止跟踪的所有函数。
编译器选项
#debug 布尔值;;
启用/禁用调试事件的插入。默认值为true
#labels 布尔值;;
如果参数为false,则忽略函数类型中的标签,或者如果参数为true,则切换回默认行为(交换样式)。
#ppx "文件名";;
解析后,通过预处理器命令传递抽象语法树。
#principal 布尔值;;
如果参数为true,则在类型检查期间检查信息路径,以确保所有类型都以主要方式派生。如果参数为false,则不检查信息路径。
#rectypes;;
在类型检查期间允许任意递归类型。注意:启用后,此选项将无法禁用,因为这会导致类型系统不健全。
#warn_error "警告列表";;
将参数启用的警告视为错误,将参数禁用的警告视为正常警告。
#warnings "警告列表";;
根据参数启用或禁用警告。

3 顶级环境和模块系统

顶级环境代码段可以使用与单独编译单元相同的机制引用编译单元中定义的标识符:要么使用限定名称(Modulename.localname),要么使用open构造和非限定名称(参见第 ‍11.3节)。

但是,在引用另一个编译单元之前,必须在内存中存在该单元的实现。启动时,顶级环境系统包含标准库中所有模块的实现。可以使用上面描述的#load指令输入用户模块的实现。引用未提供实现的单元会导致错误Reference to undefined global `...'

请注意,输入open Mod仅访问Mod的编译接口(.cmi文件),但不会加载Mod的实现,并且如果未加载Mod的任何实现,则不会导致任何错误。只有在执行引用Mod的值或模块定义时,才会出现“引用未定义全局Mod”错误。

4 常见错误

本节描述并解释了最常遇到的错误消息。

找不到文件文件名
在当前目录或搜索路径的目录中都找不到指定的文件。

如果文件名具有mod.cmi格式,则表示您已引用编译单元mod,但找不到其编译接口。解决方法:首先编译mod.mlimod.ml,以创建编译接口mod.cmi

如果文件名具有mod.cmo格式,则表示您正在尝试使用#load加载尚不存在的字节码对象文件。解决方法:首先编译mod.ml

如果您的程序跨越多个目录,则也可能出现此错误,因为您尚未指定要查找的目录。解决方法:使用#directory指令将正确的目录添加到搜索路径。

此表达式的类型为t1,但被用作类型t2
参见第 ‍13.4节。
引用未定义全局mod
您忘记使用#load加载模块的实现到内存中。参见上面第 ‍14.3节。

5 构建自定义顶级环境系统:ocamlmktop

ocamlmktop命令构建包含在启动时预加载的用户代码的OCaml顶级环境。

ocamlmktop命令将一组.cmo.cma文件作为参数,并将它们与实现OCaml顶级环境的对象文件链接起来。典型用法是

        ocamlmktop -o mytoplevel foo.cmo bar.cmo gee.cmo

这将创建字节码文件mytoplevel,其中包含OCaml顶级环境系统以及来自三个.cmo文件的代码。此顶级环境可以直接执行,并通过以下方式启动

        ./mytoplevel

这会进入一个常规的顶层循环,除了来自 foo.cmobar.cmogee.cmo 的代码已经加载到内存中,就像您键入了

        #load "foo.cmo";;
        #load "bar.cmo";;
        #load "gee.cmo";;

进入顶层时一样。不过,模块 FooBarGee 并没有打开;如果您希望这样做,您仍然需要自己进行

        open Foo;;

操作。

5.1 选项

ocamlmktop 识别以下命令行选项。

-cclib libname
在“自定义运行时”模式下链接时,将 -llibname 选项传递给 C 链接器。请参阅第 ‍13章中 ocamlc 的相应选项。
-ccopt option
在“自定义运行时”模式下链接时,将给定的选项传递给 C 编译器和链接器。请参阅第 ‍13章中 ocamlc 的相应选项。
-custom
在“自定义运行时”模式下链接。请参阅第 ‍13章中 ocamlc 的相应选项。
-I 目录
将给定目录添加到用于搜索编译后的目标代码文件(.cmo.cma)的目录列表中。
-o exec-file
指定链接器生成的顶层文件的名称。默认值为 a.out

6 本机顶层:ocamlnat(实验性)

本节描述一个尚未正式支持但可能很有用的工具。

在传统顶层系统中执行的 OCaml 代码使用字节码解释器。当需要提高性能或测试仅在编译为本地代码时才能正确执行的程序时,可以使用本机顶层

对于大多数安装,本机顶层不会与 OCaml 工具链的其余部分一起安装。在这种情况下,需要从源代码构建 OCaml 发行版。从发行版的已构建源代码树中,您可以使用 make natruntop 来构建和执行本机顶层。(或者可以使用 make ocamlnat,它只执行构建步骤。)

如果在构建本机顶层后运行 make install 命令,则可以直接调用 ocamlnat 程序(来自源代码或安装目录),而不是使用 make natruntop