Hello,<br><br>I am learning haskell and I found a space leak that I find difficult to solve. I&#39;ve been asking at #haskell but we could not solve<br>the issue.<br><br>I want to lazily read a set of 22 files of about 200MB each, filter them and then I want to output the result into a unique file.<br>
If I modify the main function to work only with one input file,  the program runs without issues. I will call this version A.<br>Version B  uses a mapM_ to iterate over a list of filenames and uses appendFile to output the result of filtering each file.<br>
In this case the memory usage grows sharply and quickly (profiles show constant memory growth). In less than a minute, memory<br>occupation will make my system hang with swapping.<br><br>This is version B: <br><br>------------------------------- Program B --------------------------------------------------------------------------------------------------------------------<br>
import Data.List <br>import System.Environment<br>import System.Directory<br>import Control.Monad <br><br><br>-- different types of chromosomes<br>data Chromosome =    C1<br>                | C2<br>                | C3<br>
                | C4<br>                | C5<br>                | C6<br>                | C7<br>                | C8<br>                | C9<br>                | C10<br>                | C11<br>                | C12<br>                | C13<br>
                | C14                <br>                | C15<br>                | C16<br>                | C17<br>                | C18<br>                | C19<br>                | CX<br>                | CY<br>                | CMT<br>
                  deriving (Show) <br>-- define a window<br>type Sequence = [Char]<br>-- Window data<br>data Window = Window { sequen :: Sequence,<br>                       chrom :: Chromosome,<br>                       pos   :: Int<br>
                     }<br>-- print a window<br>instance Show Window where<br>    show w =  (sequen w) ++ &quot;\t&quot; ++ show (chrom w) ++ &quot;\t&quot; ++ show (pos w)<br><br>-- Reading fasta files with haskell<br><br>
-- Initialize the <br>main = do <br>       -- get the arguments (intput is <br>       [input, output, windowSize] &lt;- getArgs<br>       -- get directory contents (only names)<br>       names &lt;- getDirectoryContents input<br>
       -- prepend directory<br>       let fullNames = filter isFastaFile $ map (\x -&gt; input ++ &quot;/&quot; ++ x) names<br>       let wSize = (read windowSize)::Int<br>       -- process the directories<br>       mapM (genomeExecute output wSize filterWindow)  fullNames<br>
<br><br>-- read the files one by one and write them to the output file<br>genomeExecute :: String -&gt; Int -&gt; (Window -&gt; Bool) -&gt; String -&gt; IO ()<br>genomeExecute  outputFile windowSize f inputFile = do<br>  fileData &lt;- readFile inputFile<br>
  appendFile outputFile $ fastaExtractor fileData windowSize f <br><br>-- <br>isFastaFile :: String -&gt; Bool<br>isFastaFile fileName = isSuffixOf &quot;.fa&quot; fileName <br><br><br>-- fasta extractor (receives a Fasta String and returns a windowed string ready to be sorted)<br>
-- an example on how to compose several functions to parse a fasta file<br>fastaExtractor :: String -&gt; Int -&gt; (Window -&gt; Bool) -&gt; String<br>fastaExtractor input wSize f = printWindowList $ filter f $ readFasta  wSize input<br>
<br>-- MAIN FILTER that removes N elements from the strings!<br>filterWindow :: Window -&gt; Bool<br>filterWindow w = not (elem &#39;N&#39; (sequen w))<br><br>-- print a window list (the printing makes it ready for output as raw data)<br>
printWindowList :: [Window] -&gt; String<br>printWindowList l = unlines $ map show l<br><br>-- read fasta, remove stuff that is not useful from it<br>-- removes the <br>readFasta :: Int -&gt; [Char] -&gt; [Window]<br>readFasta windowSize sequence = <br>
    -- get the header<br>    let (header:rest) = lines sequence<br>        chr = parseChromosome header<br>        in<br><br>-- We now do the following:<br>--      take window                  create counter                         remove newlines      <br>
   map (\(i, w) -&gt; Window w chr i) $ zip [0..]  $ slideWindow windowSize  $ filter ( &#39;\n&#39; /= )  $ unlines rest<br><br><br>slideWindow :: Int -&gt; [Char] -&gt; [[Char]]<br>slideWindow _ [] = []<br>slideWindow windowSize l@(_:xs)  = take windowSize l : slideWindow  windowSize xs<br>
<br><br><br>-- Parse the chromosome from a fasta comment<br>-- produce a more compact chromosome representation<br>parseChromosome :: [Char] -&gt; Chromosome<br>parseChromosome line<br>    | isInfixOf &quot;chromosome 1,&quot; line = C1<br>
    | isInfixOf &quot;chromosome 2,&quot; line = C2<br>    | isInfixOf &quot;chromosome 3,&quot; line = C3<br>    | isInfixOf &quot;chromosome 4,&quot; line = C4<br>    | isInfixOf &quot;chromosome 5,&quot; line = C5<br>    | isInfixOf &quot;chromosome 6,&quot; line = C6<br>
    | isInfixOf &quot;chromosome 7,&quot; line = C7<br>    | isInfixOf &quot;chromosome 8,&quot; line = C9<br>    | isInfixOf &quot;chromosome 10,&quot; line = C10<br>    | isInfixOf &quot;chromosome 11,&quot; line = C11<br>
    | isInfixOf &quot;chromosome 12,&quot; line = C12<br>    | isInfixOf &quot;chromosome 13,&quot; line = C13<br>    | isInfixOf &quot;chromosome 14,&quot; line = C14<br>    | isInfixOf &quot;chromosome 15,&quot; line = C15<br>
    | isInfixOf &quot;chromosome 16,&quot; line = C16<br>    | isInfixOf &quot;chromosome 17&quot;  line = C17<br>    | isInfixOf &quot;chromosome 18&quot;  line = C18<br>    | isInfixOf &quot;chromosome 19&quot;  line = C19<br>
    | isInfixOf &quot;chromosome X&quot;   line = CX<br>    | isInfixOf &quot;chromosome Y&quot;   line = CY<br>    | isInfixOf &quot;mitochondrion&quot;  line = CMT<br>    | otherwise = error &quot;BAD header&quot;<br><br>
<br>-------------------------------- End of program B ------------------------------------------------------------------------------------------------<br><br>-------------------------------- Program A ---------------------------------------------------------------------------------------------------------<br>
If instead of the main function given above I use the following main function to process only one input file, things work OK for even<br>the largest files. Memory usage remains constant in this case.<br><br>main = do <br>
       -- get the arguments<br>       [input, output, windowSize] &lt;- getArgs<br>       -- keep the input stream<br>       inpStr &lt;- readFile input<br>       let wSize = (read windowSize)::Int<br>       writeFile output $ fastaExtractor inpStr wSize filterWindow<br>
<br><br>It is not easy for me to see why is Haskell keeping data in memory. Do you have any idea why  program B is<br>not working?<br><br>Thank you for your help!<br><br>Arnoldo Muller<br>