Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reimplementing getContents using getChar

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?

like image 575
johanneslink Avatar asked Dec 03 '16 17:12

johanneslink


2 Answers

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)
like image 169
Reid Barton Avatar answered Nov 17 '22 22:11

Reid Barton


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 ()
like image 1
bojo Avatar answered Nov 17 '22 23:11

bojo