The point of monads, from the point of view of programmer convenience, is that they let you have a bag of contextual stuff along with your values. Thus if you think of a server application that processes requests, you might want to carry all of:
- configuration applicable to this request
(or global configuration)
- request metadata (time received, from where,
from whom, authenticated how, etc.)
- request processing state
- logs/traces
- I/O methods, so that
- you can make all your code deterministic
and thus easier to write tests for
- so you can mock the world (see previous
item)
with each request. Thus each function that handles a request can get all of that context, and can be fully deterministic, yet it can function in a non-deterministic world.Besides this there's everything about the Maybe ("Optional") and Error/Either monads that just makes it easy to make sure all errors and "nulls" are handled.
And what makes all of this possible (in Haskell) is the use of operators (functions) and syntax that lets one write "statements" that are actually combined into large expressions.