Previously: Natural transformation as a basis of control
So, this is our first type of functors I want to introduce you - Hom Functors.
Mapping slots
In previous chapter you should have seen these circle operators:
(`yo`)(yo): t a `AR_` into (from a o) (t o)
There could be situations, when functors have more than one argument to map on - products as an example. For such cases we have following operators:
(`yoi`)(yoi): t a i `AR_` into (from a o) (t o i)
(`yio`)(yio): t i a `AR_` into (from a o) (t i o)
It’s actually possible to define functors with arbitrary ariety and positions, probably they could be useful for really complicated types (probably something for streaming abstractions or tricky resource management definitions):
(`yoii`)(yoii): t a i ii `AR_` into (from a o) (t o i ii)
(`yioi`)(yioi): t i a ii `AR_` into (from a o) (t i o ii)
(`yiio`)(yiio): t i ii a `AR_` into (from a o) (t i ii o)
But let’s go back to our example. How would we map different functions to a product of Boolean and List?
x = True Unit `lu` [1,2,3,4,5]: Boolean `P` List Integer
x `yoi` rewrap not ====> False Unit `lu` [1,2,3,4,5]
x `yio` intro @List ===> False Unit `lu` [[1],[2],[3],[4],[5]]
Another way to map exact argument is to use wrappers. If both parts of products are the same you can use Both
wrapper:
x = [1,2,3] `lu` [4,5,6]: List Integer `P` List Integer
x `yi` Both `yo` (+ 1) ===> [2,3,4] `lu` [5,6,7]
Actually, Boolean is just a wrapper over a Sum of Unit which focuses on second argument - that why in code snippet above we used rewrap expression:
x = True Unit: Boolean
unwrap x: Unit `S` Unit
y = x `yu` [1,2,3,4,5]: Maybe (List Integer)
unwrap y: Unit `S` List Integer
Function functor
There is another common example of two argument functor - function. Let’s say we have such a function:
odd: Integer `AR_` Boolean
In this case you can modify Integer
parameter contravariantly and Boolean
parameter covariantly:
odd `yai` (+ 999999)
odd `yio` rewrap not
Actually those two operators above (yai/yio) have special Hom Functor aliases:
odd `ha` (+ 999999)
odd `ho` rewrap not
If it looks like composition to you - you are not wrong. This is exactly composition and reverse composition (from some books) operators - they are just much powerful in Я so that they can map one category into another (in contrast with endofunctors behavior).
Attribute functor
You can think of attributes in Я as pointers to a substrucutre in a bigger structure. Haskell developers get used to call it optics but I think it’s pretty bad naming since we don’t use any physics laws for reasoning on these programming constructions. “It reminds me that thing so let’s call it this way” is not very convincing reason to me, words heve meanings - even if you import them into your vocabulary.
Okay, I should stop ranting, let’s get to the point.
focus: Reference (Scrolilng List i) (Only i) (Only i)
unwrap: Reference (Only i) (i) (i)
Scope focus `ho` Scope unwrap: Scrolilng List i `AT` Only i
As you can see, we can map Attribute morphism on a second argument of Attribute functor so that you can think of it as a composition of Attributes.
(`ho`)(ho): t i a `AR_` into (from a o) (t i o)
If we try to instantiate ho operator for our case it would look like this. You can see that it’s not endofunctor, we map Attribute into Arrow:
(`ho`)(ho): i `AT` a `AR___` a `AT` o `AR_` i `AT` o
Transition functor
Another case of applying Hom Functor operators is Transition.
Consider a stateful computation which pushes item on top of a Stackable datastructure:
Event `ha` push: Transition (Nonempty List i) i
We can apply an Arrow covariantly on a second argument which represents result of a stateful computation:
Event `ha` push @(Nonempty List) `ho` Some: Transition (Nonempty List i) (Maybe i)
But for a first argument which represents state we cannot apply Arrow since state itself is neither covariant nor covarcontravariantiant in Arrow category - it’s actuall invariant.
We cannot handle invariant positions (probably not for a long time though) - but we actually don’t even need that. We already have Attributes. But instead of pushing an item on a top of the stack (let’s say it’s slot #1) we are going to zoom into the state to a substructure of a Nonempty List which is actually Maybe (List Nonempty item)
then wrap it into a List (since it’s its supertype) - as a result, we push an item on slot #2!
Event `ha` push @List `ha__` Scope `hv` sub `ho_` Scope `hv` wrap: Transition (Nonempty List i) i
But wait, the type declarations hasn’t changed… and this is fine. Keep in mind - it’s contravariant position, morphisms are reversed. But now we push not to a Nonempty List but rather to a regular List.
Generalizing stage
So I think you already got this - in Я all operarator names on Hom Functor like parameter arrangement (T[-,+]
) start with h
token: ho, ha, hu, hv and often behave like composition.
Well, in this chapter there were no commutative diagrams at all! Let’s fix it:
Continue: Using Yoneda lemma for flipping notation