content: {-# LANGUAGE
      DataKinds,
      PolyKinds,
      TypeFamilies,
      TypeOperators,
      UndecidableInstances #-}
  
  -- | Bifunctors.
  --
  -- Bifunctors are "two-argument functors".
  --
  -- This module is the type-level equivalent of "Data.Bifunctor".
  module Fcf.Class.Bifunctor
    ( Bimap
    , First
    , Second
    ) where
  
  import Fcf.Core (Exp, Eval)
  import Fcf.Combinators (Pure)
  
  -- $setup
  -- >>> :set -XGADTs
  -- >>> import Fcf.Core (Eval)
  -- >>> import Fcf.Combinators (Flip)
  -- >>> import Fcf.Data.Nat (Nat, type (+), type (-))
  -- >>> import Fcf.Data.Symbol (Symbol)
  -- >>> import Numeric.Natural (Natural)
  
  -- | Type-level 'Data.Bifunctor.bimap'.
  --
  -- === __Example__
  --
  -- >>> data Example where Ex :: a -> Example  -- Hide the type of examples to avoid brittleness in different GHC versions
  -- >>> :kind! Ex (Eval (Bimap ((+) 1) (Flip (-) 1) '(2, 4)) :: (Natural, Natural))
  -- Ex (Eval (Bimap ((+) 1) (Flip (-) 1) '(2, 4)) :: (Natural, Natural)) :: Example
  -- = Ex '(3, 3)
  data Bimap :: (a -> Exp a') -> (b -> Exp b') -> f a b -> Exp (f a' b')
  
  -- (,)
  type instance Eval (Bimap f g '(x, y)) = '(Eval (f x), Eval (g y))
  
  -- Either
  type instance Eval (Bimap f g ('Left  x)) = 'Left  (Eval (f x))
  type instance Eval (Bimap f g ('Right y)) = 'Right (Eval (g y))
  
  
  -- | Type-level 'Data.Bifunctor.first'.
  -- Apply a function along the first parameter of a bifunctor.
  --
  -- === __Example__
  --
  -- >>> :kind! Eval (First ((+) 1) '(3,"a"))
  -- Eval (First ((+) 1) '(3,"a")) :: (Natural, Symbol)
  -- = '(4, "a")
  data First :: (a -> Exp b) -> f a c -> Exp (f b c)
  type instance Eval (First f x) = Eval (Bimap f Pure x)
  
  -- | Type-level 'Data.Bifunctor.second'.
  -- Apply a function along the second parameter of a bifunctor.
  --
  -- This is generally equivalent to 'Data.Functor.Map'.
  --
  -- === __Example__
  --
  -- >>> :kind! Eval (Second ((+) 1) '("a",3))
  -- Eval (Second ((+) 1) '("a",3)) :: (Symbol, Natural)
  -- = '("a", 4)
  data Second :: (c -> Exp d) -> f a c -> Exp (f a d)
  type instance Eval (Second g x) = Eval (Bimap Pure g x)
  