Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Computation Constructs (Monads, Arrows, etc.)

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.

like image 657
Doug Moore Avatar asked Mar 09 '12 15:03

Doug Moore


People also ask

What are monads in programming?

What is a Monad? A monad is an algebraic structure in category theory, and in Haskell it is used to describe computations as sequences of steps, and to handle side effects such as state and IO. Monads are abstract, and they have many useful concrete instances. Monads provide a way to structure a program.

What is a monad example?

Monads are simply a way to wrapping things and provide methods to do operations on the wrapped stuff without unwrapping it. For example, you can create a type to wrap another one, in Haskell: data Wrapped a = Wrap a. To wrap stuff we define return :: a -> Wrapped a return x = Wrap x.

What is Kleisli arrow?

Kleisli is a type of Arrow for a Monadic context. It is defined as: final case class Kleisli[F[_], A, B](run: A => F[B]) Kleisli Type Signature. The Kleisli type is a wrapper around A => F[B] , where F is some context that is a Monad.

What is a monad OOP?

In terms of OO programming, a monad is an interface (or more likely a mixin), parameterized by a type, with two methods, return and bind that describe: How to inject a value to get a monadic value of that injected value type; How to use a function that makes a monadic value from a non-monadic one, on a monadic value.


1 Answers

Arrows are generalized by Categories, and so by the Category typeclass.

 class Category f where
     (.) :: f a b -> f b c -> f a c
     id :: f a a

The Arrow typeclass definition has Category as 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". Arrow provides a Category with additional structure for working with tuples. So, while Category mirrors something about Haskell's function space, Arrow extends that to something about product types.

Every Monad gives rise to something called a "Kleisli Category" and this construction gives you instances of ArrowApply. You can build a Monad out of any ArrowApply such that going full circle doesn't change your behavior, so in some deep sense Monad and ArrowApply are the same thing.

 newtype Kleisli m a b = Kleisli { runKleisli :: a -> m b }

 instance Monad m => Category (Kleisli m) where
     id = Kleisli return
     (Kleisli f) . (Kleisli g) = Kleisli (\b -> g b >>= f)

 instance Monad m => Arrow (Kleisli m) where
     arr f = Kleisli (return . f)
     first (Kleisli f) = Kleisli (\ ~(b,d) -> f b >>= \c -> return (c,d))
     second (Kleisli f) = Kleisli (\ ~(d,b) -> f b >>= \c -> return (d,c))

Actually every Arrow gives rise to an Applicative (universally quantified to get the kinds right) in addition to the Category superclass, and I believe the combination of the appropriate Category and Applicative is enough to reconstruct your Arrow.

So, these structures are deeply connected.

Warning: wishy-washy commentary ahead. One central difference between the Functor/Applicative/Monad way of thinking and the Category/Arrow way of thinking is that while Functor and its ilk are generalizations at the level of object (types in Haskell), Category/Arrow are 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 that Arrows have a categorical basis, and no one in math thinks Applicative is interesting, it is my understanding that Applicative is generally better understood than Arrow.

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

class Functor m => Monad m where
   return :: a -> m a
   join :: m (m a) -> m a

(okay, I know that isn't how Haskell defines things, but ma >>= f = join $ fmap f ma and join x = x >>= id so it just as well could be) then the comonad is

class Functor m => Comonad m where
   extract :: m a -> a -- this is co-return
   duplicate :: m a -> m (m a) -- this is co-join

This thing turns out to be pretty common also. It turns out that Comonad is the basic underlying structure of cellular automata. For completness, I should point out that Edward Kmett's Control.Comonad puts duplicate in a class between functor and Comonad for "Extendable Functors" because you can also define

   extend :: (m a -> b) -> m a -> m b -- Looks familiar? this is just the dual of >>=
   extend f = fmap f . duplicate
   --this is enough
   duplicate = extend id

It turns out that all Monads are also "Extendable"

   monadDuplicate :: Monad m => m a -> m (m a)
   monadDuplicate = return

while all Comonads are "Joinable"

   comonadJoin :: Comonad m => m (m a) -> m a
   comonadJoin = extract

so these structures are very close together.

like image 123
Philip JF Avatar answered Nov 09 '22 19:11

Philip JF