    {-
    A simple homogeneous pair type with useful Functor, Applicative, and
    Traversable instances.
    -}

    {-# LANGUAGE CPP #-}

    module Pair ( Pair(..), unPair, toPair, swap, pLiftFst, pLiftSnd ) where

    #include "HsVersions.h"

    import GhcPrelude

    import Outputable
    import qualified Data.Semigroup as Semi

    data Pair a = Pair { pFst :: a, pSnd :: a }
    -- Note that Pair is a *unary* type constructor
    -- whereas (,) is binary

    -- The important thing about Pair is that it has a *homogenous*
    -- Functor instance, so you can easily apply the same function
    -- to both components
    instance Functor Pair where
      fmap f (Pair x y) = Pair (f x) (f y)

    instance Applicative Pair where
      pure x = Pair x x
      (Pair f g) <*> (Pair x y) = Pair (f x) (g y)

    instance Foldable Pair where
      foldMap f (Pair x y) = f x `mappend` f y

    instance Traversable Pair where
      traverse f (Pair x y) = Pair <$> f x <*> f y

    instance Semi.Semigroup a => Semi.Semigroup (Pair a) where
      Pair a1 b1 <> Pair a2 b2 =  Pair (a1 Semi.<> a2) (b1 Semi.<> b2)

    instance (Semi.Semigroup a, Monoid a) => Monoid (Pair a) where
      mempty = Pair mempty mempty
      mappend = (Semi.<>)

    instance Outputable a => Outputable (Pair a) where
      ppr (Pair a b) = ppr a <+> char '~' <+> ppr b

    unPair :: Pair a -> (a,a)
    unPair (Pair x y) = (x,y)

    toPair :: (a,a) -> Pair a
    toPair (x,y) = Pair x y

    swap :: Pair a -> Pair a
    swap (Pair x y) = Pair y x

    pLiftFst :: (a -> a) -> Pair a -> Pair a
    pLiftFst f (Pair a b) = Pair (f a) b

    pLiftSnd :: (a -> a) -> Pair a -> Pair a
    pLiftSnd f (Pair a b) = Pair a (f b)