IO marks computations that may perform input, output, or other external effects. Declaring IO in a return type makes impurity visible to the compiler and separates pure calculations from effectful routines. section 1.5.1
An IO-returning function may call impure operations, so callers must also participate in the IO chain. Effects therefore propagate upward through functions until reaching an impure entry point such as main. section 1.5.1
Pure calculation, effectful boundary
The pure max function computes a value without IO. printNumber performs output and returns IO (), while printMaxNumber and main compose the effectful boundary around the calculation. section 1.5.1
Listing 1.7 Pure functions and impure routines
max :: Float -> Float -> Float
max a b = if a > b then a else b #1
printNumber :: Float -> IO ()
printNumber n = putStrLn ("Biggest number: " ++ show n) #2
printMaxNumber :: Float -> Float -> IO () #3
printMaxNumber n1 n2 = do
let maxN = max n1 n2
printNumber maxN
main :: IO () #4
main = printMaxNumber 3.14 2.71
Trace how a pure maximum calculation feeds an IO-producing printer and then reaches the program entry point. section 1.5.1
In concurrent application design, bare IO is treated as a temptation requiring architectural restraint. STM, IORefs, MVars, and free monads provide more deliberate structures for state handling and framework construction. section 9.3





