on main help sign in
[#17] Add more map-like functions (#19)

* [#17] Add more map-like functions * Fix review comments

commitcbcbb076fe020a8e0748712fa8a2cad378c15831 authorDmitrii Kovanikov <kovanikov@gmail.com> dateMon 9 Jul 2018, 16:47 (8 years ago) parent75b6a57
5 files changed+103−35
README.md+9−1 internal/Data/TypeRep/CacheMap.hs+67−19 src/Data/TypeRep/Map.hs+3 test/Test/TypeRep/MapProperty.hs+11−3 typerep-map.cabal+13−12
README.md+9−1
@@ -9,7 +9,7 @@
99`typerep-map` introduces `TypeRepMap` — data structure like [`Map`](http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#t:Map), but where types serve as keys, and values have the types specified in the corresponding key spots.
1010
1111```haskell
12−ghci> let typeRepMap = insert (Identity True) $ insert (Identity (42 :: Int)) empty
12+ghci> let typeRepMap = insert (Identity True) $ one (Identity (42 :: Int))
1313
1414ghci> size typeRepMap
15152
@@ -27,4 +27,12 @@ Nothing
2727
2828ghci> lookup (insert (Identity "hello") typeRepMap) :: Maybe (Identity String)
2929Just (Identity "hello")
30+
31+ghci> member @Int typeRepMap
32+True
33+
34+ghci> let trMap = delete @Int typeRepMap
35+
36+ghci> member @Int trMap
37+False
3038```
internal/Data/TypeRep/CacheMap.hs+67−19
@@ -1,10 +1,11 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE KindSignatures #-}
5−{-# LANGUAGE MagicHash #-}
6−{-# LANGUAGE PolyKinds #-}
7−{-# LANGUAGE TypeFamilies #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MagicHash #-}
7+{-# LANGUAGE TypeInType #-}
8+{-# LANGUAGE TypeFamilies #-}
89
910-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
1011
@@ -14,8 +15,11 @@ module Data.TypeRep.CacheMap
1415
1516 -- 'TypeRepMap' interface
1617 , empty
18+ , one
1719 , insert
20+ , delete
1821 , lookup
22+ , member
1923 , size
2024
2125 -- * Helpful testing functions
@@ -34,7 +38,6 @@ import Data.List (nubBy)
3438import Data.Maybe (fromJust)
3539import Data.Proxy (Proxy (..))
3640import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
37−import Data.Word (Word64)
3841import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
3942import GHC.Exts (inline, sortWith)
4043import GHC.Fingerprint (Fingerprint (..))
@@ -57,7 +60,7 @@ data TypeRepMap (f :: k -> Type) = TypeRepMap
5760instance Show (TypeRepMap f) where
5861 show = show . toFps
5962
60−-- | Returnes the list of 'Fingerprint's from 'TypeRepMap'.
63+-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
6164toFps :: TypeRepMap f -> [Fingerprint]
6265toFps TypeRepMap{..} = zipWith Fingerprint
6366 (Unboxed.toList fingerprintAs)
@@ -70,20 +73,52 @@ fromAny = unsafeCoerce
7073empty :: TypeRepMap f
7174empty = TypeRepMap mempty mempty mempty
7275
76+-- | Construct a 'TypeRepMap' with a single element.
77+one :: forall a f . Typeable a => f a -> TypeRepMap f
78+one x = insert x empty
79+{-# INLINE one #-}
80+
7381-- | Inserts the value with its type as a key.
7482insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
7583insert x = fromListPairs . addX . toPairList
7684 where
77− toPairList :: TypeRepMap f -> [(Fingerprint, Any)]
78− toPairList trMap = zip (toFps trMap) (V.toList $ anys trMap)
79−
8085 pairX :: (Fingerprint, Any)
8186 pairX@(fpX, _) = (calcFp x, unsafeCoerce x)
8287
8388 addX :: [(Fingerprint, Any)] -> [(Fingerprint, Any)]
84− addX l = pairX : filter ((/= fpX) . fst) l
89+ addX l = pairX : deleteByFst fpX l
8590{-# INLINE insert #-}
8691
92+-- Helper type to workaround some issues with types
93+type KindOf (a :: k) = k
94+
95+{- | Deletes value from list.
96+
97+>>> let trMap = delete @Bool $ insert (Identity True) $ one (Identity 'a')
98+>>> size trMap
99+1
100+>>> member @Bool trMap
101+False
102+>>> member @Char trMap
103+True
104+-}
105+delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
106+delete = fromListPairs . deleteByFst (typeFp @a) . toPairList
107+{-# INLINE delete #-}
108+
109+{- | Returns 'True' if there exist value of given type.
110+
111+>>> member @Char $ one (Identity 'a')
112+True
113+>>> member @Bool $ one (Identity 'a')
114+False
115+-}
116+member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
117+member trMap = case lookup @a trMap of
118+ Nothing -> False
119+ Just _ -> True
120+{-# INLINE member #-}
121+
87122{- | Looks up the value at the type.
88123
89124>>> let x = lookup $ insert (Identity (11 :: Int)) empty
@@ -91,11 +126,10 @@ insert x = fromListPairs . addX . toPairList
91126Just (Identity 11)
92127>>> x :: Maybe (Identity ())
93128Nothing
94−
95129-}
96130lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
97131lookup tVect = fromAny . (anys tVect V.!)
98− <$> cachedBinarySearch (typeRepFingerprint $ typeRep $ Proxy @a)
132+ <$> cachedBinarySearch (typeFp @a)
99133 (fingerprintAs tVect)
100134 (fingerprintBs tVect)
101135{-# INLINE lookup #-}
@@ -128,6 +162,23 @@ cachedBinarySearch (Fingerprint (W64# a) (W64# b)) fpAs fpBs = inline (go 0#)
128162 len = let !(I# l) = Unboxed.length fpAs in l
129163{-# INLINE cachedBinarySearch #-}
130164
165+----------------------------------------------------------------------------
166+-- Internal functions
167+----------------------------------------------------------------------------
168+
169+typeFp :: forall a . Typeable a => Fingerprint
170+typeFp = typeRepFingerprint $ typeRep $ Proxy @a
171+{-# INLINE typeFp #-}
172+
173+toPairList :: TypeRepMap f -> [(Fingerprint, Any)]
174+toPairList trMap = zip (toFps trMap) (V.toList $ anys trMap)
175+
176+deleteByFst :: Eq a => a -> [(a, b)] -> [(a, b)]
177+deleteByFst x = filter ((/= x) . fst)
178+
179+nubByFst :: (Eq a) => [(a, b)] -> [(a, b)]
180+nubByFst = nubBy ((==) `on` fst)
181+
131182----------------------------------------------------------------------------
132183-- Functions for testing and benchmarking
133184----------------------------------------------------------------------------
@@ -165,10 +216,7 @@ fromListPairs :: [(Fingerprint, Any)] -> TypeRepMap f
165216fromListPairs kvs = TypeRepMap (Unboxed.fromList fpAs) (Unboxed.fromList fpBs) (V.fromList ans)
166217 where
167218 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
168− (fps, ans) = unzip $ fromSortedList $ sortWith fst $ nubPairs kvs
169−
170−nubPairs :: (Eq a) => [(a, b)] -> [(a, b)]
171−nubPairs = nubBy ((==) `on` fst)
219+ (fps, ans) = unzip $ fromSortedList $ sortWith fst $ nubByFst kvs
172220
173221----------------------------------------------------------------------------
174222-- Tree-like conversion
src/Data/TypeRep/Map.hs+3
@@ -6,8 +6,11 @@ module Data.TypeRep.Map
66
77 -- 'TypeRepMap' interface
88 , empty
9+ , one
910 , insert
11+ , delete
1012 , lookup
13+ , member
1114 , size
1215
1316 -- * Helpful testing functions
test/Test/TypeRep/MapProperty.hs+11−3
@@ -15,7 +15,7 @@ import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
1515import Test.Tasty (TestName, TestTree)
1616import Test.Tasty.Hedgehog (testProperty)
1717
18−import Data.TypeRep.CacheMap (TF (..), TypeRepMap, fromList, insert, lookup)
18+import Data.TypeRep.CacheMap (TF (..), TypeRepMap, delete, fromList, insert, lookup, member)
1919
2020import qualified Hedgehog.Gen as Gen
2121import qualified Hedgehog.Range as Range
@@ -39,6 +39,16 @@ test_InsertInsert = prop "insert k b . insert k a == insert k b" $ do
3939 let b = IntProxy proxy (i + 1)
4040 lookup @n @IntProxy (insert b $ insert a m) === Just b
4141
42+test_DeleteMember :: PropertyTest
43+test_DeleteMember = prop "member k . delete k == False" $ do
44+ m <- forAll genMap
45+ TF (proxy :: IntProxy n) <- forAll genTF
46+ shouldInsert <- forAll Gen.bool
47+
48+ if shouldInsert then
49+ member @n (delete @n $ insert proxy m) === False
50+ else
51+ member @n (delete @n m) === False
4252
4353----------------------------------------------------------------------------
4454-- Generators
@@ -47,11 +57,9 @@ test_InsertInsert = prop "insert k b . insert k a == insert k b" $ do
4757data IntProxy (n :: Nat) = IntProxy (Proxy n) Int
4858 deriving (Show, Eq)
4959
50−
5160genMap :: MonadGen m => m (TypeRepMap IntProxy)
5261genMap = fromList <$> Gen.list (Range.linear 0 1000) genTF
5362
54−
5563genTF :: MonadGen m => m (TF IntProxy)
5664genTF = do
5765 randNat :: Integer <- Gen.integral (Range.linear 0 10000)
typerep-map.cabal+13−12
@@ -1,14 +1,15 @@
11name: typerep-map
2−version: 0.1.0.0
3−description: Efficient implementation of data type with types as keys
4−homepage: https://github.com/vrom911/typerep-map
5−bug-reports: https://github.com/vrom911/typerep-map/issues
2+version: 1.0
3+synopsis: Efficient implementation of data type with types as keys
4+description: See README.md for more details.
5+homepage: https://github.com/kowainik/typerep-map
6+bug-reports: https://github.com/kowainik/typerep-map/issues
67license: MIT
78license-file: LICENSE
8−author: Veronika Romashkina
9−maintainer: vrom911@gmail.com
10−copyright: 2017 Veronika Romashkina
11−category: Data Structures
9+author: Kowainik, Vladislav Zavialov
10+maintainer: xrom.xkov@gmail.com
11+copyright: 2017-2018 Kowainik
12+category: Data, Data Structures, Types
1213build-type: Simple
1314extra-source-files: README.md
1415cabal-version: 2.0
@@ -16,6 +17,10 @@ tested-with: GHC == 8.0.2
1617 , GHC == 8.2.2
1718 , GHC == 8.4.3
1819
20+source-repository head
21+ type: git
22+ location: https://github.com/kowainik/typerep-map.git
23+
1924library
2025 hs-source-dirs: src
2126 exposed-modules: Data.TypeRep.Map
@@ -94,7 +99,3 @@ benchmark typerep-map-benchmark
9499 TypeApplications
95100 if impl(ghc >= 8.2.2)
96101 other-modules: DMap
97−
98−source-repository head
99− type: git
100− location: https://github.com/vrom911/typerep-map.git