{-# OPTIONS_HADDOCK not-home #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Generic.HKD.Types
( HKD (..)
, HKD_
, GHKD_
, Tuple (..)
) where
import Data.Barbie (ConstraintsB (..), FunctorB (..), ProductB (..), ProductBC (..), TraversableB (..))
import Data.Barbie.Constraints (Dict (..))
import Data.Function (on)
import Data.Functor.Product (Product (..))
import Data.Kind (Constraint, Type)
import Data.Proxy (Proxy (..))
import Data.Void (Void)
import GHC.Generics
import GHC.TypeLits (KnownSymbol, symbolVal)
import Test.QuickCheck.Arbitrary (Arbitrary (..), CoArbitrary (..))
import Test.QuickCheck.Function (Function (..), functionMap)
newtype HKD (structure :: Type) (f :: Type -> Type)
= HKD { runHKD :: HKD_ f structure Void }
type HKD_ (f :: Type -> Type) (structure :: Type)
= GHKD_ f (Rep structure)
type family GHKD_ (f :: Type -> Type) (rep :: Type -> Type)
= (output :: Type -> Type) | output -> f rep where
GHKD_ f (M1 index meta inner) = M1 index meta (GHKD_ f inner)
GHKD_ f (left :*: right) = GHKD_ f left :*: GHKD_ f right
GHKD_ f (K1 index value) = K1 index (f value)
GHKD_ f (left :+: right) = GHKD_ f left :+: GHKD_ f right
instance (Eq tuple, Generic xs, Tuple f xs tuple)
=> Eq (HKD xs f) where
(==) = (==) `on` toTuple
instance (Ord tuple, Generic xs, Tuple f xs tuple)
=> Ord (HKD xs f) where
compare = compare `on` toTuple
instance (Semigroup tuple, Generic xs, Tuple f xs tuple)
=> Semigroup (HKD xs f) where
x <> y = fromTuple (toTuple x <> toTuple y)
instance (Monoid tuple, Generic xs, Tuple f xs tuple)
=> Monoid (HKD xs f) where
mempty = fromTuple mempty
instance (Arbitrary tuple, GToTuple (HKD_ f structure) tuple)
=> Arbitrary (HKD structure f) where
arbitrary = fmap (HKD . gfromTuple) arbitrary
instance (CoArbitrary tuple, GToTuple (HKD_ f structure) tuple)
=> CoArbitrary (HKD structure f) where
coarbitrary (HKD x) = coarbitrary (gtoTuple x)
instance (Generic structure, Function tuple, Tuple f structure tuple)
=> Function (HKD structure f) where
function = functionMap toTuple fromTuple
class GShow (named :: Bool) (rep :: Type -> Type) where
gshow :: rep p -> String
instance GShow named inner => GShow named (D1 meta inner) where
gshow = gshow @named . unM1
instance (GShow 'True inner, KnownSymbol name)
=> GShow any (C1 ('MetaCons name fixity 'True) inner) where
gshow (M1 x) = symbolVal (Proxy @name) <> " {" <> gshow @'True x <> "}"
instance (GShow 'False inner, KnownSymbol name)
=> GShow any (C1 ('MetaCons name fixity 'False) inner) where
gshow (M1 x) = symbolVal (Proxy @name) <> " " <> gshow @'False x
instance (GShow 'True left, GShow 'True right)
=> GShow 'True (left :*: right) where
gshow (left :*: right) = gshow @'True left <> ", " <> gshow @'True right
instance (GShow 'False left, GShow 'False right)
=> GShow 'False (left :*: right) where
gshow (left :*: right) = gshow @'False left <> " " <> gshow @'False right
instance (GShow 'True inner, KnownSymbol field)
=> GShow 'True (S1 ('MetaSel ('Just field) i d c) inner) where
gshow (M1 inner) = symbolVal (Proxy @field) <> " = " <> gshow @'True inner
instance GShow 'False inner => GShow 'False (S1 meta inner) where
gshow (M1 inner) = gshow @'False inner
instance (Show (f inner)) => GShow named (K1 R (f inner)) where
gshow (K1 x) = show x
instance (Generic structure, GShow 'True (HKD_ f structure))
=> Show (HKD structure f) where
show (HKD x) = gshow @'True x
class Tuple (f :: Type -> Type) (structure :: Type) (tuple :: Type)
| f structure -> tuple where
toTuple :: HKD structure f -> tuple
fromTuple :: tuple -> HKD structure f
class GToTuple (rep :: Type -> Type) (tuple :: Type)
| rep -> tuple where
gfromTuple :: tuple -> rep p
gtoTuple :: rep p -> tuple
instance GToTuple inner tuple
=> GToTuple (M1 index meta inner) tuple where
gfromTuple = M1 . gfromTuple
gtoTuple = gtoTuple . unM1
instance (GToTuple left left', GToTuple right right')
=> GToTuple (left :*: right) (left', right') where
gfromTuple (x, y) = gfromTuple x :*: gfromTuple y
gtoTuple (x :*: y) = (gtoTuple x, gtoTuple y)
instance GToTuple (K1 index inner) inner where
gfromTuple = K1
gtoTuple = unK1
instance (Generic structure, GToTuple (HKD_ f structure) tuple)
=> Tuple f structure tuple where
toTuple = gtoTuple . runHKD
fromTuple = HKD . gfromTuple
class GFunctorB (rep :: Type -> Type) where
gbmap :: (forall a. f a -> g a) -> GHKD_ f rep p -> GHKD_ g rep p
instance GFunctorB inner => GFunctorB (M1 index meta inner) where
gbmap f = M1 . gbmap @inner f . unM1
instance (GFunctorB left, GFunctorB right)
=> GFunctorB (left :*: right) where
gbmap f (left :*: right) = gbmap @left f left :*: gbmap @right f right
instance GFunctorB (K1 index inner) where
gbmap f (K1 x) = K1 (f x)
instance GFunctorB (Rep structure) => FunctorB (HKD structure) where
bmap f = HKD . gbmap @(Rep structure) f . runHKD
class GTraversableB (rep :: Type -> Type) where
gbtraverse
:: Applicative t
=> (forall a. f a -> t (g a))
-> GHKD_ f rep p -> t (GHKD_ g rep p)
instance GTraversableB inner => GTraversableB (M1 index meta inner) where
gbtraverse f = fmap M1 . gbtraverse @inner f . unM1
instance (GTraversableB left, GTraversableB right)
=> GTraversableB (left :*: right) where
gbtraverse f (left :*: right)
= (:*:) <$> gbtraverse @left f left
<*> gbtraverse @right f right
instance GTraversableB (K1 index inner) where
gbtraverse f (K1 x) = fmap K1 (f x)
instance (FunctorB (HKD structure), GTraversableB (Rep structure))
=> TraversableB (HKD structure) where
btraverse f = fmap HKD . gbtraverse @(Rep structure) f . runHKD
class GProductB (rep :: Type -> Type) where
gbprod :: GHKD_ f rep p -> GHKD_ g rep p -> GHKD_ (f `Product` g) rep p
gbuniq :: (forall a. f a) -> GHKD_ f rep p
instance GProductB inner => GProductB (M1 index meta inner) where
gbprod (M1 x) (M1 y) = M1 (gbprod @inner x y)
gbuniq zero = M1 (gbuniq @inner zero)
instance (GProductB left, GProductB right)
=> GProductB (left :*: right) where
gbprod (leftX :*: rightX) (leftY :*: rightY)
= gbprod @left leftX leftY :*: gbprod @right rightX rightY
gbuniq zero
= gbuniq @left zero :*: gbuniq @right zero
instance GProductB (K1 index inner) where
gbprod (K1 x) (K1 y) = K1 (Pair x y)
gbuniq zero = K1 zero
instance (FunctorB (HKD structure), GProductB (Rep structure))
=> ProductB (HKD structure) where
bprod (HKD x) (HKD y) = HKD (gbprod @(Rep structure) x y)
buniq zero = HKD (gbuniq @(Rep structure) zero)
class GAllBC (rep :: Type -> Type) where
type GAllB (c :: Type -> Constraint) rep :: Constraint
class GConstraintsB (rep :: Type -> Type) where
gbaddDicts :: GAllB c rep => GHKD_ f rep p -> GHKD_ (Dict c `Product` f) rep p
instance GAllBC inner => GAllBC (M1 index meta inner) where
type GAllB c (M1 index meta inner) = GAllB c inner
instance GConstraintsB inner => GConstraintsB (M1 index meta inner) where
gbaddDicts (M1 x) = M1 (gbaddDicts @inner x)
instance (GAllBC left, GAllBC right) => GAllBC (left :*: right) where
type GAllB c (left :*: right) = (GAllB c left, GAllB c right)
instance (GConstraintsB left, GConstraintsB right)
=> GConstraintsB (left :*: right) where
gbaddDicts (left :*: right)
= gbaddDicts @left left :*: gbaddDicts @right right
instance GAllBC (K1 index inner) where
type GAllB c (K1 index inner) = c inner
instance GConstraintsB (K1 index inner) where
gbaddDicts (K1 x) = K1 (Pair Dict x)
instance
( FunctorB (HKD structure)
, GConstraintsB (Rep structure)
, GAllBC (Rep structure)
)
=> ConstraintsB (HKD structure) where
type AllB c (HKD structure) = GAllB c (Rep structure)
baddDicts
:: forall c f
. AllB c (HKD structure)
=> HKD structure f
-> HKD structure (Dict c `Product` f)
baddDicts (HKD x)
= HKD (gbaddDicts @(Rep structure) x)
class GProductBC (rep :: Type -> Type) where
gbdicts :: GAllB c rep => GHKD_ (Dict c) rep p
instance GProductBC inner => GProductBC (M1 index meta inner) where
gbdicts = M1 gbdicts
instance (GProductBC left, GProductBC right)
=> GProductBC (left :*: right) where
gbdicts = gbdicts :*: gbdicts
instance GProductBC (K1 index inner) where
gbdicts = K1 Dict
instance
( ProductB (HKD structure)
, ConstraintsB (HKD structure)
, GProductBC (Rep structure)
) => ProductBC (HKD structure) where
bdicts = HKD gbdicts