On my journing towards grasping lazy IO in Haskell I tried the following:
main = do
chars <- getContents
consume chars
consume :: [Char] -> IO ()
consume [] = return ()
consume ('x':_) = consume []
consume (c : rest) = do
putChar c
consume rest
which just echos all characters typed in stdin until I hit 'x'.
So, I naively thought it should be possible to reimplement getContents
using getChar
doing something along the following lines:
myGetContents :: IO [Char]
myGetContents = do
c <- getChar
-- And now?
return (c: ???)
Turns out it's not so simple since the ???
would require a function of type IO [Char] -> [Char]
which would - I think - break the whole idea of the IO monad.
Checking the implementation of getContents
(or rather hGetContents
) reveals a whole sausage factory of dirty IO stuff. Is my assumption correct that myGetContents
cannot be implemented without using dirty, ie monad-breaking, code?
You need a new primitive unsafeInterleaveIO :: IO a -> IO a
that delays the execution of its argument action until the result of that action would be evaluated. Then
myGetContents :: IO [Char]
myGetContents = do
c <- getChar
rest <- unsafeInterleaveIO myGetContents
return (c : rest)