Arrows, …

… Monads, Profunctors, Categories, Applicatives, and everything else.

Manuel Bärenz

(Pro)functors

Functors

class Functor f where
  fmap
    ::  (a ->   b)
    -> f a -> f b
data Stream a = Stream a (Stream a)

head (Stream a _) = a
instance Functor Stream where
  fmap f (Stream a as) = Stream (f a) $ fmap f as

Stream functions

Stream a -> Stream b subtype hierarchy:

  • a -> b (via fmap)
  • a -> Stream b (via e.g. head)
  • Synchronous stream functions (SF)
  • Arbitrary functions Stream a -> Stream b
newtype SF a b = SF (a -> (b, SF a b))

apply :: SF a           b
  -> Stream a -> Stream b
apply (SF unSF) (Stream a as) = let (b, sf') = unSF a
  in Stream b $ apply sf' as

Functor

instance Functor ((->) a) where
  fmap = (.)

newtype Kleisli f a b = Kleisli (a -> f b)
instance Functor (Kleisli Stream a) where
  fmap f (Kleisli k) = Kleisli $ fmap f . k

instance Functor (SF a) where
  fmap f (SF unSF) = SF $ \a ->
    let (b, sf') = unSF a in (f b, fmap f sf')

Profunctor

But we can also precompose with functions! (A.k.a Contravariant.)

class Profunctor p where
  lmap ::  (a -> b)
       -> p      b    c
       -> p a         c
  rmap ::       (b -> c)
       -> p a    b
       -> p a         c
instance Profunctor (->) where
  lmap = flip (.)
  rmap = (.) -- This is just fmap!

(Pro)functor

  • Every functor f gives several canonical profunctors:
    • a -> f b (Kleisli)
    • f a -> b (Cokleisli, irrelevant today)
    • f a -> f b
  • Instances are all cobbled together with (.) and fmap.
  • But there are profunctors that are not of this form!
instance Profunctor SF where
  rmap = fmap
  lmap f (SF unSF) = SF $ \a ->
    let (c, sf') = unSF $ f a in (c, lmap f sf')

Functor < Profunctor?

  • Every Profunctor gives you back a Functor..
    • …by fixing the “input” type
    • Just use rmap
  • But you cannot always recover the Profunctor from that!
  • Example: SF

Arrows

Applicative < Arrow < Monad?

In the Functor-Applicative-Monad-hierarchy, people sometimes insert Arrow.

-- Note: I'm leaving out 'Category' for simplicity
class Arrow arr where
  arr ::    (a -> b)
      -> arr a    b
  (&&&) :: arr a  b
        -> arr a     c
        -> arr a (b, c)
  (>>>) :: arr a  b
        -> arr    b  c
        -> arr a     c

Profunctor < Arrow!

  • arr and (>>>) generalise lmap and rmap.

Arrow < Monad?

  • Every monad m gives several canonical arrows:
    • a -> m b (Kleisli)
    • m a -> b (Cokleisli, irrelevant today)
    • m a -> m b
  • Instances are all cobbled together with (.) and fmap.
  • But there are arrows that are not of this form!
instance Arrow SF where
  -- Exercises for you

Applicative < Arrow?

  • Every Arrow gives you back an Applicative
    • …by fixing the “input” type
    • Just use (&&&)
  • But you cannot always recover the Arrow from that!
  • Example: SF