Full source code

Contains code for generating operator compositions. Details are below.

Operator argument arrangements

Each Functor has a number of arguments, let’s call it Arity for simplicity:

type Arity = Nonempty List Unit

Currently we are interested in single and double slot functors (others are not in use anyway):

arguments = is @(Nonempty List Arity) ["#", "##"]

In case of double slot functors (like Arrow, Product, Sum, Attribute, Transition), we need a way to designate current focused parameter - we can use Scrolling List for it:

type Position = Scrolling List

So we can expand each Arity to all possible focused argument Position with kyo operator:

positions arity = arity
 `kyo` Range @(Position Unit)

We also need to define Variance, either covariant or contravariant marker:

type Variance = Unit `S` Unit
 
pattern Contra e = This e
pattern Co e = That e

Right now focused slots are empty - for each functor’s Position we should variate a Variance:

variances = Both @(P) `ho` to @(Nonempty List)
 `ha__` (`lu` Contra Unit) `lo` (`lu` Co Unit)

Now it’s time to compose it all together with yok operator:

arrangements = arguments
 `yok_` Plane `ha` positions
 `yok_` Plane `ha` variances

As a result we have these single operator templates:

`ya`, `yo`, `yai`, `yoi`, `yia`, `yio`

How to compose operators together?

Before composing operators we should take a quick look at Functor composition - it’s used a lot in Я. Long story short, we compose functors by treating one of them as an argument of another one:

tt (t e)

We just distinguish different functors by longer variable names for now.

In internal modules of Я these type operators are heavily used:

(tt `T'TT'I` t) ~ (t `TT'T'I` tt)

Composition for single argument functors is trivial - we could represent it as a list. But for multi-parameter functors we need a Tree (especially if we would like to generate Monoidal Functor’s as well):

ttt i (tt (t _) ii)

In this example above we have a composition of ttt i _, tt _ ii and t functors.

We know what to do next, I guess.

Universal quantification

How can we generate a sequence like this?

t i (tt (ttt e) ii)

We need a stateful counter that we are going to increment every time we generate a new variable name by traversing free variable slots:

quant symbol variables = variables
 `yukl` Forth `ha` New `ha` State `ha` Event `hv` fill symbol

As a counter we have a Nonempty List of Unit’s, we fill it with a proper symbol.

fill symbol counter = counter `yo` symbol `lu_` push Unit `ho` that `hv` counter

Well, about a symbol itself - we need two counters, one for functors (t) and another one for ignored arguments (i):

 `yuk_____` New `ha` State `ha___` Event `ha_` variable T focus `ho__'ha` Scope ...
 `lo____'yp` New `ha` State `ha___` Event `ha_` variable I other `ho__'ha` Scope ...
 `hv______` this @(Position Unit) `hv` x

We are done with renaming, so that we have Position Name instead of Position Unit - all slots are filled with proper variable names. Let’s call this result type a Layer:

type Layer = Position Name `P` Variance

Building up a functorial tree

Next step is to merge a layer into a functorial tree.

We actually already have a dedicated labeled natural transformation for it - a focused item of Position is becoming a root item and unfocused ones are becoming singleton trees - on the samee level with an old root. I hope this visualisation could help you to understand it:

We can apply it to a wrapped product of a Position and a Tree and get an updated Tree back:

wrap @(AR) `ho` Aloft @Position `ho` to @Tree
 : Position Name `P` Tree Name `AR___` Tree Name

Combining everything together

We are almost done, it’s only left to incrementally combine constant templates set (ya, yo, yai, yoi, yia, yio) with an accumulator:

combinations affix template =
 affix `lu` Cross `hv` template
 `yp'yo'hd` push @(Nonempty List)
 `ho'ho` that @(Nonempty List _)

As soon as template sequences are ready, we can start rendering operators:

operator template = template
 `yokl` Forth `ha` New `ha` layer
 `he'he'hv___` wrap [Unit] `lu` wrap [Unit] `lu` intro @Tree [by E] `lu` []
 `yi__` render `ha` that @(Namespace `P` Tree Name `P` List Layer)

Final notes

That’s all folks! I just covered main logic from this generator, I didn’t cover how we add Kleisli source morphisms to it and other small details - but you should grasp the idea of how it works. I hope it would help you to better undersand how operators work and how easy to compose them.

Rendering code isa bit messy and repetitive right now, I probably should come up with a better intermediate representation for it (like unifying constraint structure, keep all generated universal quantifiers somewhere) - but it’s a matter of finding a time slot to do it.

I wanted to tell you about this since it’s first real world application of Я.

Full source code