views:

204

answers:

3

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?

A: 

I missed one point of laziness, which renders the answer below false.


Because sum calculates the total sum of all elements in the sequence. Which is, in your case, endless.

You probably want

map ((curry ex) 1) [1..]

That is

map -- map each item x to y
    (
        (
            curry ex -- curry ex, to transform (x, y) -> z into x -> y -> z
        )
        1 -- then invoke it with 1, which results in y -> z, x being 1
    )
    [1..] -- the infinite sequence to be mapped.
Dykam
Hm, I don't think that's it. See my update above.
perimosocordiae
Damned updates. Yeah, quite obvious now what the issue is.
Dykam
-1: It would only evaluate the sum if it was actually needed, and in this case it wasn't.
Zifre
+13  A: 

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 }
camccann
Aha, that's really inconvenient. Good to know, though, thanks!
perimosocordiae
@perimosocordiae: As a fairly heavy user of GHCi, I've generally taken up the habit of writing most or all of my definitions in an external file, loading the file with GHCi, and using the REPL mostly for evaluating simple expressions.
camccann
+1  A: 
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.

sepp2k