haskelllazy-evaluation

Lazy IO in haskell: How to return a lazy list that is generated by some blocking IO?


getText = do
    c <- getChar
    s <- getText
    return (c : s)

main = do
    s <- getText
    putStr s

What I expect to see is that the input line being echoed each time after I press 'Enter'. But nothing is echoed ... (I know that this is a infinite loop) It seems that it won't "return" until all the "IO" above it are performed. ...

However, the following code:

main = do
    s <- getContents
    putStr s

It display the line immediately after input.

Given the function getChar, can I write a getText that behaves like getContents?


Solution

  • This can be accomplished with the unsafeInterleaveIO function from System.IO.Unsafe. Your getText function then becomes

    getText = do
        c <- getChar
        s <- unsafeInterleaveIO $ getText
        return (c : s)
    

    Abstracting slightly, we can get a function to generalize this behaviour

    lazyDoIO :: IO a -> IO [a]
    lazyDoIO act = unsafeInterleaveIO $ do
        now <- act
        rest <- lazyDoIO act
        return (now : rest)
    
    getText = lazyDoIO getChar
    

    Most Haskellers would cringe at doing this, however. If you want to do incremental stream processing of IO generated data, it would be much safer to use a library like Pipes or Conduits.