1

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.

duplode
  • 33,731
  • 7
  • 79
  • 150
fpt
  • 358
  • 3
  • 11
  • 2
    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 Oct 17 '16 at 15:46
  • Right. FWIW, [IHaskell](https://github.com/gibiansky/IHaskell) allows mixing both styles. – leftaroundabout Oct 17 '16 at 15:57
  • 7
    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 Oct 17 '16 at 16:05
  • 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 Oct 17 '16 at 19:24

3 Answers3

4

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
Alec
  • 31,829
  • 7
  • 67
  • 114
  • 5
    This doesn't have anything to do with compilation and interpretation. Lots of Lisp actually is compiled, I believe, although I think Scheme may be the only Lisp that's really designed for good compilation. – dfeuer Oct 17 '16 at 17:49
  • 1
    I suspect the real motivation for not including this functionality in early versions of ghci is the surprising behavior of e.g. `maybeDoubleMe Nothing = Nothing\nmaybeDoubleMe (Just x) = Just (x+x)` in ghci compared to in a file. – Daniel Wagner Oct 17 '16 at 22:53
3

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.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
0

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.

Community
  • 1
  • 1
fpt
  • 358
  • 3
  • 11