Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this Haskell statement not evaluate lazily?

I have the following function defined:

ex 1 x = 1
ex 0 x = 0
ex b x = b ** x

Then, when I execute the following:

1 `ex` (sum [1..])

it tries to calculate the sum of the infinite sequence, instead of being lazy and returning 1. Why?


EDIT: Upon further investigation, I found that laziness happens if I define the ex function in a file, but not if I define it in GHCI:

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x
Prelude> ex 1 (sum [1..])
<interactive>: out of memory (requested 1048576 bytes)

If I pull the ex definition into a file (in this case, test.hs):

$ ghci
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help
Loading package base ... linking ... done.
Prelude> :load test.hs 
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> ex 1 (sum [1..])
1.0

The new question, then, is why?

like image 949
perimosocordiae Avatar asked Jul 08 '10 17:07

perimosocordiae


2 Answers

In GHCi, each let statement introduces a new definition of ex, instead of multiple pattern cases as you expect. So it hangs because, when you enter ex 1 (sum [1..]) afterwards, only the final ex b x = b ** x version exists.

If you want to define a function with multiple pattern cases in GHCi, you'll need to put it in a single let statement, like this:

let ex 1 x = 1; ex 0 x = 0; ex b x = b ** x

The same applies for anything else that would normally be written across multiple lines, such as do notation. For instance, a function like this:

f x = do
    y <- get
    put (x + y)
    return y

Would have to be written like this in GHCi:

let f x = do { y <- get; put (x + y); return y }
like image 95
C. A. McCann Avatar answered Nov 05 '22 13:11

C. A. McCann


Prelude> let ex 1 x = 1
Prelude> let ex b x = b ** x

You're not defining a function with two cases here. You define a function with one case, and then you define it again overriding the previous definition.

To define one function with two patterns use let ex 1 x = 1; ex b x = b ** x, i.e. separate the cases with a semicolon.

like image 22
sepp2k Avatar answered Nov 05 '22 11:11

sepp2k