Skip to the content.

Traversable, Lone and Distributive

Traversable type class is a fundamental class in Haskell that most container types are instance of. The most important factor of Traversable is sequenceA. Let’s revisit it by defining our own Traversable'.

import Control.Applicative
import Data.Functor.Const
import Data.Functor.Identity

class (Functor f) => Traversable' f where
  sequence' :: (Applicative g) => f (g a) -> g (f a)

We call it sequence' here. As you can see, we have two functors f and g, and if f is Traversable', you can convert f (g a) to g (f a) with any Applicative g.

You can make most of containers an instance of Traversable' such as Identity, ((,) b), Either b and so on.

instance Traversable' Identity where
  sequence' :: (Applicative g) => Identity (g a) -> g (Identity a)
  sequence' (Identity ga) = Identity <$> ga

instance Traversable' Maybe where
  sequence' :: (Applicative g) => Maybe (g a) -> g (Maybe a)
  sequence' Nothing = pure Nothing
  sequence' (Just ga) = Just <$> ga

instance Traversable' [] where
  sequence' :: (Applicative g) => [g a] -> g [a]
  sequence' [] = pure []
  sequence' (ga : gas) = (:) <$> ga <*> sequence' gas

instance Traversable' ((,) b) where
  sequence' :: (Applicative g) => (b, g a) -> g (b, a)
  sequence' (b, ga) = (b,) <$> ga

instance Traversable' (Either b) where
  sequence' :: (Applicative g) => Either b (g a) -> g (Either b a)
  sequence' (Left b) = pure (Left b)
  sequence' (Right ga) = Right <$> ga

instance Traversable' (Const b) where
  sequence' :: (Applicative g) => Const b (g a) -> g (Const b a)
  sequence' (Const b) = pure (Const b)

You can find some instances only use fmap or (<$>) to implement Traversable'. For example, Identity and ((,) b) don’t use pure nor (<*>). So what if we have only Functor instead of Applicative? Let’s define such type class named Lone.

class (Traversable' f) => Lone f where
  sequenceL :: (Functor g) => f (g a) -> g (f a)

sequenceL is very similar to sequence', but has Functor constraint instead of Applicative. The implementations of sequenceL are identical to sequence'. You’ll find a container that always have one element can be an instance of Lone.

instance Lone Identity where
  sequenceL :: (Functor g) => Identity (g a) -> g (Identity a)
  sequenceL (Identity ga) = Identity <$> ga

instance Lone ((,) b) where
  sequenceL :: (Functor g) => (b, g a) -> g (b, a)
  sequenceL (b, ga) = (b,) <$> ga

It turns out that all Lone functors are isomorphic to ((,) b) as it has one element that will be mapped over with additional information b. Note that Identity is isomorphic to ((,) ()).

So now, we have two functor f and g, and if f is Lone, you can convert f (g a) to g (f a) with any functor g.

Now, let’s reverse the direction. What constraint do we need if we apply it to g instead of f? We can define Distributive type class.

class (Functor g) => Distributive g where
  distribute :: (Functor f) => f (g a) -> g (f a)

Identity can be an instance of Distributive.

instance Distributive Identity where
  distribute :: (Functor f) => f (Identity a) -> Identity (f a)
  distribute gfa = Identity (runIdentity <$> gfa)

Also, you can make a function an instance as well.

instance Distributive ((->) b) where
  distribute :: (Functor f) => f (b -> a) -> b -> f a
  distribute ff b = (\f -> f b) <$> ff

It turns out that all Distributive functors are isomorphic to ((->) b). Note that Identity is isomorphic to ((->) ()).

So now, we have two functors f and g, and if g is Distributive, you can convert f (g a) to g (f a) with any functor f.

There are functors that are instances of both Lone and Distributive, which are isomorphic to both ((,) b) and ((->) b), that means they’re isomorphic to Identity.

type Identical f = (Lone f, Distributive f)