Search code examples
haskelllazy-evaluationstrictness

How to set strictness in list comprehension?


I'm bit stuck how to rewrite following strict-evaluated list comprehension to use seq instead of bang pattern:

zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]

Any idea ?

I've tried

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

but this unfortunately does not force an evaluation to WHNF.


Solution

  • You can mechanically translate bang patterns into calls to seq following the GHC manual:

    This:

    zipWith' f l1 l2 = [ f e1 e2 | (!e1, !e2) <- zip l1 l2 ]
    

    Becomes Too lazy:

    zipWith' f l1 l2 =
        [ f e1 e2
        | e <- zip l1 l2
        , let t = case e of (x,y) -> x `seq` y `seq` (x,y)
        , let e1 = fst t
        , let e2 = snd t
        ]
    

    Which is more concisely written as (too lazy):

    zipWith' f l1 l2 =
        [ f e1 e2
        | e <- zip l1 l2
        , let (e1,e2) = case e of (x,y) -> x `seq` y `seq` (x,y)
        ]
    

    Though I'd write it as (wrong, too lazy)

    zipWith' f l1 l2 = zipWith (\x y -> uncurry f (k x y)) l1 l2
        where
            k x y = x `seq` y `seq` (x, y)
    

    You could also move the strictness hint to a data structure:

    data P = P !Integer !Integer
    
    zipWith' f l1 l2 = [ f e1 e2 | P e1 e2 <- zipWith P l1 l2 ]
    

    Or as:

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