how to solve “stack space overflow” in haskell
-
18-09-2019 - |
Question
Running the following program will print "space overflow: current size 8388608 bytes." I have read this and this, but still don't know how to resolve my problem. I am using foldr, shouldn't it be guaranteed to be "tail recursive"?
I feel great about Haskell so far until I know I should prevent "space overflow" when using the powerful recursion. :)
module Main where
import Data.List
value a b =
let l = length $ takeWhile (isPrime) $ map (\n->n^2 + a * n + b) [0..]
in (l, a ,b)
euler27 = let tuple_list = [value a b | a <-[-999..999] , b <- [-999..999]]
in foldr (\(n,a,b) (max,v) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list
main = print euler27
EDIT: remove the definiton of isPrime
for simplicity
Solution
As pierr answered, you should use foldl'
. For more details:
foldl'
calculates its "left-side" before giving it to your fold step.foldr
gives your fold step a "thunk" for the right-side value. This "thunk" will be calculated when needed.
Let's make a sum with foldr
and see how it evaluates:
foldr (+) 0 [1..3]
1 + foldr (+) 0 [2..3]
1 + 2 + foldr (+) 0 [3]
1 + 2 + 3 + foldl (+) 0 [] -- this is a big thunk..
1 + 2 + 3 + 0
1 + 2 + 3
1 + 5
6
And with foldl'
: (tag omitted in code because SO doesn't display it nicely)
foldl (+) 0 [1..3]
-- seq is a "strictness hint".
-- here it means that x is calculated before the foldl
x `seq` foldl (+) x [2..3] where x = 0+1
foldl (+) 1 [2..3]
x `seq` foldl (+) x [3] where x = 1+2
foldl (+) 3 [3]
x `seq` foldl (+) x [] where x = 3+3
foldl (+) 6 []
6
In good uses for foldr
, which don't leak. the "step" must either:
- Return a result that doesn't depend on the "right-side", ignoring it or containing it in a lazy structure
- Return the right-side as is
Examples of good foldr
usage:
-- in map, the step returns the structure head
-- without evaluating the "right-side"
map f = foldr ((:) . f) []
filter f =
foldr step []
where
step x rest
| f x = x : rest -- returns structure head
| otherwise = rest -- returns right-side as is
any f =
foldr step False
where
-- can use "step x rest = f x || rest". it is the same.
-- version below used for verbosity
step x rest
| f x = True -- ignore right-side
| otherwise = rest -- returns right-side as is
OTHER TIPS
replace
foldr (\(n,a,b) (max,v) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list
with
foldl' (\(max ,v) (n,a,b) -> if n > max then (n , a * b) else (max ,v) ) (0,0) tuple_list
solved this problem, should that suggest we should always prefer to use foldl' instead of other variants(foldl,foldr)?