Reading — step 1 of 4
Learn
~1 min readMonads in Practice
IO is the monad for real-world side effects. Pure functions can't do I/O; functions that return IO a can.
main :: IO ()
main = do
name <- getLine -- IO String — extract the line
putStrLn ("Hello, " ++ name) -- IO () — write
Common IO actions:
getLine :: IO String
putStr, putStrLn :: String -> IO ()
print :: Show a => a -> IO () -- print x = putStrLn (show x)
readLn :: Read a => IO a -- parse one line of typed input
getContents :: IO String -- read all of stdin
Mixing pure and IO — use <- to extract IO values, regular bindings (let) for pure work:
main :: IO ()
main = do
line <- getLine
let upper = map toUpper line -- pure transform
putStrLn upper
Loops in IO — no built-in for, use these:
import Control.Monad
forM_ [1..5] $ \n -> do
putStrLn ("line " ++ show n)
replicateM_ 3 (putStrLn "hi") -- print 3 times
mapM_ putStrLn ["a", "b", "c"]
-- with results:
results <- mapM readLn [(); (); ()] -- read 3 things
Why IO is a monad — same do/>>= syntax as Maybe, but threading a different context (the world state, conceptually). The pattern of "a sequence of computations" works for both pure error handling and impure side effects.
Pure code can never "escape" IO — there's no unsafelyRunIO :: IO a -> a. This separation is what makes Haskell's purity work in practice.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…