People are saying avoid Monad “tutorials” [written by unqualified retards] , so writing down one is an IQ test and understanding is a “filter”.
And, indeed, one has to know and understand some related concepts and become to a certain kind of mathematical thinking, otherwise one would apply bullshit imperative programming concepts instead of mathematical ones.
The first concept is “composition of functions”, the famous \(\circ\) operator of the traditional mathematical notation. it takes two functions (of a single argument) and produces a new function (of a single argument, which is operationally defined is nesting of function calls.
Notice that this is The Only Way to define composition of functions and that there is no other way to define it.
At the level of “arrows” \(g: B \rightarrow C \circ f: A \rightarrow B\) becomes \(A \rightarrow C\) and reads “g after f”, literally. Notice also that “B disappear”. So this is math.
Observe that Nesting is literally the only way to express “this [and] then that”. “And” here signifies that there are “[at least] two of them”, and “then” means “after” (a Human Language has to converge on What Is).
Notice also that contrary to the bullshit imperative programming thinking, nothing has been “assigned”, “modified” (destructively overwritten) or “executed”. We have no “commands” to yell in mathematics.
This is a pure algebra of expressions, mechanically evaluated by substitution of an equal for an equal – pure equations, which also serve as rewriting/simplification/reduction/evaluation rules, which an evaluator/simplifier/compiler can apply. GHC does this systematically and consistently all the time, all other languages suck (since they do not mechanically enforce the fundamental (also necessary and sufficient) Referential Transparency property).
So, what happens is that a new pure expression is being formed (and being bound to some fresh symbol or applied in-place to some value) and this expression denotes the very same value as if we would use an explicit variable (instead of nesting).
\[ y = f(x) z = g(y) \]
This is the definition of the composition operator
g . f = \x -> g(f x)
Notice, again, that nothing “assigned” or “executed”.
This is just a mathematical equation (a formula – a true statement) which says that “whenever you see an expression on the left size of \(=\), you can mechanically substitute the expression on the right, since they are equal to each other”.
Notice that similar equations as rewriting rules can be used to simplify (reduce) and evaluate of the expressions (formulas).
Now, if we just assume that we can compose any function whatsoever, it wouldn’t work. The types have to match, obviously, and there are some “laws” of a function composition (as an operation) in general – Associativity and Identity.
A proper composition has to be associative and have the Identity Element (the famous Identity function \(\lambda x.x\), which is also unique – a “transformation” that “does nothing”, a “step” that does not “move” you, “an arrow which goes back where it started”).
Observe that Mother Nature (Mother Molecular Biology, if you will) does not have any of such “operations” (processes) in principle, this is a pure human abstraction – a convenient social construct.
Associativity is a simple thing. If you join together 3 pieces of a water hose or a pipe (two pieces at a time), it does not matter which ones you join “first” (and the “compound” one is no different from the single piece).
And, naturally, composing with the Identity Function produces a new expression (a new nested function) that behaves exactly the same (the same outputs for the same inputs).
Why do we need this? Well, the \(0\) as the Identity Element of the operation, traditionally denoted as \(+\) and \(1\) as the identity element of the operation traditionally denoted as\(\times\) should give (You) a hint.
The whole “discipline” is to realize that composition of functions, traditionally denoted as \(\circ\) and \(+\) is the same “pattern” (process), except the Commutativity law of addition (\(a + b = b + a\)).
This is also simple – it does not matter when you merge two piles of apples into one, which one you choice to move apples into. Notice that it is also matter of “rotation” of the observer around the two piles.
In other words, these laws are Universal – do not depend of Human points of view or opinions.
Here is another subtlety. \(1\) is a Natural Number (and the true “magic” number), while \(0\) is an human abstract notion (of the Mind) defined as \(x - x\).
Ok. When you multiply two Natural Numbers you get another Natural Number. Similarly, when you compose two \(\lambda\) you get another \(\lambda\), except that the order of nesting matters, and order of composing does not.
What about the Identity element? It is a convenient “Initial (the first) element” in a chain of compositions or a “Terminal (the last) element” in a chain.
Notice that Mother Molecular Biology always uses distinct (but not arbitrary) start and stop “structural markers” for its sequences, since Reality has no Identity Elements (or even Numbers – they are (only exist as) abstractions of the Mind – of an external Observer).
There is a lot to in this very realization, which connects (binds) everything together (and points at the Mind itself).
So, the \(\lambda x.x\) is as ephemeral as \(0\), but the \(1\) has “more reality” in itself (it is a Unit).
Yes, yes, “Natural Numbers form a Monoid under the operation of addition, with 0 as the identity element” and “form a Monoid under multiplication (as a repeated addition to itself) with the identity element of 1”.
Typed function’s form a Non-Commutative Monoid under the composition operation (\(\circ\)) with \(\lambda x.x\) as the identity element.
When we think of lambdas as “arrows” we “see” how it is almost addition – “putting together” but not in an arbitrary order. And this, of course, is how “Causality/The Universe unfolds”.
“From this [and also this] that arises” is as old as the Buddha.
Yes, yes, the “whole thing” is a DAG (and a Non-Commutative Semigroup at the level of “arrows”).
A Monad is Functor with an overloaded Composition Operator.
Overloaded means that it obeys the laws of Associativity and Identity, but is not only /Nesting. (the only possible definition of function composition). It is Nesting and also something else.
And there can be lots and lots of such “something elses”.
The Functor part is also necessary. It provides (establishes) a distinct abstraction barrier – a particular algebraic structure, which has to be preserved by composition, which means that it is these particular algebraic structures are being composed.
The Applicative part says that one can be “applied” to another, but this is just writing \(AB\) instead of \(A \times B\) (again, a non-Commutative operation).