The type value is IO Intnot really Int. It is more like a piece of paper that says: "Hey, Haskell runtime, please Intvalue Intso-and-so." A piece of paper is inert and remains the same, even if Intultimately produced by the runtime are different.
You send a piece of paper at runtime by assigning it main. If an action is IOnever in the way mainand instead languishes inside a container, it will never be executed.
Functions that return actions IOare pure like others. They always return the same sheet of paper. What the runtime does with these instructions is another question.
If they were not clean, we would have to think twice before changing
foo :: (Int -> IO Int) -> IO Int
foo f = liftA2 (+) (f 0) (f 0)
so that:
foo :: (Int -> IO Int) -> IO Int
foo f = let x = f 0 in liftA2 (+) x x
source
share