This boils down to an ugly hack which is what a monad is.
The principle is that a function by definition is a mapping, which maps a set of values into another set of values. The crucial difference between a function and a procedures is that a function must, by definition, produce the same input for the same output. Always.
So, when a supposed function produces sometimes values and sometimes errors, everything is broken. It is not a function anymore. The ugly hack is to wrap different values into a compound structure of a certain type and reformulate the law of functions to the law of procedures - the same type of output for same values. This is what Maybe monad and Either monad are. Mere ADTs, mere wrappers. As long as input and output values are of the same type (as long as they conform to the same protocol/interface they are considered to be equivalent, but, of course, not equal) procedures which take such type as input and output values could be composed and form a pipelines (like any other procedures with consistent types).
There is nothing more than that. And, strictly speaking, it is not a pure functional programming anymore. Just procedural with static typing (hello, Haskell purists!).