-- parses a poem, refering words to CMU for pronounciation
parsePoem = do p ← getContents
return (checkEachLine (lines p))
main = do p ← parsePoem
Do you see the monad? Well, that's a rather mean question, because the monad is entirely implied in this program fragment.
One of the motivators for incorporating monads into Haskell was dealing with things in the "real world" that fall outside easy descriptions in purely functional (mathematical) domains. One of those things is input and output. Whereas an equation...
... was true yesterday, is true now, and will be true tomorrow (albeit, it took over 100 pages of introduction to set theory in the Principia Mathematica to be able to back up that truth), things of the "real world", particularly input and output change, and change irrevocably. For example, if I buy and eat some ice cream (mint chocolate chip, please!), I'm not getting a refund. The state of the world has changed: I'm parted from my money, and that scoop of ice cream is no longer in the shop's inventory (and they have a Form 1120S, Schedule A to prove it). Input/output, as part of the state of the world, is one of the members of the awkward squad. So, to help it to be a used and useful language, the Haskell designers included the IO monad as one of the libraries.
So, if one were to query
parsePoem, one would find that they are the types IO
IO [Char], respectively, meaning that
maininteracts with IO, returning "nothing", and
parsePoeminteracts with IO, returning a list of characters (the poem that you just finished reading aloud).
In short, the IO monad does all the handshaking (or, more correctly, handwaving) with the state of the world, so the programmer can do something as "simple" as printing "Hello, world!" easily. All this monad asks, as every monad asks, is "Pay no attention to that man behind the curtain."