How to do it...

  1. Create a new project io-monad with the simple Stack template:
        stack new io-monad simple
  1. Open src/Main.hs; we will be editing this file.
  1. After initial module definition, add the following imports. Only those functions that are used in the program are imported from the corresponding module:
        import System.IO (hGetLine, hIsEOF, withFile, Handle, 
IOMode(..))
import System.Environment (getArgs)
import Control.Monad
import Data.List (intercalate)
  1. Write the function getLineSeq, which returns a list of lines when given a file handle:
         -- From the file handle, check if we have reached end of file,
-- otherwise read the file line by line
getLinesSeq :: Handle -> IO [String]
getLinesSeq h = do
eof <- hIsEOF h
-- Use (:) to get the line and append remaining ...
if eof then return [] else (:) <$> hGetLine h <*> getLinesSeq h
  1. Write the function printLine to print the line number and line in the format line number : line. It uses the intercalate function, which separates the items in the list by a given element:
         -- Print line number and string separated by :
printLine :: (Int, String) -> IO ()
printLine (lineno, line) = putStrLn $ intercalate " : " [show
lineno, line]
  1. Write the withLineNumbers function, which takes any monad that emits a list of strings and returns a list of tuple. Each tuple contains the line number and line itself: 
         -- Given a monad that gives us list of strings, return the 
list of
-- (int,string) where int is the line number, and string
represents
-- the corresponding line.
withLineNumbers :: Monad m => m [String] -> m [(Int,String)]
withLineNumbers m = zip <$> pure [1..] <*> m
  1. Now, write the main function to open a file and print the contents along with the line number:
        main :: IO ()
main = do
-- Throw an error if number of arguments is not 1
args <- getArgs
when (length args /= 1) $ do
putStrLn $ "Incorrect arguments " ++ (show args)
error "Provide file name"
-- Open the file and print the lines with line number
withFile (head args) ReadMode (\h -> do
-- Each line is zipped with line number
lines <- withLineNumbers (getLinesSeq h)
-- Print line
forM_ lines printLine
)
  1. Build and run the project. Note that we give the argument Setup.hs to the command prompt:
      stack build
stack exec -- io-monad Setup.hs
  1. Upon running the project, you should see the following output--contents of Setup.hs along with the line number: