Search code examples
haskelliomonadsio-monaddo-notation

Why does the type match on the next line but not on the same line in `do` block?


I’m reading in several lines from the input that the user has to type:

main :: IO ()
main = do
  let size = 3
  arr <- replicateM size getLine
  let pairs = map parsePair arr
  print pairs

Why am I allowed to do map parsePair arr on a separate line but not on the same line, like this:

arr <- map parsePair (replicateM size getLine)

Doing so, I get the error :

• Couldn't match type ‘[]’ with ‘IO’
  Expected type: IO [Int]
    Actual type: [[Int]]

To give you more details, here is parsePair:

parsePair string = map parseInt $ words string

parseInt :: String -> Int
parseInt s = read s :: Int

Solution

  • Because the type of replicateM size getLine is IO [String], it is not a list of Strings, it is basically a description of an IO action that will obtain a [String]. You can see the arrow <- in an IO monad as a way to retrieve it and unpack the result.

    You can however do some processing on that like, since IO is a Functor as well, you can make use of fmap :: Functor f => (a -> b) -> f a -> f b:

    main :: IO [Int]
    main = do
      let size = 3
      fmap (map parsePair) (replicateM size getLine)

    or you can shift the fmap to the getLine part:

    main :: IO [Int]
    main = do
      let size = 3
      replicateM size (fmap parsePair getLine)

    Note that there is a readLn :: Read a => IO a function that is basically fmap read getLine (except that it does some addional error handling). We can thus use:

    main :: IO [Int]
    main = do
      let size = 3
      replicateM size readLn