Search code examples
haskelliomonadstype-mismatchdo-notation

Haskell get values from IO domain


After reading the Haskell books I am kind of confused (or I simply forgot) how to get a value from the IO domain, into the 'Haskell world' to parse it, like so:

fGetSeq = do
  input <- sequence [getLine, getLine, getLine]
  fTest input
  mapM_ print input

fTest =  map (read :: String -> Int)

Obviously compiler complains. Couldn't match [] with IO. Is there a simple rule of thumb for passing values between 'worlds' or is it just my bad by omitting typesigs?


Solution

  • get a value from the IO domain, into the 'Haskell world'

    You use the bind operator: (>>=) :: Monad m => m a -> (a -> m b) -> m b.

    If m = IO it looks like: (>>=) :: IO a -> (a -> IO b) -> IO b.

    As you can see, the function with type a -> IO b addresses the a without IO.

    So given a value in the IO monad, e.g. getLine :: IO String:

    getInt :: IO Int
    getInt = getLine >>= (\s -> return (read s))
    

    Here, s :: String, read :: String -> Int, and return :: Int -> IO Int.

    You can rewrite this using a do-block:

    getInt :: IO Int
    getInt = do
      s <- getLine
      return (read s)
    

    Or use the standard library function that does exactly this:

    getInt :: IO Int
    getInt = readLn
    

    As for your example, you can immediately fix it using a let-binding:

    foo :: IO ()
    foo = do
      input <- sequence [getLine, getLine, getLine]
      let ints = bar input
      mapM_ print ints
    
    bar :: [String] -> [Int]
    bar = map read
    

    Or you can restructure it to use getInt as defined above:

    foo :: IO ()
    foo = sequence [getInt, getInt, getInt] >>= mapM_ print