开发者

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?


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 }


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.


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.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜