packages feed

aztecs-0.2.0.0: src/Data/Aztecs/Entity.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

module Data.Aztecs.Entity
  ( Entity (..),
    EntityT,
    FromEntity (..),
    ToEntity (..),
    ConcatT,
    IntersectT,
    Intersect (..),
    DifferenceT,
    Difference (..),
    SplitT,
    Split (..),
    concat,
    (<&>),
    (:&) (..),
  )
where

import Data.Kind (Type)
import Prelude hiding (concat)

data Entity (ts :: [Type]) where
  ENil :: Entity '[]
  ECons :: t -> Entity ts -> Entity (t ': ts)

instance Show (Entity '[]) where
  show ENil = "[]"

instance (Show a, Show' (Entity as)) => Show (Entity (a ': as)) where
  show (ECons x xs) = "[" ++ show x ++ showRow xs

class Show' a where
  showRow :: a -> String

instance Show' (Entity '[]) where
  showRow ENil = "]"

instance (Show a, Show' (Entity as)) => Show' (Entity (a ': as)) where
  showRow (ECons x xs) = ", " ++ show x ++ showRow xs

(<&>) :: Entity as -> a -> Entity (a : as)
(<&>) es c = ECons c es

type family ConcatT (a :: [Type]) (b :: [Type]) where
  ConcatT '[] b = b
  ConcatT (a ': as) b = a ': ConcatT as b

concat :: Entity as -> Entity bs -> Entity (ConcatT as bs)
concat ENil ys = ys
concat (ECons x xs) ys = ECons x (concat xs ys)

type family SplitT (a :: [Type]) (b :: [Type]) :: [Type] where
  SplitT '[] bs = bs
  SplitT (a ': as) (a ': bs) = SplitT as bs

class Split (a :: [Type]) (b :: [Type]) where
  split :: Entity b -> (Entity a, Entity (SplitT a b))

instance Split '[] bs where
  split e = (ENil, e)

instance forall a as bs. (Split as bs) => Split (a ': as) (a ': bs) where
  split (ECons x xs) =
    let (as, bs) = split @as xs
     in (ECons x as, bs)

data a :& b = a :& b

type family EntityT a where
  EntityT (a :& b) = a ': EntityT b
  EntityT (Entity ts) = ts
  EntityT a = '[a]

class FromEntity a where
  fromEntity :: Entity (EntityT a) -> a

instance {-# OVERLAPS #-} (EntityT a ~ '[a]) => FromEntity a where
  fromEntity (ECons a ENil) = a

instance FromEntity (Entity ts) where
  fromEntity = id

instance (FromEntity a, FromEntity b, EntityT (a :& b) ~ (a ': EntityT b)) => FromEntity (a :& b) where
  fromEntity (ECons a rest) = a :& fromEntity rest

class ToEntity a where
  toEntity :: a -> Entity (EntityT a)

instance {-# OVERLAPS #-} (EntityT a ~ '[a]) => ToEntity a where
  toEntity a = ECons a ENil

instance ToEntity (Entity ts) where
  toEntity = id

instance (ToEntity a, ToEntity b, EntityT (a :& b) ~ (a ': EntityT b)) => ToEntity (a :& b) where
  toEntity (a :& b) = ECons a (toEntity b)

type family ElemT (a :: Type) (b :: [Type]) :: Bool where
  ElemT a '[] = 'False
  ElemT a (a ': as) = 'True
  ElemT a (b ': as) = ElemT a as

type family If (cond :: Bool) (true :: [Type]) (false :: [Type]) :: [Type] where
  If 'True true false = true
  If 'False true false = false

type family IntersectT (a :: [Type]) (b :: [Type]) :: [Type] where
  IntersectT '[] b = '[]
  IntersectT (a ': as) b = If (ElemT a b) (a ': IntersectT as b) (IntersectT as b)

class Intersect' (flag :: Bool) (a :: [Type]) (b :: [Type]) where
  intersect' :: Entity a -> Entity b -> Entity (IntersectT a b)

instance (Intersect as b, ElemT a b ~ 'True) => Intersect' 'True (a ': as) b where
  intersect' (ECons x xs) ys = ECons x (xs `intersect` ys)

instance (Intersect as (b ': bs), ElemT a (b ': bs) ~ 'False) => Intersect' 'False (a ': as) (b ': bs) where
  intersect' (ECons _ xs) = intersect xs

class Intersect (a :: [Type]) (b :: [Type]) where
  intersect :: Entity a -> Entity b -> Entity (IntersectT a b)

instance Intersect '[] b where
  intersect _ _ = ENil

instance (Intersect' (ElemT a bs) (a ': as) bs) => Intersect (a ': as) bs where
  intersect = intersect' @(ElemT a bs)

type family DifferenceT (a :: [Type]) (b :: [Type]) :: [Type] where
  DifferenceT '[] b = '[]
  DifferenceT (a ': as) b = If (ElemT a b) (DifferenceT as b) (a ': DifferenceT as b)

class Difference' (flag :: Bool) (a :: [Type]) (b :: [Type]) where
  difference' :: Entity a -> Entity b -> Entity (DifferenceT a b)

instance (Difference as b, DifferenceT (a ': as) b ~ DifferenceT as b) => Difference' 'True (a ': as) b where
  difference' (ECons _ xs) ys = xs `difference` ys

instance (Difference as (b ': bs), DifferenceT (a ': as) (b ': bs) ~ (a ': DifferenceT as (b ': bs))) => Difference' 'False (a ': as) (b ': bs) where
  difference' (ECons x xs) ys = ECons x (xs `difference` ys)

class Difference (a :: [Type]) (b :: [Type]) where
  difference :: Entity a -> Entity b -> Entity (DifferenceT a b)

instance Difference '[] b where
  difference _ _ = ENil

instance (Difference' (ElemT a bs) (a ': as) bs) => Difference (a ': as) bs where
  difference = difference' @(ElemT a bs)