编译与解释:让或不让

Compiled vs Interpreted: To Let or Not To Let

为什么 Haskell 解释器 (GHCI 7.10.3) 需要函数定义在 let 表达式中,但 Haskell 编译器 (GHC 7.10.3) 抛出解析器错误,如果函数定义在 let 表达式中?

我正在完成“Learn You a Haskell for Great Good!” Baby的第一个函数是doubleMe: 双我 x = x + x

为什么解释器在 let 表达式中接受此定义,否则会在输入“=”时抛出解析错误?同时,如果我从文件编译相同的函数,为什么 GHC 在函数定义在 let 表达式内时抛出解析错误,而在不在 let 表达式内时编译定义?来自 Lisp 背景,我很惊讶交互式 Haskell 和文件加载和编译 Haskell 以不同的方式对待这些定义。

这背后的原因是 GHCi(在 7.10.3 中)只期望出现提示

如果这让您感到惊讶,请记住 Lisp 和 Haskell 的计算非常不同 - Lisp 只是被解释,而 Haskell 正在被编译。

如您所知,顶级定义不在此列表中。值得庆幸的是,这在 GHCi 8.0.1 中得到了修复,它现在支持原始的顶级函数声明。以下作品(在 8.0.1 中):

ghci> doubleMe x = x + x
ghci> doubleMe 1
2

GHCi 解释器命令行将其输入视为在 do 子句中。所以你可以输入:

:module + System.Random
v <- getStdRandom $ randomR (1,10)

除了 :module 指令之外,这正是 do 子句中的情况。

同样你可以这样写

let f x = 2 * x

因为在 do 子句中就是这样。

现代 Lisp 实现编译为本机代码,通常默认情况下即使在提示符下输入代码也是如此。 Lisp 的提示不仅仅是输入命令的地方,它还是与语言交互的地方,因为整个语言都可以通过读取-评估-打印循环使用。这意味着 Lisp 将文本读入符号表达式,然后对其求值,打印任何打印输出和任何返回值。例如,

? (defun a-fun () nil)
A-FUN
? (compiled-function-p #'a-fun)
T

Compiled-Function-P Clozure Common Lisp

使用 Lisp,您可以通过编译和加载文件将代码输入到 Lisp 图像中,您也可以通过在 REPL 中输入代码将其输入到 Lisp 图像中。所以事实证明我很惊讶,因为我期待 GHCi 提示是一个 REPL,但正如 @Alec describes it's not because it doesn't read text into Haskell expressions that it would then evaluate, as Lisp does. As @dfeuer 所说,问题不在于编译与解释。问题是 GHCi 的提示提供与 Haskell 编译器的有限交互,而不是像 Lisp 的 REPL 那样与 Haskell 本身交互。