{-# LANGUAGE Safe #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE PolyKinds #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE StandaloneDeriving #-} {-| Module : Data.PolyMap Copyright : (c) 2015 David Farrell License : PublicDomain Stability : unstable Portability : non-portable (GHC extensions) Polygonal maps for Haskell with flexible storage. -} module Data.PolyMap ( module Data.PolyMap.Relation , module Data.PolyMap.Storage.Set -- * PolyMap Type , PolyMap , SimplePolyMap -- * Query , Data.PolyMap.null , size , member , notMember , Data.PolyMap.lookup -- * Construction , empty , singleton -- * Insertion , insert -- * Indexed , lookupIndex , lookupRelation ) where import Data.PolyMap.Nat import Data.PolyMap.Relation import Data.PolyMap.Storage (Storage) import qualified Data.PolyMap.Storage as S import Data.PolyMap.Storage.List() import Data.PolyMap.Storage.Set type family HasType a (as :: [*]) :: Bool where HasType a '[] = 'False HasType a (a ': as) = 'True HasType a (b ': as) = HasType a as type family TypeAt (n :: Nat) (as :: [*]) where TypeAt 'Z (a ': as) = a TypeAt ('S n) (a ': as) = TypeAt n as type family MapStorage (f :: kf) (as :: [k0]) :: [(k0, kf)] where MapStorage f '[] = '[] MapStorage f (a ': as) = '(a, f) ': MapStorage f as type family MapFst (as :: [(k0, k1)]) :: [k0] where MapFst '[] = '[] MapFst ('(a, b) ': as) = a ': MapFst as -- |A polymap whose sides are defined by a list of types zipped with storage types. data family PolyMap (as :: [(*, * -> *)]) data instance PolyMap '[] = UnitPolyMap data instance PolyMap ('(a, s) ': as) = s a :<=>: PolyMap as -- |A simple polymap whose sides are defined by a list of types and a single storage type. type SimplePolyMap (as :: [*]) (s :: * -> *) = PolyMap (MapStorage s as) infixr 4 :<=>: deriving instance Show (PolyMap '[]) deriving instance (Show a, Show (s a), Show (PolyMap as)) => Show (PolyMap ('(a, s) ': as)) class PolyMapClass (as :: [(*, * -> *)]) where -- |Is the polymap empty? null :: PolyMap as -> Bool -- |The number of relations in the polymap. size :: PolyMap as -> Int -- |The empty polymap. empty :: PolyMap as -- |Retrieve a relation by its /index/, i.e. by the zero-based index of the -- storage of each of its sides. The index is a number from /0/ up to, but -- not including, the 'size' of the polymap. lookupRelation :: Int -> PolyMap as -> Maybe (Relation (MapFst as)) singleton' :: Relation (MapFst as) -> PolyMap as insert' :: Relation (MapFst as) -> PolyMap as -> PolyMap as instance PolyMapClass '[] where null UnitPolyMap = True size UnitPolyMap = 0 empty = UnitPolyMap singleton' UnitRelation = UnitPolyMap insert' UnitRelation UnitPolyMap = UnitPolyMap lookupRelation _ UnitPolyMap = Just UnitRelation instance (Storage s a, PolyMapClass as) => PolyMapClass ('(a, s) ': as) where null (xs :<=>: _) = Prelude.null xs size (xs :<=>: _) = length xs empty = mempty :<=>: empty singleton' (x :<->: xs) = S.singleton x :<=>: singleton' xs insert' (x :<->: xs) (m :<=>: ms) = mconcat [m, (S.singleton x)] :<=>: insert' xs ms lookupRelation i (m :<=>: ms) = (:<->:) <$> S.lookupElem i m <*> lookupRelation i ms class PolyMapLookup (n :: Nat) (as :: [(*, * -> *)]) where -- |Is the key a member at the specified side of the polymap. member :: Proxy n -> TypeAt n (MapFst as) -> PolyMap as -> Bool -- |Lookup the /index/ of a key, which is its zero-based index in the storage -- at the specified side of the polymap. The index is a number from /0/ up -- to, but not including, the 'size' of the polymap. lookupIndex :: Proxy n -> TypeAt n (MapFst as) -> PolyMap as -> Maybe Int instance PolyMapLookup n '[] where member Proxy _ UnitPolyMap = False lookupIndex Proxy _ UnitPolyMap = Nothing instance (Eq a, Storage s a) => PolyMapLookup 'Z ('(a, s) ': as) where member Proxy x (xs :<=>: _) = elem x xs lookupIndex Proxy x (xs :<=>: _) = S.lookupIndex x xs instance (Storage s a, PolyMapLookup n as) => PolyMapLookup ('S n) ('(a, s) ': as) where member Proxy x (_ :<=>: ms) = member (Proxy :: Proxy n) x ms lookupIndex Proxy x (_ :<=>: ms) = lookupIndex (Proxy :: Proxy n) x ms -- |Is the key not a member at the specified side of the polymap? See also 'member'. notMember :: PolyMapLookup n as => Proxy n -> TypeAt n (MapFst as) -> PolyMap as -> Bool notMember proxy x m = not (member proxy x m) -- |Lookup the value at a key at the specified side of the polymap. -- -- The function will return the corresponding value as @('Just' value)@, or -- 'Nothing' if the key isn't at the specified side of the polymap. lookup :: (PolyMapClass as, PolyMapLookup n as) => Proxy n -> TypeAt n (MapFst as) -> PolyMap as -> Maybe (Relation (MapFst as)) lookup proxy x m = case lookupIndex proxy x m of Nothing -> Nothing Just i -> lookupRelation i m -- |A polymap with a single relation. singleton :: (PolyMapClass as, ToRelation a (MapFst as)) => a -> PolyMap as singleton r = singleton' (toRelation r) -- |Insert a new relation into the polymap. insert :: (PolyMapClass as, ToRelation a (MapFst as)) => a -> PolyMap as -> PolyMap as insert r m = insert' (toRelation r) m