htree-0.1.1.0: src/Data/HTree/Existential.hs
{-# LANGUAGE UndecidableInstances #-}
-- | Existential types and helpers to work with existentials 'HList's and 'HTree's
module Data.HTree.Existential
( -- * existential data types
Some (..)
, Some2 (..)
-- * existential type synonyms
, ETree
, EList
-- * working with existential HTrees/HLists functions
, with
, with2
, withSomeHTree
, withSomeHList
, hcFoldEHList
, hcFoldMapEHTree
-- * useful functors to work with existential type-level structures
, Has (..)
, HasTypeable
, HasIs
-- ** working with 'Has'
, withProves
, prodHas
, flipHas
)
where
import Data.HTree.Constraint (Has (Proves), HasIs, HasTypeable, proves)
import Data.HTree.Families (Both)
import Data.HTree.List (HList (HCons, HNil))
import Data.HTree.Tree (HTree (HNode))
import Data.Kind (Type)
import Type.Reflection (SomeTypeRep (SomeTypeRep), Typeable, eqTypeRep, typeOf, (:~~:) (HRefl))
-- | a Some type that takes an arity one type constructor, this is for completeness
type Some :: forall l. (l -> Type) -> Type
data Some g where
MkSome :: g k -> Some g
-- | take some existentai arity one type constructor and a function that takes the
-- non-existential one and returns some @r@ and return an @r@
with :: forall {l} (g :: l -> Type) r. Some g -> (forall m. g m -> r) -> r
with (MkSome a) f = f a
-- | a Some type that take an arity two type constructor, this is necessary
-- so that we avoid using composition on the type level or having visible
-- parameters to the type synonyms
type Some2 :: forall k l. (k -> l -> Type) -> k -> Type
data Some2 g f where
MkSome2 :: g f k -> Some2 g f
-- | take some existential arity two type constructor and a function that takes the
-- non-existential one and returns some @r@ and return an @r@
with2
:: forall {k} {l} (g :: k -> l -> Type) (f :: k) r
. Some2 g f
-> (forall m. g f m -> r)
-> r
with2 (MkSome2 a) f = f a
-- | HTree but the type level tree is existential
type ETree :: forall k. (k -> Type) -> Type
type ETree = Some2 HTree
-- | HList but the type level list is existential
type EList :: forall k. (k -> Type) -> Type
type EList = Some2 HList
-- | 'with2' specialized to 'HTree's
withSomeHTree :: ETree f -> (forall t. HTree f t -> r) -> r
withSomeHTree = with2
-- | 'with2' specialized to 'HList's
withSomeHList :: EList f -> (forall xs. HList f xs -> r) -> r
withSomeHList = with2
-- | fold over existential hlists
hcFoldEHList
:: forall c f y
. (forall x. c x => f x -> y -> y)
-> y
-> EList (Has c f)
-> y
hcFoldEHList f def el = with2 el \case
HNil -> def
HCons (Proves x) xs ->
let y = hcFoldEHList f def (MkSome2 xs)
in f x y
-- | fold over existential htrees
hcFoldMapEHTree
:: forall c f y
. Semigroup y
=> (forall a. c a => f a -> y)
-> ETree (Has c f)
-> y
hcFoldMapEHTree f et = with2 et \case
HNode (Proves x) HNil -> f x
HNode x@(Proves _) (y `HCons` ys) ->
hcFoldMapEHTree f (MkSome2 y)
<> hcFoldMapEHTree f (MkSome2 (HNode x ys))
-- | destruct 'Some', destruct 'Has'
withProves :: Some (Has c f) -> (forall a. c a => f a -> r) -> r
withProves x k = with x (`proves` k)
-- | condens the 'Has' constraints in an existential
prodHas :: forall c1 c2 f. Some (Has c1 (Has c2 f)) -> Some (Has (Both c1 c2) f)
prodHas x = withProves x \pc2 -> proves pc2 (MkSome . Proves)
-- | flip the constraints in an existential
flipHas :: forall c1 c2 f. Some (Has c1 (Has c2 f)) -> Some (Has c2 (Has c1 f))
flipHas x = withProves (prodHas x) (MkSome . Proves . Proves)
deriving stock instance (forall k. Show (g f k)) => Show (Some2 g f)
deriving stock instance (forall k. Show (g k)) => Show (Some g)
instance {-# OVERLAPPING #-} Typeable f => Show (Some (Has Typeable f)) where
show (MkSome (Proves f)) =
"(MkSome (Proves @Typeable " <> show (typeOf f) <> "))"
instance
(forall x. Eq x => Eq (f x), Typeable f)
=> Eq (ETree (Has (Both Typeable Eq) f))
where
ex == ey =
with2 ex \x -> with2 ey \y ->
case (x, y) of
(HNode (Proves m) ms, HNode (Proves n) ns) ->
case eqTypeRep (typeOf m) (typeOf n) of
Nothing -> False
Just HRefl ->
let go
:: forall xs ys
. HList (HTree (Has (Both Typeable Eq) f)) xs
-> HList (HTree (Has (Both Typeable Eq) f)) ys
-> Bool
go HNil HNil = True
go (m' `HCons` ms') (n' `HCons` ns') =
MkSome2 m' == MkSome2 n' && go ms' ns'
go _ _ = False
in m == n && go ms ns
instance
(forall x. Eq x => Eq (f x), Typeable f)
=> Eq (EList (Has (Both Typeable Eq) f))
where
ex == ey =
with2 ex \x -> with2 ey \y ->
case (x, y) of
(HNil, HNil) -> True
(HCons (Proves x') xs', HCons (Proves y') ys') ->
case eqTypeRep (typeOf x') (typeOf y') of
Nothing -> False
Just HRefl -> x' == y' && MkSome2 xs' == MkSome2 ys'
(_, _) -> False
instance Eq (f k) => Eq (EList (HasIs k f)) where
ex == ey =
with2 ex \x -> with2 ey \y ->
case (x, y) of
(HNil, HNil) -> True
(HCons (Proves x') xs', HCons (Proves y') ys') ->
(x' == y') && MkSome2 xs' == MkSome2 ys'
(_, _) -> False
instance
( forall x. Eq x => Eq (f x)
, Typeable f
)
=> Eq (Some (Has Typeable (Has Eq f)))
where
MkSome (Proves (Proves x1)) == MkSome (Proves (Proves x2)) =
case eqTypeRep (typeOf x1) (typeOf x2) of
Just HRefl -> x1 == x2
Nothing -> False
instance
( forall x. Ord x => Ord (f x)
, Typeable f
, Eq (Some (Has Typeable (Has Ord f)))
)
=> Ord (Some (Has Typeable (Has Ord f)))
where
MkSome (Proves (Proves x1)) `compare` MkSome (Proves (Proves x2)) =
case eqTypeRep (typeOf x1) (typeOf x2) of
Just HRefl -> x1 `compare` x2
Nothing -> SomeTypeRep (typeOf x1) `compare` SomeTypeRep (typeOf x2)