2016-10-17 74 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條款。