Lazy IO in haskell: How to return a lazy list that is generated by some locked 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 string echoes every time after I hit 'Enter'. But nothing echoes ... (I know this is an infinite loop) It looks like it won't " return
" until all the "IOs" on it are done ....
However, the following code:
main = do
s <- getContents
putStr s
Displays the string immediately after typing.
Given a function getChar
, can I write getText
that behaves like getContents
?
This can be accomplished using a function unsafeInterleaveIO
from
System.IO.Unsafe
. Then your function getText
will become
getText = do
c <- getChar
s <- unsafeInterleaveIO $ getText
return (c : s)
Abstracting a little, we can get a function to generalize this behavior
lazyDoIO :: IO a -> IO [a]
lazyDoIO act = unsafeInterleaveIO $ do
now <- act
rest <- lazyDoIO act
return (now : rest)
getText = lazyDoIO getChar
However, most Haskellers would cringe at the same time. If you want to do incremental processing of the IO
generated data stream , it would be much safer to use a library like Pipes
or Conduits
.
This is a job for ... unsafeInterleaveIO
- a special action that makes lazy IO possible. This allows you to turn the I / O action into a single thunk. This can then be stored in a framework and the action will only be evaluated when its result is required.
getText = unsafeInterleaveIO $ do
c <- getChar
s <- getText
return (c : s)
Now yours getText
returns immediately with only computation suspended for each getChar. If you want the result, it runs.
You are talking about Input, but you are not checking it in your code.
Try the following:
getText = do
c <- getChar
if (c == '\n')
then return [c]
else do
s <- getText
return (c : s)
main = do
s <- getText
putStr s