The whole premise is that you end up with some main function which is a description of side effects, but you still have referential transparency and the like at a language level (so I/O doesn't need special treatment).
(there's a whole thing about "monad = IO" in laypeople discussion of Haskell which I think muddies this. There are loads of monads not about doing side effects, like list. But instead people think there's a link between monads and side effects, because we say "IO monad" instead of just "in IO" or something)
EDIT: it's v true that like.... side effects are side effects are side effects, so you can take that shortcut when talking about stuff. But if you want to get into the details about whether some expression is pure or not, you're brushing up against important semantics!