{- | 'traverse' for generic data types.

TODO This is harder to conceptualize than generic 'foldMap'. No nice clean
explanation yet.

This function can provide generic support for simple parser-esque types.
-}

module Generic.Data.Function.Traverse
  ( GenericTraverse(..)
  , genericTraverseNonSum , GTraverseNonSum
  , GenericTraverseSum(..), PfxTagCfg(..)
  , genericTraverseSum,     GTraverseSum
  , eqShowPfxTagCfg
  ) where

import GHC.Generics

import Generic.Data.Function.Traverse.NonSum
import Generic.Data.Function.Traverse.Sum
import Generic.Data.Function.Traverse.Constructor

import Data.Text qualified as Text

-- | Generic 'traverse' over a term of non-sum data type @f a@.
--
-- @f a@ must have exactly one constructor.
genericTraverseNonSum
    :: forall f a
    .  (Generic a, GTraverseNonSum f (Rep a), Functor f)
    => f a
genericTraverseNonSum :: forall (f :: Type -> Type) a.
(Generic a, GTraverseNonSum f (Rep a), Functor f) =>
f a
genericTraverseNonSum = forall a x. Generic a => Rep a x -> a
to forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {k} {k} (f :: k -> Type) (f' :: k -> k) (p :: k).
GTraverseNonSum f f' =>
f (f' p)
gTraverseNonSum

-- | Generic 'traverse' over a term of sum data type @f a@.
--
-- @f a@ must have at least two constructors.
--
-- You must provide a configuration for how to handle constructors.
genericTraverseSum
    :: forall f a pt
    .  (Generic a, GTraverseSum f (Rep a), GenericTraverseC f pt, Functor f)
    => PfxTagCfg pt
    -> f a
genericTraverseSum :: forall (f :: Type -> Type) a pt.
(Generic a, GTraverseSum f (Rep a), GenericTraverseC f pt,
 Functor f) =>
PfxTagCfg pt -> f a
genericTraverseSum PfxTagCfg pt
ptc = forall a x. Generic a => Rep a x -> a
to forall (f :: Type -> Type) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {k} (f :: Type -> Type) (f' :: k -> Type) pt (p :: k).
(GTraverseSum f f', GenericTraverseC f pt) =>
PfxTagCfg pt -> f (f' p)
gTraverseSum PfxTagCfg pt
ptc

-- | Construct a prefix tag config using existing 'Eq' and 'Show' instances.
--
-- The user only needs to provide the constructor name parser.
eqShowPfxTagCfg :: (Eq a, Show a) => (String -> a) -> PfxTagCfg a
eqShowPfxTagCfg :: forall a. (Eq a, Show a) => (String -> a) -> PfxTagCfg a
eqShowPfxTagCfg String -> a
f = PfxTagCfg
    { pfxTagCfgFromCstr :: String -> a
pfxTagCfgFromCstr = String -> a
f
    , pfxTagCfgEq :: a -> a -> Bool
pfxTagCfgEq = forall a. Eq a => a -> a -> Bool
(==)
    , pfxTagCfgShow :: a -> Text
pfxTagCfgShow = String -> Text
Text.pack forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Show a => a -> String
show
    }