Trash collecting list while performing an action on it

I want to write a conjugate gradient solver in Haskell and want to use lazy lists to decouple the stop rule and output information from iterations. My code looks something like this:

data CGState = CGState { cgx :: Image , cgp :: Image , cgr :: Image , cgr2 :: Double } cg :: Operator -> Image -> [CGState] cg = [...] runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image runCG stoprule op rhs = do let steps = takeWhile (not . stoprule) $ cg op rhs fmap last $ forM (zip [(1::Int)..] steps) $ \(n, cgs) -> do putStrLn $ show n ++ " " ++ show (sqrt $ cgr2 cgs) return $ cgx cgs 

The idea is to iterate over the list, output some information, but save only the last iteration. However, running this code does not seem to garbage collect the previous iterations. I assume this is due to IO: if I rewrote code like

 runCG :: (CGState -> Bool) -> Operator -> Image -> IO Image runCG stoprule op rhs = do let steps = takeWhile (not . stoprule) $ cg op rhs return $ cgx $ last steps 

the problem does not arise, i.e. all but the last iteration gets directly collected garbage.

How can I achieve the same effect, being able to output some information about iterations?

+6
source share
1 answer

That's right, I think the problem is with fmap in IO : since IO actions are always performed in strict order, fmap only applies last after the entire list of results from forM was built.

Instead, you can probably use Control.Monad.foldM , which seamlessly stacks over the list (unchecked):

 runCG stoprule op rhs = do let steps = takeWhile (not . stoprule) $ cg op rhs foldM (\ _ (n, cgs) -> do putStrLn $ show n ++ " " ++ show (sqrt $ cgr2 cgs) return $ cgx cgs) undefined -- initial value for fold is ignored as long as steps is nonempty (zip [(1::Int)..] steps) 
+6
source

All Articles