Pairs are, essentially, defined like this:
data (,) a b = (,) a b
The Functor
class looks like this:
class Functor f where
fmap :: (a -> b) -> f a -> f b
Since the types of function arguments and results must have kind *
(i.e. they represent values rather than type functions that can be applied further or more exotic things), we must have a :: *
, b :: *
, and, most importantly for our purposes, f :: * -> *
. Since (,)
has kind * -> * -> *
, it must be applied to a type of kind *
to obtain a type suitable to be a Functor
. Thus
instance Functor ((,) x) where
-- fmap :: (a -> b) -> (x,a) -> (x,b)
So there's actually no way to write a Functor
instance doing anything else.
One useful class that offers more ways to work with pairs is Bifunctor
, from Data.Bifunctor
.
class Bifunctor f where
bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
bimap f g = first f . second g
first :: (a -> b) -> f a y -> f b y
first f = bimap f id
second :: (c -> d) -> f x c -> f x d
second g = bimap id g
This lets you write things like the following (from Data.Bifunctor.Join
):
newtype Join p a =
Join { runJoin :: p a a }
instance Bifunctor p => Functor (Join p) where
fmap f = Join . bimap f f . runJoin
Join (,)
is then essentially the same as Pair
, where
data Pair a = Pair a a
Of course, you can also just use the Bifunctor
instance to work with pairs directly.
type Tree a = Free T2 a
. In fact, most uses of that type (in its capacity as a functor) involve branching or concurrency of some sort. – Infantine