2016-10-17 182 views
1

为什么Haskell解释器(GHCI 7.10.3)需要函数定义在let表达式中,但Haskell编译器(GHC 7.10.3)会抛出解析器错误函数定义在let表达式内?编译vs解释:允许还是不允许

我正在通过“Learn You a Haskell for Great Good!”婴儿的第一个功能是doubleMe: doubleMe x = x + x

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

+2

这是一个惯例。如果GHCi的工作与.hs文件完全相同,那么写'1 + 1'将是一个错误,以及'print(2,3)'。相反,GCHi选择使用一点魔法来接受这些表达式和'let'定义。关于为什么'x = 1'没有让我被拒绝 - 我不认为有一个明确的答案,除了“它需要更多的魔法”。 – chi

+0

对。 FWIW,[IHaskell](https://github.com/gibiansky/IHaskell)允许混合两种风格。 – leftaroundabout

+7

最新版本的GHCi(8.0.1)接受'doubleMe x = x + x'。像你这样的人抱怨他们为此增加了一个特例。 :) – Alec

回答

0

现代Lisp实现编译为本地代码,即使在提示符处输入代码时,通常也会默认编译为本机代码。 Lisp的提示不仅仅是输入命令的地方,它是一个与语言交互的地方,因为整个语言都可以通过Read-Evaluate-Print Loop获得。这意味着Lisp将文本读入符号表达式,然后评估它,打印任何打印输出和任何返回的值。例如,

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

Compiled-Function-P Clozure Common Lisp

和Lisp,代码可以通过编译,并通过在REPL键入它加载一个文件,你也可以进入Lisp的形象进入Lisp的图像。所以事实证明我很惊讶,因为我期待GHCi提示是一个REPL,但正如@Alec所描述的那样,它并不是因为它没有将文本读入Haskell表达式中,而是像Lisp那样对它进行评估。正如@dfeuer所说,问题不在于编译与解释。问题在于,GHCi的提示提供了与Haskell编译器的有限交互,而不是像Lisp的REPL那样与Haskell本身交互。

4

这背后的原因是,GHCI(在7.10.3)期望在提示仅

  • commands(式中:h列出可用的命令)
  • declarations(之类的东西datatypenewtypeclassinstancederivingforeign常规定义)
  • imports
  • expressions(之类的东西1+1let x = 3 in x*x
  • I/O Actions/do statments(之类的东西print "hi"x <- getLineORlet doubleMe x = x + x

如果这似乎令人惊讶的你,记住,Lisp和哈斯克尔的评价很不同 - Lisp只是被解释,而Haskell正在被编译。

正如您所看到的,顶级定义不是此列表的一部分。谢天谢地,这已经在GHCi 8.0.1中得到了修复,它现在支持原始的顶层函数声明。以下工作(8.0.1):

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

这与编译和解释没有任何关系。我相信很多Lisp实际上都是编译的,尽管我认为Scheme可能是唯一真正为良好编译设计的Lisp。 – dfeuer

+1

我怀疑在ghci的早期版本中没有包含这个功能的真正动机是例如令人惊讶的行为。 '在ghci中,maybeDoubleMe Nothing = Nothing \ nmaybeDoubleMe(Just x)= Just(x + x)'与在文件中相比。 –

3

的GHCI解释器命令行对待其输入,就好像它是在一个do子句。除了:module指令

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

这是它会究竟是如何成为一个do条款:所以,你可以键入此。

同样,你可以写

let f x = 2 * x 

,因为这是它怎么会在do条款。