5

There are two strict versions of zipWith function:

1) Really strict, elements of lists l1 and l2 get evaluated so their thunks do not eat all stack space (Don Stewart code)

zipWith' f l1 l2 = [ f e1 e2 | (e1, e2) <- zipWith k l1 l2 ]
            where
                k x y = x `seq` y `seq` (x,y)

2) Not really strict, attempt to force evaluation by other way.

zipWith'' f l1 l2 = [ f e1 e2 | (e1, e2) <- zip (map (\x -> x `seq` x) l1) (map (\x -> x `seq` x) l2) ]

The question is: why the equivalent code from the 2nd example using map does not make the function also strict?

hammar
  • 138,522
  • 17
  • 304
  • 385
David Unric
  • 7,421
  • 1
  • 37
  • 65
  • 1
    The `zipWith'` function will force any element thunks in l1 and l2 as it processes the elements, but the `f e1 e2` thunk is not forced. – augustss Jun 28 '11 at 11:18

2 Answers2

15

It's a common mistake to use

x `seq` x

Which is exactly equivalent to

x

An excellent explanation is available in Neil Mitchell's post on Bad Strictness.

jaspervdj
  • 893
  • 6
  • 7
  • Interesting but it does not correspond why the following versions **are not equivalent**: zipWith''' f l1 l2 = [ f e1 e2 | (e1, e2) <- zip (sLst l1) (sLst l2) ] where sLst [] = [] sLst (x:xs) = x `seq` x : sLst xs and zipWith''' f l1 l2 = [ f e1 e2 | (e1, e2) <- zip (sLst l1) (sLst l2) ] where sLst [] = [] sLst (x:xs) = x : sLst xs – David Unric Jun 28 '11 at 10:16
  • Recursive construction of list with *x `seq` x : sLst xs* makes it strict whereas "equivalent" *x : sLst xs* does not ... – David Unric Jun 28 '11 at 10:19
  • Those are not equivalent. The first version you wrote is `seq x (x : sLst xs)`. Mind the associativity. – jaspervdj Jun 28 '11 at 10:37
  • Forget it, my mistake due to functions precedence. The former expression without parens did meant x `seq` (x : sLst xs). – David Unric Jun 28 '11 at 10:40
3

Instead of tautological map, one can use this function to force the list:

evl []     = []
evl (x:xs) = x `seq` (x:evl xs)
-- Cannot figure out how to do this with fold.

Then the strict zipWith is

zipWith''' f xs ys = zipWith f (evl xs) (evl ys)
n. m. could be an AI
  • 112,515
  • 14
  • 128
  • 243