
On Sun, 27 Jun 2010, Bertram Felgenhauer wrote:
If the compiler had produced
Main.lvl3 = case Main.ds of wild_Xw { (prefix_aCf, suffix_aCh) -> suffix_aCh }
Main.lvl4 = Main.go1 Main.lvl3
instead, then there would not be a leak. This whole record selector thunk business is very fragile.
Bertram, thank you a lot for the detailed analysis! It seems that I have stepped into a nasty implementation detail of GHC.
The good news is that the problem is completely unrelated to unsafePerformIO (the presence of unsafePerformIO makes optimisations more difficult, but any pure function of sufficient complexity would have the same effect).
There's a simple fix for the problem, too: Change
let (prefix, suffix) = makeTwoLists 'a'
to let !(prefix, suffix) = makeTwoLists 'a'
in which case the compiler produces code similar to the non-leaky case for all alternatives.
Actually this fix works for my example program and another one that is based on chunky StorableVector. But when I switch off profiling the StorableVector example runs into a space leak, again, while the one with plain lists that I have sent here still works. I'll investigate into this, but it seems indeed very fragile and I wonder whether there is a more reliable way. Maybe I can combine splitAtLazy and (++) to a function like splitAtAndAppend :: [x] -> ([a] -> [b]) -> ([a] -> [b]) -> [a] -> [b] but I'm afraid I will need pairs temporarily and then I run into the same problems.