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 Я.