The OCaml Toplevel
toplevel类似于 OCaml 的计算器或命令行界面。它类似于 Java 的 JShell,或者交互式 Python 解释器。topleve 对于尝试小段代码而不必启动 OCaml 编译器很方便。但不要过分依赖它,因为创建、编译和测试大型程序需要更强大的工具。一些其他语言可能会将toplevel称为 REPL,即读取-求值-打印-循环:它读取程序员输入,求值它,打印结果,然后重复。
在终端窗口中,输入 utop
以启动 toplevel。按下 Control-D 退出 toplevel。您也可以输入 #quit;;
然后按回车。请注意,您必须在那里输入 #
:这是除了您已经看到的 #
提示之外的内容。
类型和值
您可以在 OCaml toplevel输入表达式。用双分号 ;;
结束一个表达式,然后按回车键。OCaml 将求值表达式,告诉您结果值及其类型。例如:
# 42;;
- : int = 42
让我们解析 utop 的回应,从右到左阅读:
42
是该值。int
是该值的类型。- 该值没有被赋予名称,因此使用符号
-
。
那个 utop interaction 是作为这本书的一部分“硬编码”进去的。我们不得不输入所有的字符: #
, -
等等。但用来撰写这本书的基础设施实际上使我们能够编写代码,在将书翻译成 HTML 或 PDF 时由 OCaml 求值。从现在开始,这通常是我们要做的。它看起来像这样:
42
- : int = 42
第一个代码块中带有 42
的是我们要求 OCaml 运行的代码。如果您想将其输入到 utop 中,可以复制粘贴。在块的右上角有一个图标可以轻松完成这个操作。只需记得在末尾添加双分号。第二个代码块,稍微缩进,是 OCaml 在翻译书籍时的输出。
如果您在网络浏览器中查看此内容,请查找右上角的下载图标。选择
.md
选项,您将看到本书此页面的原始 MyST Markdown 源代码。您会发现,上面第二个示例的输出实际上并不存在于源代码中。这是好事!这意味着输出与我们用来构建本书的 OCaml 编译器的当前版本保持一致。这也意味着任何编译错误都可以作为构建本书的一部分来检测,而不是潜伏在那里等待您,亲爱的读者,去发现它们。
您可以通过 let
定义将值绑定到名称,如下所示:
let x = 42
val x : int = 42
再次,让我们解析这个回答,这次从左到右阅读:
- 一个值被绑定到一个名称,因此使用
val
关键字。 x
是绑定的值的名称。int
是该值的类型。42
是该值。
您可以将整个输出发音为“ x
的类型为 int
,等于 42
。”
Functions
可以使用以下类似的语法在 toplevel 定义一个函数:
let increment x = x + 1
val increment : int -> int = <fun>
让我们剖析一下这个回应:
increment
是与值绑定的标识符。int -> int
是值的类型。这是接受int
作为输入并产生int
作为输出的函数类型。将箭头 -> 视为一种将一个值转换为另一个值的视觉隐喻,这正是函数所做的。- 值是一个函数,utop选择不打印它(因为它现在已经被编译,并且在内存中有一个不容易适合漂亮打印的表示)。相反,toplevel打印
<fun>
,这只是一个占位符。
<fun>
本身不是一个值。它只是表示一个不可打印的函数值。
您可以使用以下语法“调用”函数:
increment 0
- : int = 1
increment(21)
- : int = 22
increment (increment 5)
- : int = 7
但在 OCaml 中,通常的术语是我们“应用”函数,而不是“调用”它。
注意 OCaml 在写括号或不写括号,以及写空格或不写空格方面是灵活的。学习 OCaml 时的一个挑战之一是弄清楚何时实际上需要括号。因此,如果发现自己遇到语法错误问题,一种策略是尝试添加一些括号。然而,首选风格通常是在不需要时省略括号。因此, increment 21
比 increment(21)
更好。
在toplevel加载代码
除了允许您定义函数外,toplevel还将接受指令,这些指令不是 OCaml 代码,而是告诉toplevel自身要执行某些操作。所有指令都以 #
字符开头。也许最常见的指令是 #use
,它会将文件中的所有代码加载到toplevel,就好像您在toplevel中键入了该文件中的代码一样。
例如,假设您创建了一个名为 mycode.ml
的文件。在该文件中放入以下代码:
let inc x = x + 1
启动toplevel。尝试输入以下表达式,并观察错误:
inc 3
File "[7]", line 1, characters 0-3:
1 | inc 3
^^^
Error: Unbound value inc
Hint: Did you mean incr?
错误发生是因为toplevel尚不知道名为 inc
的函数。现在向toplevel发出以下指令:
# #use "mycode.ml";;
请注意,上面的第 #
个字符表示给您的toplevel提示。第二个 #
字符是您键入的字符,用于告诉toplevel您正在发布指令。如果没有该字符,toplevel会认为您正在尝试应用名为 use
的函数。
现在再试一次:
inc 3
- : int = 4
toplevel的工作流程
使用存储在文件中的代码时,最佳工作流程是:
- 编辑文件中的代码。
- 在toplevel加载代码
#use
。 - 交互式地测试代码。
- 退出toplevel。警告:不要跳过这一步。
假设您想要修复代码中的一个错误。诱人的做法是不退出toplevel,编辑文件,并将
#use
指令重新发出到同一toplevel会话中。抵制这种诱惑。在同一会话中从早期#use
指令加载的“陈旧代码”可能会导致一些令人惊讶的事情发生——尤其是在您初学该语言时。因此,在重新使用文件之前,始终退出toplevel。