IO and Interacting with the World
21.1 The IO Type
IO a is a description of an action that, when executed by the runtime, may perform side effects and produce a value of type a. main :: IO () is the entry point; the runtime executes it.
Pure code can't execute IO actions. IO code can call pure functions freely. This separation is enforced by the type system.
import System.IO
import Control.Monad (unless)
main :: IO ()
main = do
hSetBuffering stdout NoBuffering
putStr "What's your name? "
name <- getLine -- `<-` extracts the result of an IO action
let greeting = "Hello, " ++ name -- `let` for pure values
putStrLn greeting
putStr "Enter numbers separated by spaces: "
line <- getLine
let nums = map read (words line) :: [Int]
print (sum nums)
loop
loop :: IO ()
loop = do
putStr "> "
input <- getLine
unless (input == "quit") $ do
putStrLn ("You said: " ++ input)
loop21.2 Common IO Functions
putStrLn, putStr :: String -> IO ()
print :: Show a => a -> IO ()
getLine :: IO String
getContents :: IO String -- lazy: all of stdin
interact :: (String -> String) -> IO () -- interact (map toUpper)
readFile :: FilePath -> IO String
writeFile, appendFile :: FilePath -> String -> IO ()
readLn :: Read a => IO a
import System.Environment (getArgs, getProgName, lookupEnv)
import System.Exit (exitSuccess, exitFailure, exitWith, ExitCode (..))
import System.Directory (doesFileExist, listDirectory, createDirectoryIfMissing, removeFile)
main :: IO ()
main = do
args <- getArgs
case args of
[file] -> do
exists <- doesFileExist file
if exists
then readFile file >>= putStr . unlines . map reverse . lines
else hPutStrLn stderr "file not found" >> exitFailure
_ -> do
prog <- getProgName
hPutStrLn stderr ("usage: " ++ prog ++ " FILE")
exitWith (ExitFailure 2)21.3 Handles and Resource Safety
Lazy IO pitfalls: readFile/hGetContents read lazily, which can cause "handle closed" errors or leaks. Use strict Data.Text.IO/Data.ByteString reads, or streaming libraries (Module 44).
import System.IO
import Control.Exception (bracket)
copyUpper :: FilePath -> FilePath -> IO ()
copyUpper src dst =
withFile src ReadMode $ \hIn -> -- the handle is closed automatically, even on exceptions
withFile dst WriteMode $ \hOut -> do
contents <- hGetContents hIn
hPutStr hOut (map toUpper contents)
-- bracket acquire release use
withResource :: IO ()
withResource = bracket (openFile "log.txt" AppendMode) hClose (\h -> hPutStrLn h "entry")21.4 Mutable References in IO
import Data.IORef
counterDemo :: IO ()
counterDemo = do
ref <- newIORef (0 :: Int)
mapM_ (\x -> modifyIORef' ref (+ x)) [1 .. 100]
readIORef ref >>= print -- 5050
-- Also: ST monad for local mutation with a pure interface (Module 22), MVar/TVar for concurrency