I have become rather interested in how computation is modeled in Haskell. Several resources have described monads as “composable computation” and arrows as “abstract views of computation”. I’ve never seen monoids, functors or applicative functors described in this way. It seems that they lack the necessary structure.
I find that idea interesting and wonder if there are any other constructs that do something similar. If so, what are some resources that I can use to acquaint myself with them? Are there any packages on Hackage that might come in handy?
Note: This question is similar to
Monads vs. Arrows and https://stackoverflow.com/questions/2395715/resources-for-learning-monads-functors-monoids-arrows-etc, but I am looking for constructs beyond funtors, applicative functors, monads, and arrows.
Edit: I concede that applicative functors should be considered “computational constructs”, but I’m really looking for something I haven’t come across yet. This includes applicative functors, monads and arrows.
Arrowsare generalized by Categories, and so by theCategorytypeclass.The
Arrowtypeclass definition hasCategoryas a superclass. Categories (in the haskell sense) generalize functions (you can compose them but not apply them) and so are definitely a “model of computation”.Arrowprovides aCategorywith additional structure for working with tuples. So, whileCategorymirrors something about Haskell’s function space,Arrowextends that to something about product types.Every
Monadgives rise to something called a “Kleisli Category” and this construction gives you instances ofArrowApply. You can build aMonadout of anyArrowApplysuch that going full circle doesn’t change your behavior, so in some deep senseMonadandArrowApplyare the same thing.Actually every
Arrowgives rise to anApplicative(universally quantified to get the kinds right) in addition to theCategorysuperclass, and I believe the combination of the appropriateCategoryandApplicativeis enough to reconstruct yourArrow.So, these structures are deeply connected.
Warning: wishy-washy commentary ahead. One central difference between the
Functor/Applicative/Monadway of thinking and theCategory/Arrowway of thinking is that whileFunctorand its ilk are generalizations at the level of object (types in Haskell),Category/Arroware generelazation of the notion of morphism (functions in Haskell). My belief is that thinking at the level of generalized morphism involves a higher level of abstraction than thinking at the level of generalized objects. Sometimes that is a good thing, other times it is not. On the other-hand, despite the fact thatArrowshave a categorical basis, and no one in math thinksApplicativeis interesting, it is my understanding thatApplicativeis generally better understood thanArrow.Basically you can think of “Category < Arrow < ArrowApply” and “Functor < Applicative < Monad” such that “Category ~ Functor”, “Arrow ~ Applicative” and “ArrowApply ~ Monad”.
More Concrete Below:
As for other structures to model computation: one can often reverse the direction of the “arrows” (just meaning morphisms here) in categorical constructions to get the “dual” or “co-construction”. So, if a monad is defined as
(okay, I know that isn’t how Haskell defines things, but
ma >>= f = join $ fmap f maandjoin x = x >>= idso it just as well could be)then the comonad is
This thing turns out to be pretty common also. It turns out that
Comonadis the basic underlying structure of cellular automata. For completness, I should point out that Edward Kmett’sControl.Comonadputsduplicatein a class between functor andComonadfor “Extendable Functors” because you can also defineIt turns out that all
Monads are also “Extendable”while all
Comonadsare “Joinable”so these structures are very close together.