1

From my understanding, as Haskell uses lazy evaluation, which allows operations on for example infinite lists to be evaluated in a finite amount of time.

As a test, I defined the following function

X Boolean
Y Int

f(X,Y) = (Y == 3) OR X

Hence, fold left applied to the infinite list of integers [1..] with False as initial value and the function defined above, should return True, because when it will reach n=3 evaluating f(n==3,False) will return True, and hence this True will propagate through the function.

I implemented this function in Haskell code

myfunc :: Bool -> Int -> Bool
myfunc True _ = True
myfunc _ n
  | (n == 3)  = True
  | otherwise = False

and tried it out in the cli

foldl myfunc False [1..]

The command becomes unresponsive, suggesting it is doing an infinite computation. Why is Haskell not benefiting from the lazy evaluation here?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
jdfauw
  • 647
  • 3
  • 11
  • 22
  • 3
    `foldl` is indeed not lazy, since it folds `f (f (f z x1) x2) x3 ...`, hence it needs to reach the end of the list. That is why `foldr` is often more advisable in terms of laziness. – Willem Van Onsem Jan 13 '20 at 14:57
  • This looks however like a simple `any`: your last function is equivalent to `any (3 ==) [1..3]`. – Willem Van Onsem Jan 13 '20 at 14:59
  • 1
    I suspect part of the misunderstanding here is thinking that the l and r ind foldl and foldr mean "start at the left of the list and process towards the right" or "start at the right of the list and process towards the left". But that's not so. Instead, it stands for "associate the combining operation to the left" or "associate the combining operation to the right". In practice this frequently means foldl starts evaluating at the right end of the list and foldr starts at the left end! – Daniel Wagner Jan 13 '20 at 21:39

1 Answers1

5

Because foldl always must traverse its argument list in whole before starting reducing it into the final result; and foldl' traverses the argument list in whole while reducing it into the final result:

foldl f z [x1, x2, ..., xn]  ==           foldl f (f z x1) [x2, ..., xn]

foldl' f z [x1, x2, ..., xn]  ==  z `seq` foldl' f (f z x1) [x2, ..., xn]

-- thus,

foldl f z1 [x1, x2, ..., xn]  ==  f (...(f (f z1 x1) x2)...) xn

foldl' f z1 [x1, x2, ..., xn]  ==  z1 `seq` (let z2 = f z1 x1  in
                                    z2 `seq` (let z3 = f z2 x2  in
                                     z3 `seq` (... `seq` (f zn xn)...))) 

You though wanted to be able to stop the traversal, which is done via right fold with a non-strict reducing function:

present y xs  =  foldr myfunc False xs 
  where
  myfunc x r  =  y == x || r 

Because myfunc is non-strict in its second argument, foldr myfunc explores the list xs in the left-to-right order,

foldr myfunc False (x:xs)  =  myfunc x (foldr myfunc False xs)
                           =  y == x || foldr myfunc False xs
Will Ness
  • 70,110
  • 9
  • 98
  • 181