Challenge 1: Manually Unwrapping IO (Maybe Int) — Possible Solution ==================================================================== Main.hs: import Text.Read (readMaybe) readIntLine :: IO (Maybe Int) readIntLine = do line <- getLine return (readMaybe line) main :: IO () main = do result <- readIntLine case result of Just n -> putStrLn ("Parsed: " ++ show n) Nothing -> putStrLn "Not a valid integer" Sample run (input: "42"): Parsed: 42 Sample run (input: "abc"): Not a valid integer Explanation: readIntLine performs the IO action (getLine) first, producing a plain String, then wraps the RESULT of parsing that string in a Maybe using readMaybe -- no transformer involved at all. Back in main, using the result requires two separate steps: `result <- readIntLine` unwraps the OUTER IO layer (a normal do-notation bind), and then a manual `case` expression is needed to unwrap the INNER Maybe layer -- exactly the "manual double-unwrapping" the chapter describes as the real cost of simply nesting IO (Maybe a) rather than using a transformer. WHY THIS WORKS AS AN ANSWER ------------------------------ This demonstrates the plain nested IO (Maybe Int) approach exactly as the chapter's own loadConfig example does, requiring a separate manual case expression to handle the inner Maybe after the outer IO bind, setting up the direct contrast Challenge 2 asks for.