Monads From Actions
After studying both monads and actions, we can build the next result: how to produce monadsa given a categorical action.
Theorem
Given a monoidal category , a monoid , and an action of this monoidal category on , then the partially applied functor is a monad.
To produce a monad we need to give its functor and its two natural transformations. For this, we first make precise what we have available before we define the functor. We will need:
- A category
- A monoidal category with
- A monoid
- A monoidal action
Using the above, we can define a functor by partially applying our action to our monoid , in other words, .
This allows us to write explicitly what the natural tranformations of a suitable monad should be. The monadic unit can be written as is given by the lax unitor . Similarly, the monadic multiplication can be written as and is given by composing the actor of the action with the monoidal multiplication .
- [!] finish the diagrams programmatically
We implement monads from action in Idris by first parameterising all the data we need.
parameters
(0 o1, o2: Type)
(cat1 : Category o1)
(cat2 : Category o2)
(mon : Monoidal cat1)
(act : LaxAction cat1 cat2 mon)
(m : MonoidObject {o=o1} {cat=cat1} {mon})Then we define the functor that we claim is a monad, that is .
T : Endo cat2
T = applyL m.obj act.lactionwe define the unit of the monad a by composing vertically the unitor from the action with the map emerging from the neutral map of the monoid
unit : idF cat2 =>> T
unit = act.lunitor ⨾⨾⨾ applyNT m.η act.lactionThe multiplication is a map which expands to . Its codomain almost matches the actor map and the monoid operation on compose
We define an alias for the functor
-- the functor F'(x) = (m ⊗ m) ⊘ x
F' : Endo cat2
F' = applyL {a = cat1} ((m.obj ⊗ m.obj)) act.lactionAnd we use it to define the natural transformation via the multiplication of the monoid object.
multAction : F' =>> T
multAction = applyNT m.mult act.lactionWe need to partially apply the actor map to be a natural transformation , we can do it by applying using whiskering but in this case it was faster to write down the map manually.
(⊘) : o1 -> o2 -> o2
(⊘) x y = act.laction.mapObj (x && y)
private infixr 3 ⊘
(~⊘~) : {x, y : o1} -> {z, w : o2} -> (x ~> y) {cat=cat1} -> z ~> w -> x ⊘ z ~> y ⊘ w
(~⊘~) m1 m2 = act.laction.mapHom (x && z) (y && w) (m1 && m2)
private infixr 3 ~⊘~
actor : (x, y : o1) -> (z : o2) -> x ⊘ (y ⊘ z) ~> (x ⊗ y) ⊘ z
actor x y z = act.lactor.component (x && (y && z))
unitor : (x : o2) -> x ~> mon.i ⊘ x
unitor x = act.lunitor.component x
private infixr 3 ⊘>
-- This could be done with careful composition of act.lactor and some lemmas about apply
-- but writing out the definition is actually easier
appActor : T ⨾⨾ T =>> F'
appActor = MkNT
(\v => actor m.obj m.obj v)
(\x, y, h => let
0 steps : CongPipeline ? ?
steps =
Cong (\vx =>
(actor m.obj m.obj x) |>
(vx ~⊘~ h))
[ cat1.id (m.obj ⊗ m.obj)
, cat1.id m.obj -⊗- cat1.id m.obj]
>| ((cat1.id m.obj ~⊘~ (cat1.id m.obj ~⊘~ h)) |>
(actor m.obj m.obj y))
:: Nil
in runProof steps
[ sym (mon.mult.presId (m.obj && m.obj))
, act.lactor.commutes
(m.obj && (m.obj && x))
(m.obj && (m.obj && y))
(cat1.id m.obj && (cat1.id m.obj && h))
]
)Using the applied actor map and the action we obtain the multiplication morphism of our monad
mult : T ⨾⨾ T =>> T
mult = appActor ⨾⨾⨾ multActionThose definitions bring us to the definition of as a monad in , the coherence diagrams are left in appendix.
-
complete squares
-
finish the coherence diagrams in idris
MonadFromLaxAction : Monad cat2 T
MonadFromLaxAction = MkMonad unit mult square identityLeft identityRight