{-# LANGUAGE
    BangPatterns,
    FlexibleContexts,
    PolyKinds #-}

module Generic.Data.Internal.Utils where

import Data.Coerce
import GHC.Generics

-- | Convert between types with representationally equivalent generic
-- representations.
gcoerce
  :: (Generic a, Generic b, Coercible (Rep a) (Rep b))
  => a -> b
gcoerce :: a -> b
gcoerce = Rep b Any -> b
forall a x. Generic a => Rep a x -> a
to (Rep b Any -> b) -> (a -> Rep b Any) -> a -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Rep a Any -> Rep b Any
forall k (f :: k -> *) (g :: k -> *) (x :: k).
Coercible f g =>
f x -> g x
coerce1 (Rep a Any -> Rep b Any) -> (a -> Rep a Any) -> a -> Rep b Any
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> Rep a Any
forall a x. Generic a => a -> Rep a x
from

-- | Compose 'gcoerce' with a binary operation.
gcoerceBinop
  :: (Generic a, Generic b, Coercible (Rep a) (Rep b))
  => (a -> a -> a) -> (b -> b -> b)
gcoerceBinop :: (a -> a -> a) -> b -> b -> b
gcoerceBinop f :: a -> a -> a
f x :: b
x y :: b
y = a -> b
forall a b.
(Generic a, Generic b, Coercible (Rep a) (Rep b)) =>
a -> b
gcoerce (a -> a -> a
f (b -> a
forall a b.
(Generic a, Generic b, Coercible (Rep a) (Rep b)) =>
a -> b
gcoerce b
x) (b -> a
forall a b.
(Generic a, Generic b, Coercible (Rep a) (Rep b)) =>
a -> b
gcoerce b
y))

-- | Coerce while preserving the type index.
coerce' :: Coercible (f x) (g x) => f x -> g x
coerce' :: f x -> g x
coerce' = f x -> g x
forall a b. Coercible a b => a -> b
coerce

coerce1 :: Coercible f g => f x -> g x
coerce1 :: f x -> g x
coerce1 = f x -> g x
forall a b. Coercible a b => a -> b
coerce

-- | Elimination of @V1@.
absurd1 :: V1 x -> a
absurd1 :: V1 x -> a
absurd1 !V1 x
_ = [Char] -> a
forall a. HasCallStack => [Char] -> a
error "impossible"

-- | A helper for better type inference.
from' :: Generic a => a -> Rep a ()
from' :: a -> Rep a ()
from' = a -> Rep a ()
forall a x. Generic a => a -> Rep a x
from

-- | A helper for better type inference.
to' :: Generic a => Rep a () -> a
to' :: Rep a () -> a
to' = Rep a () -> a
forall a x. Generic a => Rep a x -> a
to

-- | Lift binary combinators generically.
liftG2 :: Generic1 f => (Rep1 f a -> Rep1 f b -> Rep1 f c) -> f a -> f b -> f c
liftG2 :: (Rep1 f a -> Rep1 f b -> Rep1 f c) -> f a -> f b -> f c
liftG2 = \<?> :: Rep1 f a -> Rep1 f b -> Rep1 f c
(<?>) a :: f a
a b :: f b
b -> Rep1 f c -> f c
forall k (f :: k -> *) (a :: k). Generic1 f => Rep1 f a -> f a
to1 (f a -> Rep1 f a
forall k (f :: k -> *) (a :: k). Generic1 f => f a -> Rep1 f a
from1 f a
a Rep1 f a -> Rep1 f b -> Rep1 f c
<?> f b -> Rep1 f b
forall k (f :: k -> *) (a :: k). Generic1 f => f a -> Rep1 f a
from1 f b
b)