@@ -12,33 +12,13 @@
13-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
13-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
15−-- | This module introduces the 'TypeRepMap' data structure and functions to work with it.
15+-- | Internal API for 'TypeRepMap' and operations on it. The functions here do
16−
16+-- not have any stability guarantees and can change between minor versions.
17−module Data.TypeRep.Map
17+--
18− ( -- * Map type
18+-- If you need to use this module for purposes other than tests,
19− TypeRepMap (..)
21+module Data.TypeRepMap.Internal where
37− -- * Helpful testing functions
43import Prelude hiding (lookup)
23import Prelude hiding (lookup)
@@ -64,12 +44,34 @@ import qualified Data.IntMap.Strict as IM
64import qualified Data.Map.Strict as Map
44import qualified Data.Map.Strict as Map
65import qualified GHC.Exts as GHC (fromList, toList)
45import qualified GHC.Exts as GHC (fromList, toList)
67−-- | Map-like data structure that keeps types as keys.
47+{- |
49+'TypeRepMap' is a heterogeneous data structure similar in its essence to
50+'Data.Map.Map' with types as keys, where each value has the type of its key. In
51+addition to that, each value is wrapped in an interpretation @f@.
53+Here is an example of using 'Prelude.Maybe' as an interpretation, with a
54+comparison to 'Data.Map.Map':
57+ 'Data.Map.Map' 'Prelude.String' ('Prelude.Maybe' 'Prelude.String') 'TypeRepMap' 'Prelude.Maybe'
58+--------------------------- ---------------------
59+ \"Int\" -> Just \"5\" 'Prelude.Int' -> Just 5
60+ \"Bool\" -> Just \"True\" 'Prelude.Bool' -> Just 'Prelude.True'
61+ \"Char\" -> Nothing 'Prelude.Char' -> Nothing
64+The runtime representation of 'TypeRepMap' is an array, not a tree. This makes
65+'lookup' significantly more efficient.
68−data TypeRepMap (f :: k -> Type) = TypeRepMap
68+data TypeRepMap (f :: k -> Type) =
69− { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64)
70+ { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ first components of key fingerprints
70− , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64)
71+ , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ second components of key fingerprints
71− , anys :: {-# UNPACK #-} !(Array Any)
72+ , anys :: {-# UNPACK #-} !(Array Any) -- ^ values stored in the map
74+ -- ^ an unsafe constructor for 'TypeRepMap'
74-- | Shows only 'Fingerprint's.
76-- | Shows only 'Fingerprint's.
75instance Show (TypeRepMap f) where
77instance Show (TypeRepMap f) where
@@ -92,17 +94,38 @@ toFingerprints :: TypeRepMap f -> [Fingerprint]
92toFingerprints TypeRepMap{..} =
94toFingerprints TypeRepMap{..} =
93 zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
95 zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
95−-- | Empty structure.
97+{- |
99+A 'TypeRepMap' with no values stored in it.
101+prop> size empty == 0
102+prop> member @a empty == False
96empty :: TypeRepMap f
105empty :: TypeRepMap f
97empty = mempty
106empty = mempty
98{-# INLINE empty #-}
107{-# INLINE empty #-}
100−-- | Construct a 'TypeRepMap' with a single element.
111+Construct a 'TypeRepMap' with a single element.
113+prop> size (one x) == 1
114+prop> member @a (one (x :: f a)) == True
101one :: forall a f . Typeable a => f a -> TypeRepMap f
117one :: forall a f . Typeable a => f a -> TypeRepMap f
102one x = insert x empty
118one x = insert x empty
103{-# INLINE one #-}
119{-# INLINE one #-}
105−-- | Inserts the value with its type as a key.
121+{- |
123+Insert a value into a 'TypeRepMap'.
125+prop> size (insert v tm) >= size tm
126+prop> member @a (insert (x :: f a) tm) == True
106insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
129insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
107insert x = fromListPairs . addX . toPairList
130insert x = fromListPairs . addX . toPairList
@@ -113,41 +136,45 @@ insert x = fromListPairs . addX . toPairList
113 addX l = pairX : deleteByFst fpX l
136 addX l = pairX : deleteByFst fpX l
114{-# INLINE insert #-}
137{-# INLINE insert #-}
116−-- Helper type to workaround some issues with types
139+-- Extract the kind of a type. We use it to work around lack of syntax for
140+-- inferred type variables (which are not subject to type applications).
117type KindOf (a :: k) = k
141type KindOf (a :: k) = k
119−{- | Deletes value from list.
143+{- | Delete a value from a 'TypeRepMap'.
145+prop> size (delete @a tm) <= size tm
146+prop> member @a (delete @a tm) == False
121−>>> let trMap = delete @Bool $ insert (Identity True) $ one (Identity 'a')
148+>>> tm = delete @Bool $ insert (Just True) $ one (Just 'a')
122−>>> size trMap
149+>>> size tm
124−>>> member @Bool trMap
151+>>> member @Bool tm
126−>>> member @Char trMap
153+>>> member @Char tm
129delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
156delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
130delete = fromListPairs . deleteByFst (typeFp @a) . toPairList
157delete = fromListPairs . deleteByFst (typeFp @a) . toPairList
131{-# INLINE delete #-}
158{-# INLINE delete #-}
133−{- | Map over elements.
160+{- | Map over the elements of a 'TypeRepMap'.
135−>>> let trMap = insert (Identity True) $ one (Identity 'a')
162+>>> tm = insert (Identity True) $ one (Identity 'a')
136−>>> lookup @Bool trMap
163+>>> lookup @Bool tm
137Just (Identity True)
164Just (Identity True)
138−>>> lookup @Char trMap
165+>>> lookup @Char tm
139Just (Identity 'a')
166Just (Identity 'a')
140−>>> let trMap2 = hoist ((:[]) . runIdentity) trMap
167+>>> tm2 = hoist ((:[]) . runIdentity) tm
141−>>> lookup @Bool trMap2
168+>>> lookup @Bool tm2
142Just [True]
169Just [True]
143−>>> lookup @Char trMap2
170+>>> lookup @Char tm2
146hoist :: (forall x. f x -> g x) -> TypeRepMap f -> TypeRepMap g
173hoist :: (forall x. f x -> g x) -> TypeRepMap f -> TypeRepMap g
147hoist f (TypeRepMap as bs ans) = TypeRepMap as bs $ mapArray' (toAny . f . fromAny) ans
174hoist f (TypeRepMap as bs ans) = TypeRepMap as bs $ mapArray' (toAny . f . fromAny) ans
148{-# INLINE hoist #-}
175{-# INLINE hoist #-}
150−-- | The union with a combining function.
177+-- | The union of two 'TypeRepMap's using a combining function.
151unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
178unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
152unionWith f m1 m2 = fromListPairs
179unionWith f m1 m2 = fromListPairs
153 $ Map.toList
180 $ Map.toList
@@ -159,13 +186,13 @@ unionWith f m1 m2 = fromListPairs
159 combine a b = toAny $ f (fromAny a) (fromAny b)
186 combine a b = toAny $ f (fromAny a) (fromAny b)
160{-# INLINE unionWith #-}
187{-# INLINE unionWith #-}
162−-- | The (left-biased) union of two maps. It prefers the first map when
189+-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
163-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
190-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
164union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
191union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
165union = unionWith const
192union = unionWith const
166{-# INLINE union #-}
193{-# INLINE union #-}
168−{- | Returns 'True' if there exist value of given type.
195+{- | Check if a value of the given type is present in a 'TypeRepMap'.
170>>> member @Char $ one (Identity 'a')
197>>> member @Char $ one (Identity 'a')
@@ -173,14 +200,14 @@ True
175member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
202member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
176−member trMap = case lookup @a trMap of
203+member tm = case lookup @a tm of
177 Nothing -> False
204 Nothing -> False
178 Just _ -> True
205 Just _ -> True
179{-# INLINE member #-}
206{-# INLINE member #-}
181−{- | Looks up the value at the type.
208+{- | Lookup a value of the given type in a 'TypeRepMap'.
183−>>> let x = lookup $ insert (Identity (11 :: Int)) empty
210+>>> x = lookup $ insert (Identity (11 :: Int)) empty
184>>> x :: Maybe (Identity Int)
211>>> x :: Maybe (Identity Int)
185Just (Identity 11)
212Just (Identity 11)
186>>> x :: Maybe (Identity ())
213>>> x :: Maybe (Identity ())
@@ -193,7 +220,7 @@ lookup tVect = fromAny . (anys tVect `indexArray`)
193 (fingerprintBs tVect)
220 (fingerprintBs tVect)
194{-# INLINE lookup #-}
221{-# INLINE lookup #-}
196−-- | Returns the size of the 'TypeRepMap'.
223+-- | Get the amount of elements in a 'TypeRepMap'.
197size :: TypeRepMap f -> Int
224size :: TypeRepMap f -> Int
198size = sizeofPrimArray . fingerprintAs
225size = sizeofPrimArray . fingerprintAs
199{-# INLINE size #-}
226{-# INLINE size #-}
@@ -236,7 +263,7 @@ typeFp = typeRepFingerprint $ typeRep $ Proxy @a
236{-# INLINE typeFp #-}
263{-# INLINE typeFp #-}
238toPairList :: TypeRepMap f -> [(Fingerprint, Any)]
265toPairList :: TypeRepMap f -> [(Fingerprint, Any)]
239−toPairList trMap = zip (toFingerprints trMap) (GHC.toList $ anys trMap)
266+toPairList tm = zip (toFingerprints tm) (GHC.toList $ anys tm)
241deleteByFst :: Eq a => a -> [(a, b)] -> [(a, b)]
268deleteByFst :: Eq a => a -> [(a, b)] -> [(a, b)]
242deleteByFst x = filter ((/= x) . fst)
269deleteByFst x = filter ((/= x) . fst)