Unnecessary, redundant abstractions is the root or of all evil. Especially the wrong ones.

There are many problems with serious FP programming, most of them are caused by millions of idiots who are constantly broadcasting virtue signaling pseudo-intellectual bullshit.

Another set problems is that thinking symbolically, in terms of unfamiliar and unnatural notation is hard – unpacking chains of partially applied constructors, converted to a prefix notation… But thinking in terms of arrows, almost without notation, is ever harder – one has to discard every familiar but inapplicable in this very abstract context, notions, and think in an unfamiliar way.

But once one managed to rebuild everything from the first principles, from the ground up, then, suddenly, everything clicks (for a few hours until you begin to forget it).

Okay, let’s unlearn what we have been taught, what we have read about what idiots were said about subtle topics, which require very trained mind,

What is a Product? (yes, again)

More than one of something in particular locality. Okay, we have to accept that admitting one element products, and even zero element products is convenient for algebraic completeness and notation. And it helps to “see” things as they are.

So, a product is an abstract structure, which can be better conceptualized as two things at the same locality, but just one or none will do. It is better to be imagined and visualized as a CO (carbon monoxide) molecule at first, but has to be collapsed to a single dot or a asterisk.

We are not supposed to look at its actual structure, and this is the trick. We have to forget its structure and relativity and whatever we may know.

That dot is “equipped with two projections”. First, “equipped with” is just the notion of, lets say, a Set and an operation on its elements, defined as a pure function, a Monody, let’s say. Natural Numbers under addition form a Monoid. There are also Groups, Fields and what not.

What “projections”? Well, what a single \(\lambda\) looks like? It is \(a \rightarrow b\), and we usually say “maps a to b, or goes from a to b”. This is what “arrows” are for now – a pure function (Same input – same output. Always). Just like we said before, a single mapping of values is just an ordered pair \(a, b\) – the same information, the same structure (a has to be known (observed) prior to b).

Now, a Product is “that dot” with two arrows come out of it and into “each of something”.

\(A \times B\) is the Product (the order is in which this has been written is irrelevant, just rotate it freely), and also \(A \times B \rightarrow A\) and \(A \times B \rightarrow B\) – these are the arrows or projections (since the arrows point outside of it).

Suppose we have two other arrows (pure functions, from some domain \(X\), lets say): \(f: X \rightarrow A\) and \(g: X \rightarrow B\).

Then if we somehow combine these functions together, we will get exactly that dot back: \(<f,g>:X \rightarrow A \times B\)

The function itself, by the way, is just \(x \mapsto (f(x), g(x))\) while the Product is defined as \((a, b)\) (a tuple).

And this is it, minus the notation, just visualize only arrows between dots and then rotate the whole construction freely. This is a Product.

Now think that there is one “projection” instead of two, and one “injection” arrow instead of two. That would be your single element Product, perfectly valid construction.

Here is the last mental leap: imagine that every \(x\) or \(a\) or even \(1\) or \(2\) is just such kind of a degenerate one-thing Product, whenever it may appear.

When we see \(a -> b\) we shall imagine not a “Number”, but this degenerate one-thing Product.

Now let’s have some a-has, real quick. multiple intellectual “orgasms”, you know.

Every such a degenerate Product can be freely extended with something, something else.

\(A -> (A \times S)\), lets say, and we have a Kleisli arrow. \((A \times S) \rightarrow (B \times S)\) and this is Mealey machine (a canonical state transformer) or equivalently \(S \rightarrow (A \rightarrow (B \times S))\) or a coalgebra \(S \rightarrow (B \times S)^A\).

Now what is an Arrow abstraction (Control.Arrow)? Conceptually, you lift an ordinary function \(A \rightarrow B\) by creating a Categorical morphism ((->), and the computation becomes, for instance,\((A \times C) \rightarrow (B \times C)\) , just passing \(C\) along. That \(C\) can be anything you like, not just IO. State or reminder of the Parser’s input.

With Mealy machine you explicitly pass updated state around using Products, modifying the state. With a Kleisli arrows you extend every value in the Codomain into a larger Functor, which may be a Product. With Control.Arrow you extend both sides of an arrow to be larger Products.

With Monadic interfaces you extend the Codomain and overload composition to use Kleisli arrows, with all the plumbing, unpacking and re-packing again into a tuple in the definition of >>=. With Arrows interface, you do not have to write the “unpacking, applying, re-packing” part by yourself, since the library cummerbunds will do it for you, by modifying the tuples being passed along.

This is how Products are being used. But the real question is “What is going on?” and the answer is:

“Abstraction by Parameterizarion” (of Barbara Liskov strikes back) – we are [still] adding extra parameters to an ordinary functions (lamdas) of a lazy language, by overloading what composition means.

And the structural notion was of “extending a Product”.