
On Mon, Jun 19, 2006 at 05:50:13PM +0100, Duncan Coutts wrote:
On Mon, 2006-06-19 at 17:03 +0100, Jon Fairbairn wrote:
il [] = error "foo" il [x] = ([], x) il (x:xs) = cof x (il xs) where cof x ~(a,b) = (x:a, b) -- !
From a quick test, it looks like none of our suggested solutions actually work in constant space.
main = interact $ \s -> case il s of (xs, x) -> let l = length xs in l `seq` show (l,x)
I was hoping to have enlightenment served to me, but since nobody has
explained this, I took a crack at it. I still can't explain it, but I
got some data that maybe somebody else will understand. My code:
initlast :: [a] -> ([a], a)
initlast [x] = ([], x)
initlast (x:xs) = let (init, last) = initlast xs
in (x:init, {-# SCC "last" #-} last)
lenshow n (_:xs) last = let n1 = n+1 in n1 `seq` lenshow n1 xs last
lenshow n [] last = show (n,last)
main = interact $ \s -> case initlast s of
(xs, x) -> lenshow 0 xs x
lenshow is just "show (length xs, x)", written out so I can tweak it
later. This exhibits the runaway space usage with a large input that
Duncan described. If you throw away "last" in lenshow and just "show
n", it runs in constant space.
It seems that the reference to "last" that I annotated as a cost center
is holding a chain of trivial thunks--trivial because "last" is just
being copied from the result of the recursive call to initlast. I
thought maybe I could get rid of them by returning an unboxed tuple from
initlast, but this turned out to make no difference.
Profiling gave a couple hints. Retainer set profiling (-hr) showed the
retainer set holding all the memory was
{