On Fri, 26 Jun 1998, S. Alexander Jacobson wrote:

 | > myFoldl f s [] = s
 | > myFoldl f s (x:xs) = f s (foldl f x xs)
 | > total = 3 + (myFoldl (+) 3 [4..100000])
 | > total = 3 + 4 + (myFoldl (+) 4 [5..100000])

Just to avoid confusion: the function called myFoldl above is called
foldr in the Haskell prelude
  http://haskell.org/onlinereport/standard-prelude.html#$vfoldr

With foldr, if the supplied operator needs to evaluate its second
argument to do any real work (as in the case for (+)) a _very_ big
expression will be built before any real evaluation starts.
On the other hand, if the operator does work already when provided with
one argument, the caller can benefit from this result immediately.
Bad:  foldr (+)                   0  [1..100000]
Good: foldr (\x xs -> (x+1) : xs) [] [1..100000]

The real Haskell prelude foldl:
   foldl f z []     =  z
   foldl f z (x:xs) =  foldl f (f z x) xs
uses its second arg. as an accumulating parameter, so that even if f
really wants its second parameter to evaluate, that argument is present
immediately (the expression (f z x) on the RHS above).
The problem here is that when the standard lazy evaluation pull unfolds
the application of foldl f z it will only form the application (f z x)
without actually performing it. Thus we get just as big an
unevaluated expression here as in the foldr case. In some sense foldl
is worse than foldr, as no partial results can be obtained even if the
operator is productive with just one argument present. Only the "Bad" case
above applies - not the "Good". (The trick of supplying an operator that
is explicitly strict does not help either.)

Solution (as already suggested in a previous mail):
In hugs the function foldl'
  foldl'           :: Eval a => (a -> b -> a) -> a -> [b] -> a
  foldl' f a []     = a
  foldl' f a (x:xs) = strict (foldl' f) (f a x) xs
is a variant of foldl that really evaluates the application (f a x)
immediately. With this definition the summation above works fine:
Good: foldl' (+) 0 [1..100000]

Note:
  In favor of foldl: A strictness analyser phase might detect the problem
    and (effectively) transform to foldl'. 
[Implementors: does any implementation do this?]

In practice (see below) only foldl' is reasonable in this specific case.

Patrik Jansson

Quick experiment:
  I tried the three variants with the four widely available Haskell 
    implementations:

{-
foldl'           :: Eval a => (a -> b -> a) -> a -> [b] -> a
foldl' f a []     = a
foldl' f a (x:xs) = strict (foldl' f) (f a x) xs
main = print (foldl' (+) 0 [1..100000])

hugs: 7 s
hbc: 0.5 s
ghc: 0.5 s
nhc: 16 s
-}

{-
main = print (foldl (+) 0 [1..100000])

ghc: needs +RTS -K5M  (5Mb stack space)
hbc: needs -A300K  (300Kb pointer stack)
hugs: Segmentation fault
nhc: needs +RTS -H1500K (1.5M heap)
-}

{-
main = print (foldr (+) 0 [1..100000])

ghc: needs +RTS -K5M  (5M stack space)
hbc: needs -A300K  (300Kb pointer stack)
hugs: Control stack overflow
nhc: needs +RTS -H1500K (1.5M heap)
-}









Reply via email to