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

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

import GHC.Generics

import Generic.Data.Rep.Assert
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 {cd} {gf} asserts tag a
    .  ( Generic a, Rep a ~ D1 cd gf
       , GContraNonSum tag gf
       , ApplyGCAsserts asserts gf
       , Contravariant (GenericContraF tag))
    => GenericContraF tag a
genericContraNonSum :: forall {k} {cd :: Meta} {gf :: Type -> Type}
       (asserts :: [GCAssert]) (tag :: k) a.
(Generic a, Rep a ~ D1 cd gf, GContraNonSum tag gf,
 ApplyGCAsserts asserts gf, Contravariant (GenericContraF tag)) =>
GenericContraF tag a
genericContraNonSum = (a -> gf Any)
-> GenericContraF tag (gf 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 (M1 D cd gf Any -> gf Any
forall k i (c :: Meta) (f :: k -> Type) (p :: k). M1 i c f p -> f p
unM1 (M1 D cd gf Any -> gf Any) -> (a -> M1 D cd gf Any) -> a -> gf Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> M1 D cd gf Any
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 {cd} {gf} opts asserts tag a
    .  ( Generic a, Rep a ~ D1 cd gf
       , GContraSum opts tag gf
       , ApplyGCAsserts asserts gf
       , Contravariant (GenericContraF tag))
    => GenericContraF tag String
    -> GenericContraF tag a
genericContraSum :: forall {k} {cd :: Meta} {gf :: Type -> Type} (opts :: SumOpts)
       (asserts :: [GCAssert]) (tag :: k) a.
(Generic a, Rep a ~ D1 cd gf, GContraSum opts tag gf,
 ApplyGCAsserts asserts gf, Contravariant (GenericContraF tag)) =>
GenericContraF tag String -> GenericContraF tag a
genericContraSum GenericContraF tag String
f = (a -> gf Any)
-> GenericContraF tag (gf 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 (M1 D cd gf Any -> gf Any
forall k i (c :: Meta) (f :: k -> Type) (p :: k). M1 i c f p -> f p
unM1 (M1 D cd gf Any -> gf Any) -> (a -> M1 D cd gf Any) -> a -> gf Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> M1 D cd gf Any
a -> Rep a Any
forall x. a -> Rep a x
forall a x. Generic a => a -> Rep a x
from) (forall {k} {k1} (opts :: SumOpts) (tag :: k) (gf :: k1 -> Type)
       (p :: k1).
GContraSum opts tag gf =>
GenericContraF tag String -> GenericContraF tag (gf p)
forall (opts :: SumOpts) (tag :: k) (gf :: Type -> Type) p.
GContraSum opts tag gf =>
GenericContraF tag String -> GenericContraF tag (gf p)
gContraSum @opts @tag GenericContraF tag String
f)