So, I’ve spent a considerable amount of time today reading various tutorials and descriptions about monads in Haskell. I think I’ve got it figured out. To summarize:
- Monads are not magical things which somehow allow side effects without actually allowing side effects, which is how most Haskell literature seems to paint it. The I/O monad has side effects aplenty, and it doesn’t even pretend to do things like keeping around the state of the universe.
- Monads are actually containers with a special way of operating on the contents. This allows the programmer to string together a bunch of functions which are executed in sequence, passing the output from the last function into the next function.
- Monads are used for I/O simply because it is not possible to access the I/O values without being a function bound to a monad, creating a “trap door” which preserves the purity of the rest of the functional code. Side-effected values (ie, user input) can only propogate up, not down. This is not necessarily true for all monads.
Basically, monads are a way for functional programmers to feel good about writing imperative code when they come up against a problem that actually requires it, and makes sure that a minimum of functional code is infected with the icky non-determinism.
Personally, I feel that the benefits of functional programming have very little to do with the no-side-effects, no-modifying-state aspects of it. I would’ve wrote an entry on why functional programming works so well, if someone hadn’t already done it twenty years ago. You’ll note that “confusing the hell out of people that try to learn your language” is not on the list of what makes functional programming so useful.