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)
You should really avoid using anything in System.IO.Unsafe
if at all possible. They tend to kill referential transparency and are not common functions used in Haskell unless absolutely necessary.
If you change your type signature a little I suspect you can get a more idiomatic approach to your problem.
consume :: Char -> Bool
consume 'x' = False
consume _ = True
main :: IO ()
main = loop
where
loop = do
c <- getChar
if consume c
then do
putChar c
loop
else return ()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With