I'm going to make an attempt to succinctly explain monads:
A monadic data type is a container that can be mapped and flattened.
- It's a container, because it stores has an inner type, which refers to the data it contains. For example, think of the type parameter that describes the elements of a list.
- It is "mappable" in the sense that its contained type can be converted to another type by providing an operation from the first type to the second type. Imagine converting a list of strings to a list of integers, where each integer is the length of the corresponding string.
- It can be "flattened" in the sense that if its inner type is an extra nesting of the container type, it can be flattened to a just one level of the container type. For example, a list of lists of integers can be flattened to just a list of integers by concatenating the sublists.
- It seems kind of trivial to say, but there has to be a way to construct a container given one of the elements it's supposed to contain.
There are also a couple of rule these operations have to fulfill. They're kind of abstract when stated on their own, but make intuitive sense when you consider examples.
Pretty simple, huh? What's cool about it is that you can assign all sorts of metadata to the container type and semantics to the flatten operation, and you get a nice representation of a pipeline of operations, where each one depends on the all the previous steps. This is basically just imperative programming -- each step depends on the current context and can optionally mutate that context, affecting subsequent steps.
You might ask, "but isn't imperative programming what we're trying not to do?" In the most general sense with mutable state everywhere, yes. But monads give you a way to describe imperative logic in very tightly controlled contexts.