toplevel 就像 OCaml 的计算器或命令行界面。它类似于 Java 中的JShell或交互式 Python 解释器。toplevel 可以方便地尝试小段代码,而无需启动 OCaml 编译器。但不要太依赖它,因为创建、编译和测试大型程序将需要更强大的工具。其他一些语言会将 toplevel 称为REPL,它代表 read-eval-print-loop:它读取程序员输入,执行,打印结果,然后重复。
在终端窗口中,键入utop
以启动 toplevel 。按 Control-D 退出 toplevel 。你也可以输入#quit;;
并按回车键。注意,这里 “#” 不是提示符的 # 。
类型和值
你可以在OCaml toplevel 中输入表达式。使用双分号 ;;
结束表达式,并按回车键。然后OCaml将计算表达式,告诉你结果值和值的类型。例如:
# 42;;
- : int = 42
让我们来仔细研究 utop 响应,从右到左阅读:
-
`42` 是值。
-
int
是值的类型。 -
该值没有命名,因此使用符号
-
。
这种 utop 交互是本书“硬编码”的一部分。我们必须输入所有字符:#
、-
等。但是,用于编写本书的基础设施实际上使我们能够在书被翻译成HTML或PDF时由OCaml执行我们编写的代码。从现在开始,这就是我们通常要做的。它看起来是这样的:
42
- : int = 42
第一个包含 42
的代码块是我们让OCaml运行的代码。如果你想在 utop 中输入,可以复制粘贴。在区块的右上角有一个图标,可以很容易地做到这一点。只需要记住在结尾加上双分号。第二个代码块缩进了一点,是翻译本书时OCaml的输出。
如果你在web浏览器中查看,请在右上角寻找下载图标。选择
.md
选项,你将看到本书这一页的原始[MyST Markdown][myst-parser.readthedocs.io/en/latest/]…
可以使用 let
定义将值绑定到名称上,如下所示:
let x = 42
val x : int = 42
让我们再次来仔细研究 utop 响应,这次从左到右阅读:
-
一个值被绑定到名称上,因此有
val
关键字。 -
x
是值绑定到的名称。 -
int
是值的类型。 -
42
是值。
你可以把整个输出读成“x
是 int
类型,等于 42
”。
函数
函数可以使用如下语法在 toplevel 中定义:
let increment x = x + 1
val increment : int -> int = <fun>
我们来研究一下这个响应:
-
increment
是值绑定的标识符。 -
int -> int
是值的类型。这是一种接受int
作为输入,并产生int
作为输出的函数。把箭头->
看作是一种视觉隐喻,表示从一个值转换到另一个值——这就是函数所做的事情。 -
该值是一个函数,toplevel 选择不打印它(因为它现在已经被编译,并且在内存中有一种表示形式,不容易进行美化输出)。相反,toplevel 仅打印一个占位符
<fun>
。
<fun>
本身不是一个值。它仅指示一个不可打印的函数值。
你可以像下面这样“调用”函数:
increment 0
increment(21)
increment (increment 5)
但是,在OCaml中,通常的词汇是“应用”函数,而不是“调用”函数。
请注意,OCaml在是否写括号以及是否写空格方面非常灵活。第一次学习OCaml的挑战之一是弄清楚什么时候需要括号。因此,如果你发现自己有语法错误的问题,一个策略是尝试添加一些括号。不过,首选的样式通常是在不需要的时候省略括号。所以, increment 21
比 increment(21)
更好。
在 toplevel 加载代码
除了允许你定义函数之外,toplevel 还接受不是OCaml代码而是告诉 toplevel 自身做某事的指令。所有指令都以 #
字符开头。也许最常见的指令是 #use
,它将文件中的所有代码加载到 toplevel 中,就像你将该文件中的代码输入到 toplevel 中一样。
例如,假设你创建了一个名为 mycode.ml
的文件。在该文件中放入以下代码:
let inc x = x + 1
启动 toplevel。尝试输入以下表达式,并观察错误:
:tags: ["raises-exception"]
inc 3
File "[7]", line 1, characters 0-3:
1 | inc 3
^^^
Error: Unbound value inc
Hint: Did you mean incr?
发生错误是因为 toplevel 还不知道关于名为 inc
的函数的任何信息。现在向 toplevel 发出以下指令(译者注:需要在 mycode.ml
文件所在目录打开命令行并启动 utop):
# #use "mycode.ml";;
请注意,上面的第一个 #
字符表示 toplevel 提示。第二个 #
字符是用来告诉 toplevel 你正在发出指令的。如果没有这个字符,toplevel 会认为你在尝试应用一个名为use的函数。
现在再试一次:
inc 3
- : int = 4
toplevel 工作流
当 toplevel 使用存储在文件中的代码时,最佳工作流程是:
-
编辑文件中的代码。
-
使用
#use
将代码加载到顶层。 -
交互式地测试代码。
-
退出 toplevel。**警告:**不要跳过这一步。
假设你想修复代码中的一个错误。我们希望不退出 toplevel,直接编辑文件,然后在同一个 toplevel 会话中重新使用
#use
指令加载。抵制这种诱惑。在同一个会话中,从先前的#use
指令加载的“修改过的代码”可能会导致令人惊讶的事情发生 —— 无论如何,当你第一次学习这门语言时,这是令人惊讶的。因此,在重用文件之前,请务必退出 toplevel。
注:本书是康奈尔大学 CS 3110 数据结构和函数式编程的教材。原书为英文版,在学习的过程中,根据自己的理解,翻译了一些,做一个记录,版权归原作者所有,如有侵权,请联系我删除。