Day 04 Advanced Patterns

Monads & IO

Monads are Haskell's mechanism for sequencing computations with effects. The IO monad wraps all real-world interactions. Maybe and Either model computation

~1 hour Hands-on Precision AI Academy

Today's Objective

Either e a represents Right a (success) or Left e (error with value of type e).

01

The Maybe Monad

Maybe a is either Just a (success) or Nothing (failure). Chaining Maybe computations with >>= (bind) short-circuits on Nothing: 'lookup key m >>= lookup key2' — if the first lookup returns Nothing, the second never runs. Do-notation desugars to bind: 'do { x <- mx; y <- my; return (x+y) }' becomes 'mx >>= \x -> my >>= \y -> return (x+y)'. This eliminates null-check pyramids.

02

The IO Monad

IO a represents a computation that performs I/O and produces a value of type a. IO actions are values — combining them with >>= sequences their execution. 'main :: IO ()' is the entry point. Common actions: putStrLn, getLine, readFile, writeFile, hSetBuffering. IO keeps pure functions separate from side effects: a function that returns IO Double announces it has side effects; a function returning Double is guaranteed pure.

03

Either and Error Handling

Either e a represents Right a (success) or Left e (error with value of type e). Use it instead of exceptions for expected failures. 'parseAge :: String -> Either String Int' returns Left 'not a number' or Right 42. The Either monad short-circuits on Left, just like Maybe on Nothing. 'ExceptT' from transformers stacks Either with IO for programs with both effects.

haskell
haskell
import System.IO
import Data.Maybe (mapMaybe)
import Text.Read (readMaybe)

-- Safe integer parsing
safeParseInt :: String -> Maybe Int
safeParseInt = readMaybe

-- Chain Maybe with do-notation
lookupAndDouble :: String -> [(String, Int)] -> Maybe Int
lookupAndDouble key db = do
  val    <- lookup key db
  double <- safeParseInt (show (val * 2))
  return double

-- IO: interactive sum calculator
main :: IO ()
main = do
  hSetBuffering stdout LineBuffering
  putStrLn "Enter numbers (blank to finish):"
  nums <- collectNums
  putStrLn $ "Sum: " ++ show (sum nums)
  putStrLn $ "Count: " ++ show (length nums)

collectNums :: IO [Int]
collectNums = do
  line <- getLine
  if null line
    then return []
    else case readMaybe line :: Maybe Int of
      Nothing -> do putStrLn "Not a number, skipping."
                    collectNums
      Just n  -> do rest <- collectNums
                    return (n : rest)
💡
Think of do-notation as a recipe: each line performs an action and names its result. The IO monad ensures these actions execute in order. Without IO, Haskell code has no defined evaluation order.

Supporting References & Reading

Go deeper with these external resources.

Docs
Monads & IO Official documentation for haskell.
GitHub
Monads & IO Open source examples and projects for Monads & IO
MDN
MDN Web Docs Comprehensive web technology reference

Day 4 Checkpoint

Before moving on, confirm understanding of these key concepts:

Continue To Day 5
Day 5 of the Haskell in 5 Days course