Question

I'm trying to generate a infinite lazy stream of values from IO wrapped by WriterT. I'm using conduits to consume this stream and write it to a file. I'm well aware of the strictness of IO in its bind operator, so how could I produce this stream lazily having IO there?

If it is impossible, should I try to change to lazy ST?

import Data.Conduit
import Control.Monad.Writer
import Data.DList as DL

type Stream = WriterT (DL.DList String) IO ()

generator :: Stream
generator = do
    tell $ DL.singleton "something"
    generator

runStream :: Stream -> IO ()
runStream s = runResourceT $ stream s
    where stream s       = sourceStream s $$ sinkStream -- sinkStream just writes to a file
          sourceStream s = do w <- liftIO $ execWriterT s
                           CL.sourceList (DL.toList w)
Was it helpful?

Solution

Seeing that nobody gave a full answer, I'll convert my comment into one. One of the biggest advantages of conduit is that it saves us from using lazy IO! Using a complicated WriterT goes against the idea. Instead, we should make generator a Source and then just plug it with a file Sink:

import Control.Monad
import Data.Conduit
import Data.Conduit.Binary
import qualified Data.ByteString.Char8 as BS

generator :: Monad m => Source m String
generator = replicateM_ 3 (yield "something\n")
    -- or `forever (...)` if you want an infinite loop

-- Reads Strings, converts them to ByteStrings and writes
-- to a file.
sinkStream :: MonadResource m => FilePath -> Sink String m ()
sinkStream file = mapInput BS.pack (const Nothing) (sinkFile file)

main :: IO ()
main = runResourceT (generator $$ sinkStream "/tmp/output.txt")
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top