Search code examples
haskellghci

Can't do partial function definitions in GHCi


For some reason partial function definitions in GHCi always lead to some issues so partially defined functions never work for me.

For example, if I do this in the source file:

factorial 1 = 1
factorial n = n * factorial(n - 1)

all works correctly. If I do the same in GHCi, I get a StackOverflow exception:

ghci> fact 1 = 1
ghci> fact n = n * fact(n - 1)
ghci> fact 5
*** Exception: stack overflow

Or if I define recursive list length function, all works fine if defined in the source file, but if I do it in GHCI, I get exception about a non-exhaustive pattern:

ghci> my_length [] = 0
ghci> my_length (x:xs) = 1 + my_length xs
ghci> my_length [1, 2, 3]
*** Exception: <interactive>:3:1-35: Non-exhaustive patterns in function my_length

Is there some trick to partial definitions in GHCi? The tutorial I'm following seems to think it should be possible.


Solution

  • tl;dr: don't wirte multi-line definitions in GHCi

    When you write this

    ghci> fact 1 = 1
    ghci> fact n = n * fact(n - 1)
    ghci> fact 5
    

    the interpreter actually sees it as something like

    ghci> let fact 1 = 1
    ghci> let fact' n = n * fact' (n - 1)
    ghci> fact' 5
    

    (In old versions of GHCi you actually had to write out let, which was relaxed at some point - a mistake in my opinion.)

    The reason the disambiguation foo vs foo' is done is a) that it allows you to "change definitions" while trying out stuff. Say you previously defined foo x = x+1, but further experiments show you actually want foo x = x+2. Well, you can just write that and the old definition will get shadowed by it. ...

    ⎡ Side note: I tend to say this isn't really working great. The problem is that other definitions you've previously made based on the old foo will still refer to that old one:

    ghci> let foo x = x+1
    ghci> let bar y = foo y - 1
    ghci> bar 19
    19
    ghci> let foo x = x+2
    ghci> bar 19
    19
    ghci> let bar y = foo y - 1
    ghci> bar 19
    20
    

    ...b) that the interpreter doesn't need to re-open definitions that could already have been complete. See, Haskell doesn't require that a definition is actually exhaustive; the line

    ghci> let fact 1 = 1
    

    is in principle a valid definition (albeit useless). It is good practice to make all definitions exhaustive and theoretically GHCi could simply wait until it is complete, but it doesn't do that.

    What it does offer instead is the multiline mode, enabled thus:

    ghci> :set +m
    ghci> let fact 1 = 1
    ghci|     fact n = n * fact (n-1)
    ghci| 
    ghci> fact 4
    24
    

    It also has a special :{ :} syntax, but I would advise against using that.

    Even multiline mode isn't working great, in particular it makes it awkward to go back with up-arrow and change a definition: you'll actually need to go through all the lines and enter them again in the right order.

    Upshot: it's best to avoid making any big definitions in GHCi. Much better to put them in a .hs source file and load that from GHCi.