2

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?

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Madderote
  • 1,107
  • 10
  • 19

2 Answers2

3

The thing about do notation is, every monadic action value in it (those to the right of <-s, or on their own line) must belong to the same monad. It's

 do {
      x <- ma ;          -- ma :: m a     x       :: a
      y <- mb ;          -- mb :: m b     y       :: b   ( with the same m! )
      return (foo x y)   --               foo x y :: c     return (foo x y) :: m c
    }                    --    :: m                  c

Now, since sequence [getLine, getLine, getLine] :: IO [String], this means your do block belongs in IO.

But you can treat the values in their own right, when you got them:

fGetSeq :: IO ()
fGetSeq = do
  inputs <- sequence [getLine, getLine, getLine]   -- inputs :: [String]
  let vals = fTest inputs
  mapM_ print vals

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

-- or just
fGetSeq1 = do
  inputs <- sequence [getLine, getLine, getLine]
  mapM_ print ( fTest inputs )

-- or
fGetSeq2 = do { vals <- fTest <$> sequence [getLine, getLine, getLine] ;
                mapM_ print vals }   -- vals :: [Int]

-- or even (with redundant parens for clarity)
fGetSeq3 = mapM_ print =<< ( fTest <$> sequence [getLine, getLine, getLine] )
    --   = mapM_ print . fTest =<< sequence [getLine, getLine, getLine]

The essence of Monad is the layering of the pure 'Haskell world' calculations in between the potentially impure, 'effectful' computations.

So we already are in the pure Haskell world, on the left hand side of that <-. Again, inputs :: [String]. A pure value.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
1

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
sshine
  • 15,635
  • 1
  • 41
  • 66