Home > Enterprise >  Why are even lazy folds so eager?
Why are even lazy folds so eager?

Time:02-14

This code:

import Data.Foldable
import Debug.Trace

factors :: [Bool]
factors = [True, True, False, True, True, True, True, True]

andy :: Bool -> Bool -> Bool
andy False _ = False
andy True False = False
andy True True = True

tandy :: Bool -> Bool -> Bool 
tandy a b = let c = a `andy` b in trace (show a    " & "    show b    " = "    show c) c

wonder :: Bool
wonder = foldl tandy True factors

says this when I evaluate wonder:

True & True = True  
True & True = True  
True & False = False
False & True = False
False & True = False
False & True = False
False & True = False
False & True = False

but I would have preferred it to have stopped sooner. I've tried this with everything imaginable in place of the foldl and with && in place of andy but it never seems to get the hint.

It seems to me that the line andy False _ = False doesn't invite the compiler to evaluate the second parameter. I haven't forced any strictness. What's going on? Even C can do better.

CodePudding user response:

tandy is strict in both parameters, even though andy is not. This is because of the tracing. You ask it to show both inputs in the call to trace, so it has to evaluate both arguments.

Consider `tandy2':

tandy2 :: Bool -> Bool -> Bool
tandy2 False _ = trace ("False & anything = False") False
tandy2 True b = trace ("True & "    show b    " = False") b

Instead of blindly evaluating both arguments in its tracing, it is careful to only evaluate the same arguments it would otherwise. This makes it actually reflect the same strictness properties as andy has.

Try using tandy2 with foldr and you'll see it stops evaluating as soon as it hits a False.

$ ghci
GHCi, version 9.2.1: https://www.haskell.org/ghc/  :? for help
ghci> import Debug.Trace
ghci> let tandy2 False _ = trace ("False & anything = False") False ; tandy2 True b = trace ("True & "    show b    " = False") b
ghci> foldr tandy2 True []
True
ghci> foldr tandy2 True [True]
True & True = False
True
ghci> foldr tandy2 True [True, False]
False & anything = False
True & False = False
False
ghci> foldr tandy2 True [True, False, True]
False & anything = False
True & False = False
False

There you go. It never evaluates the False branch more than once.

  • Related