Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell lazy evaluation

If I call the following Haskell code

find_first_occurrence :: (Eq a) => a -> [a] -> Int
find_first_occurrence elem list = (snd . head) [x | x <- zip list [0..], fst x == elem]

with the arguments

'X' "abcdXkjdkljklfjdlfksjdljjjjjjjjjjjjjjjjjjjjjjjjjjjjjjj"

how much of the zipped list [('a',0), ('b',1), ] is going to be built?

UPDATE:

I tried to run

find_first_occurrence 10 [1..]

and returns 9 almost instantly, so I guess it does use lazy evaluation at least for simple cases? The answer is also computed "instantly" when I run

let f n = 100 - n
find_first_occurrence 10 (map f [1..])
like image 352
quant_dev Avatar asked Dec 05 '22 17:12

quant_dev


2 Answers

Short answer: it will be built only up to the element you're searching for. This means that only in the worst case you'll need to build the whole list, that is when no element satisfies the conditions.

Long answer: let me explain why with a pair of examples:

ghci> head [a | (a,b) <- zip [1..] [1..], a > 10]
11

In this case, zip should produce an infinite list, however the laziness enables Haskell to build it only up to (11,11): as you can see, the execution does not diverge but actually gives us the correct answer.

Now, let me consider another issue:

ghci> find_first_occurrence 1 [0, 0, 1 `div` 0, 1]
*** Exception: divide by zero
ghci> find_first_occurrence 1 [0, 1, 1 `div` 0, 0]
1
it :: Int
(0.02 secs, 1577136 bytes)

Since the whole zipped list is not built, haskell obviously will not even evaluate each expression occurring in the list, so when the element is before div 1 0, the function is correctly evaluated without raising exceptions: the division by zero did not occur.

like image 192
Riccardo T. Avatar answered Jan 06 '23 11:01

Riccardo T.


All of it.

Since StackOverflow won't let me post such a short answer: you can't get away with doing less work than looking through the whole list if the thing you're looking for isn't there.

Edit: The question now asks something much more interesting. The short answer is that we will build the list:

('a',0):('b',1):('c',2):('d',3):('X',4):<thunk>

(Actually, this answer is just the slightest bit subtle. Your type signature uses the monomorphic return type Int, which is strict in basically all operations, so all the numbers in the tuples above will be fully evaluated. There are certainly implementations of Num for which you would get something with more thunks, though.)

like image 36
Daniel Wagner Avatar answered Jan 06 '23 12:01

Daniel Wagner