编译与解释:让或不让
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 中)只期望出现提示
- commands(输入
:h
列出可用的命令)
- declarations(诸如
data
、type
、newtype
、class
、instance
、deriving
和foreign
但 不是 常规定义)
- imports
- expressions(类似于
1+1
或 let x = 3 in x*x
)
- I/O Actions /
do
statments(类似 print "hi"
或 x <- getLine
或 let doubleMe x = x + x
)
如果这让您感到惊讶,请记住 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 本身交互。
为什么 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 中)只期望出现提示
- commands(输入
:h
列出可用的命令) - declarations(诸如
data
、type
、newtype
、class
、instance
、deriving
和foreign
但 不是 常规定义) - imports
- expressions(类似于
1+1
或let x = 3 in x*x
) - I/O Actions /
do
statments(类似print "hi"
或x <- getLine
或let doubleMe x = x + x
)
如果这让您感到惊讶,请记住 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 本身交互。