type-sets-0.1.0.0: src/Type/Set.hs
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Type.Set
( -- * Core type
TypeSet (..)
-- * Set operations
, Member
, Insert
, Remove
, Merge
-- * Tree operations
, Locate
, Follow
, Side (..)
) where
import Type.Compare
import GHC.TypeLits
------------------------------------------------------------------------------
-- | A binary search tree. When @-XDataKinds@ is turned on, this becomes the
-- backbone of the type-level set.
--
-- >>> type MySet = Insert Bool (Insert String (Insert (Maybe Int) 'Empty))
data TypeSet a
= Empty
| Branch a (TypeSet a) (TypeSet a)
------------------------------------------------------------------------------
-- | Either left or right down a path.
data Side = L | R
deriving (Eq, Ord, Show)
------------------------------------------------------------------------------
-- | /O(log n)/. Insert an element into the 'TypeSet'.
type family Insert (t :: k) (bst :: TypeSet k) :: TypeSet k where
Insert t 'Empty = 'Branch t 'Empty 'Empty
Insert t ('Branch a lbst rbst) =
InsertImpl (CmpType t a) t a lbst rbst
type family InsertImpl (ord :: Ordering)
(t :: k)
(a :: k)
(lbst :: TypeSet k)
(rbst :: TypeSet k) :: TypeSet k where
InsertImpl 'EQ t a lbst rbst = 'Branch a lbst rbst
InsertImpl 'LT t a lbst rbst = 'Branch a (Insert t lbst) rbst
InsertImpl 'GT t a lbst rbst = 'Branch a lbst (Insert t rbst)
------------------------------------------------------------------------------
-- | /O(log n)/. Determine membership in the 'TypeSet.'
type family Member (t :: k) (bst :: TypeSet k) :: Bool where
Member t 'Empty = 'False
Member t ('Branch a lbst rbst) = MemberImpl (CmpType t a) t lbst rbst
type family MemberImpl (ord :: Ordering)
(t :: k)
(lbst :: TypeSet k)
(rbst :: TypeSet k) :: Bool where
MemberImpl 'EQ t lbst rbst = 'True
MemberImpl 'LT t lbst rbst = Member t lbst
MemberImpl 'GT t lbst rbst = Member t rbst
------------------------------------------------------------------------------
-- | /O(m log n)/ for @Merge m n@; put your smaller set on the left side. Merge
-- two 'TypeSet's together.
type family Merge (small :: TypeSet k) (big :: TypeSet k) :: TypeSet k where
Merge Empty big = big
Merge small Empty = small
Merge ('Branch a lbst rbst) big = Merge rbst (Merge lbst (Insert a big))
------------------------------------------------------------------------------
-- | /O(log n)/. Remove an element from the 'TypeSet'.
type family Remove (t :: k) (bst :: TypeSet k) :: TypeSet k where
Remove t Empty = Empty
Remove t ('Branch a lbst rbst) = RemoveImpl (CmpType t a) t a lbst rbst
type family RemoveImpl (ord :: Ordering)
(t :: k)
(a :: k)
(lbst :: TypeSet k)
(rbst :: TypeSet k) :: TypeSet k where
RemoveImpl 'LT t a lbst rbst = 'Branch a (Remove t lbst) rbst
RemoveImpl 'EQ t a Empty rbst = rbst
RemoveImpl 'EQ t a lbst Empty = lbst
RemoveImpl 'EQ t a lbst rbst =
'Branch (RightMost lbst) (Remove (RightMost lbst) lbst) rbst
RemoveImpl 'GT t a lbst rbst = 'Branch a lbst (Remove t rbst)
------------------------------------------------------------------------------
-- | /O(log n)/. Get the right-most element in a tree. This function is stuck
-- if the tree is empty.
type family RightMost (bst :: TypeSet k) :: k where
RightMost ('Branch a lbst 'Empty) = a
RightMost ('Branch a lbst rbst) = RightMost rbst
------------------------------------------------------------------------------
-- | /O(log n)/. Compute a @['Side']@ which finds the desired element in the
-- tree. The result of this can be passed to 'Follow' in order to look up the
-- same element again later.
type family Locate (t :: k) (bst :: TypeSet k) :: [Side] where
Member t ('Branch a lbst rbst) = LocateImpl (CmpType t a) t lbst rbst
Member t 'Empty = TypeError ('Text "Unable to locate: " ':<>: 'ShowType t)
type family LocateImpl (ord :: Ordering)
(t :: k)
(lbst :: TypeSet k)
(rbst :: TypeSet k) :: [Side] where
LocateImpl 'EQ t lbst rbst = '[]
LocateImpl 'LT t lbst rbst = 'L ': Locate t lbst
LocateImpl 'GT t lbst rbst = 'R ': Locate t rbst
------------------------------------------------------------------------------
-- | /O(log n)/. Follow the result of a 'Locate' to get a particular element in
-- the tree.
type family Follow (ss :: [Side]) (bst :: TypeSet k) :: k where
Follow '[] ('Branch t _ _) = t
Follow ('L ': ss) ('Branch _ l _) = Follow ss l
Follow ('R ': ss) ('Branch _ _ r) = Follow ss r
Follow ss 'Empty = TypeError ('Text "Unable to follow: " ':<>: 'ShowType ss)