How so? Why is it such a big distinction? Why isn't that just encapsulation? "Handling mutable state in a 'stateless' way" is basically just Smalltalk style Object Oriented programming. (As opposed to Java or C++, which has some differences.)
dirty languages:
input -> function_a -> output/input -> function_b -> output
^ ^
| |
side_effect_a side_effect_b
| |
v v
lovecraftian_primordial_soup_of_global_state
pure languages:
input -> function_a -> output/input -> function_b -> output
^ ^
| |
side_effect_a side_effect_b ------>
|
+-------------------------------------------->
If C++ were pure, the type-signatures would look like (output, side_effect_a) function_a(input);
(output, side_effect_b) function_b(input);
The drawback is that the type-signature of function_b(function_a()) becomes complex. Now, function_b needs to accept and pass-on the upstream side-effects. To compose function_a and function_b, we need to convert the type-signature of function_b to (output, side_effect_b, side_effect_a) function_b(input, side_effect_a);
Fortunately, ">>=" converts function_b under the hood. Which allows us to write function_a() >>= function_b() >>= function_c >>= function_d
and pretend that each ">>=" is just a ";" without wrestling with compound inputs and compound returns.Note that this informal statement doesn't necessarily mean you have to be encapsulating data. A behavior, a contract, an assertion, compositions of all of these etc can also be encapsulated.
Fun ideas (not necessarily true but fun to think about):
* Monads kinda convert nesting to concatenations.
* A monad is the leakiest of the abstractions. You are always a single level of indirection away from the deepest entity encapsulated within.
* What's common among brackets, curly braces and paranthesises(?) is them being monadic if you remove them from their final construction while keeping the commas or semicolons.
Very important note: You should have already stopped reading by now if you got angry to due these false analogies.
We can have arbitrarily nested monads:
monadicObj.bind((T value) => monad.wrap(monad.wrap(value)));
Remember, `bind` only unwraps one layer. Without it unwrapping one layer, programs would continue accumulating large stacks of monads in monads.I would also point out that it only collapses abstractions of the same kind; Maybe's bind only unwraps one layer of Maybe's. If you have a Promise<Maybe<Foo>> where Foo contains potentially more monads as instance-variables, those don't all get collapsed.
I like the 'converting nesting to concatenating' observation.
Sometimes we do need parentheses though, because most languages are not associative 5 - 2 - 1 is not the same as 5 - (2 - 1). Basically minus does not form a monoid, so the parens matter.