{-# LANGUAGE AllowAmbiguousTypes #-} -- due to type class design

module Generic.Data.Function.Contra
  ( GenericContra(..)
  , genericContraNonSum, GContraNonSum
  , genericContraSum,    GContraSum
  ) where

import GHC.Generics

import Generic.Data.Function.Contra.NonSum
import Generic.Data.Function.Contra.Sum
import Generic.Data.Function.Contra.Constructor
import Data.Functor.Contravariant

-- | Generic contra over a term of non-sum data type @a@.
--
-- @a@ must have exactly one constructor.
genericContraNonSum
    :: forall {k} (tag :: k) a
    .  ( Generic a
       , Contravariant (GenericContraF tag)
       , GContraNonSum tag (Rep a)
    ) => GenericContraF tag a
genericContraNonSum :: forall {k} (tag :: k) a.
(Generic a, Contravariant (GenericContraF tag),
 GContraNonSum tag (Rep a)) =>
GenericContraF tag a
genericContraNonSum = (a -> Rep a Any)
-> GenericContraF tag (Rep a Any) -> GenericContraF tag a
forall a' a.
(a' -> a) -> GenericContraF tag a -> GenericContraF tag a'
forall (f :: Type -> Type) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap a -> Rep a Any
forall x. a -> Rep a x
forall a x. Generic a => a -> Rep a x
from (forall (tag :: k) (gf :: Type -> Type) p.
GContraNonSum tag gf =>
GenericContraF tag (gf p)
forall {k} {k1} (tag :: k) (gf :: k1 -> Type) (p :: k1).
GContraNonSum tag gf =>
GenericContraF tag (gf p)
gContraNonSum @tag)

-- | Generic contra over a term of sum data type @a@.
--
-- You must provide a contra function for constructor names.
--
-- This is the most generic option, but depending on your string manipulation
-- may be slower.
genericContraSum
    :: forall {k} (tag :: k) a
    .  ( Generic a
       , Contravariant (GenericContraF tag)
       , GContraSum tag (Rep a)
    ) => GenericContraF tag String -> GenericContraF tag a
genericContraSum :: forall {k} (tag :: k) a.
(Generic a, Contravariant (GenericContraF tag),
 GContraSum tag (Rep a)) =>
GenericContraF tag String -> GenericContraF tag a
genericContraSum GenericContraF tag String
f = (a -> Rep a Any)
-> GenericContraF tag (Rep a Any) -> GenericContraF tag a
forall a' a.
(a' -> a) -> GenericContraF tag a -> GenericContraF tag a'
forall (f :: Type -> Type) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap a -> Rep a Any
forall x. a -> Rep a x
forall a x. Generic a => a -> Rep a x
from (forall (tag :: k) (gf :: Type -> Type) p.
GContraSum tag gf =>
GenericContraF tag String -> GenericContraF tag (gf p)
forall {k} {k1} (tag :: k) (gf :: k1 -> Type) (p :: k1).
GContraSum tag gf =>
GenericContraF tag String -> GenericContraF tag (gf p)
gContraSum @tag GenericContraF tag String
f)