Search code examples
haskellinputoutputmonads

Haskell: inline auxiliary function into do block


Working through Programming in Haskell (Second Edition) by Graham Hutton, I just managed to solve exercise 10.5 (page 138). The task is to write a function adder :: IO () that reads n numbers (to be defined interactively), sums them up, and prints the result, like so:

> adder
How many numbers? 3
5
4
6
The total is 15

The functions readInt and readLine are already given as:

readInt :: IO Int
readInt = do
  line <- readLine
  return (read line :: Int)

readLine :: IO String
readLine = do
  c <- getChar
  case c of
    '\n' -> return []
    _ -> do
      cs <- readLine
      return (c:cs)

So I just had to write the adder function:

adder :: IO ()
adder = do
  putStr "How many numbers? "
  n <- readInt
  ns <- sequence [readInt | _ <- [1..n]]
  sum <- sumUp ns
  putStr $ "The total is " ++ (show sum) ++ "\n"

sumUp :: [Int] -> IO Int
sumUp xs = return $ foldl (+) 0 xs

I'm almost happy with my solution, but I'd just like to inline the sumUp function. However, I have no idea how to do it.

How can I inline a [a] -> IO a function into a do block?


Solution

  • There is no need to use return here, we can use sum :: (Foldable f, Num a) => f a -> a to sum up the numbers:

    adder :: IO ()
    adder = do
      putStr "How many numbers? "
      n <- readInt
      ns <- sequence [readInt | _ <- [1 .. n]]
      putStr $ "The total is " ++ (show (sum ns)) ++ "\n"

    We can also repeat readInt through replicateM :: Applicative m => Int -> m a -> m [a]:

    import Control.Monad(replicateM)
    
    adder :: IO ()
    adder = do
      putStr "How many numbers? "
      ns <- readInt >>= (`replicateM` readInt)
      putStrLn $ "The total is " ++ (show (sum ns))

    as for readInt, this can be implemented as:

    readInt :: IO Int
    readInt = readLn