1
votes

Why does the Haskell interpreter (GHCI 7.10.3) need function definitions to be in a let expression, but the Haskell compiler (GHC 7.10.3) throws a parser error if a function definition is within a let expression?

I'm working through "Learn You a Haskell for Great Good!" Baby's first function is doubleMe: doubleMe x = x + x

Why does the interpreter accept this definition if it is within a let expression and otherwise throw a parse error on input '='? Meanwhile, if I'm compiling the same function from a file, why does GHC throw a parse error if the function definition is within a let expression and compiles the definition if it is not within a let expression? Coming from a Lisp background, I'm surprised that interactive Haskell and file loading and compilation Haskell treats these definitions differently.

3
It's a convention. If GHCi worked exactly as writing in a .hs file, writing 1+1 would be an error, as well as print (2,3). Instead, GCHi chose to use a little magic so to accept both these expressions and let definitions. About why x=1 without let is rejected -- I don't think there's a clear answer to that except "it would require more magic".chi
Right. FWIW, IHaskell allows mixing both styles.leftaroundabout
The newest version of GHCi (8.0.1) accepts doubleMe x = x + x. Enough people like you complained that they added a special case for this. :)Alec
This is a fundamental difference I now appreciate between Haskell and Lisp: interactive access to the compiler via a command prompt vs interactive access to the language itself via a REPL, respectively.fpt

3 Answers

4
votes

The reasoning behind this is that GHCi (in 7.10.3) expects at the prompt only

  • commands (type in :h to list the commands available)
  • declarations (things like data, type, newtype, class, instance, deriving, and foreign but not a regular definition)
  • imports
  • expressions (things like 1+1 or let x = 3 in x*x)
  • I/O Actions / do statments (things like print "hi" or x <- getLine OR let doubleMe x = x + x)

If this seems surprising to you, remember that the evaluation of Lisp and Haskell is very different - Lisp just gets interpretted, while Haskell is being compiled.

As you can tell, top-level definitions are not part of this list. Thankfully this got fixed in GHCi 8.0.1, which now supports raw top-level function declarations. The following works (in 8.0.1):

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

The GHCi interpreter command line treats its input as if it were in a do clause. So you can type this:

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

Apart from the :module directive this is exactly how it would be in a do clause.

Likewise you can write

let f x = 2 * x

because that is how it would be in a do clause.

0
votes

Modern Lisp implementations compile to native code, often by default even when code is entered at the prompt. Lisp's prompt isn't just a place to enter commands, it's a place to interact with the language because the entire language is made available by the Read-Evaluate-Print Loop. This means that Lisp reads the text into symbolic expressions, which it then evaluates, printing any print output and any returned values. For example,

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

Compiled-Function-P Clozure Common Lisp

With Lisp, code you can enter into the Lisp image by compiling and loading a file you can also enter into the Lisp image by typing it out at the REPL. So it turns out I was surprised because I was expecting the GHCi prompt to be a REPL, but as @Alec describes it's not because it doesn't read text into Haskell expressions that it would then evaluate, as Lisp does. As @dfeuer says, the issue isn't about compilation versus interpretation. The issue is that GHCi's prompt offers limited interaction with a Haskell compiler, rather than interaction with Haskell itself as Lisp's REPL does.