hetero-dict 0.1.0.1 → 0.1.1.0
raw patch · 8 files changed
+303/−154 lines, 8 filesdep +aesondep +textdep +unordered-containersdep −containersdep ~template-haskellPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: aeson, text, unordered-containers, vinyl
Dependencies removed: containers
Dependency ranges changed: template-haskell
API changes (from Hackage documentation)
- Data.Hetero.DynDict: class ShowDynDict (kvs :: [KV *])
- Data.Hetero.DynDict: instance (GHC.TypeLits.KnownSymbol k, Data.Typeable.Internal.Typeable v, GHC.Show.Show v, Data.Hetero.DynDict.ShowDynDict kvs) => Data.Hetero.DynDict.ShowDynDict (k 'Data.Hetero.KVList.:= v : kvs)
- Data.Hetero.DynDict: instance Data.Hetero.DynDict.ShowDynDict '[]
- Data.Hetero.DynDict: instance Data.Hetero.DynDict.ShowDynDict kvs => GHC.Show.Show (Data.Hetero.DynDict.DynDict kvs)
- Data.Hetero.DynDict: showDict :: ShowDynDict kvs => Int -> DynDict kvs -> [(String, String, TypeRep)]
+ Data.Hetero.Dict: Store :: {-# UNPACK #-} !Int -> KVList kvs -> Store kvs
+ Data.Hetero.Dict: [storeBody] :: Store kvs -> KVList kvs
+ Data.Hetero.Dict: [storeSize] :: Store kvs -> {-# UNPACK #-} !Int
+ Data.Hetero.DynDict: instance (GHC.TypeLits.KnownSymbol k, Data.Aeson.Types.Class.FromJSON v, Data.Aeson.Types.Class.FromJSON (Data.Hetero.DynDict.DynDict kvs)) => Data.Aeson.Types.Class.FromJSON (Data.Hetero.DynDict.DynDict (k 'Data.Hetero.KVList.:= v : kvs))
+ Data.Hetero.DynDict: instance (GHC.TypeLits.KnownSymbol k, Data.Aeson.Types.Class.ToJSON v, Data.Aeson.Types.Class.ToJSON (Data.Hetero.DynDict.DynDict kvs)) => Data.Aeson.Types.Class.ToJSON (Data.Hetero.DynDict.DynDict (k 'Data.Hetero.KVList.:= v : kvs))
+ Data.Hetero.DynDict: instance Data.Aeson.Types.Class.FromJSON (Data.Hetero.DynDict.DynDict '[])
+ Data.Hetero.DynDict: instance Data.Aeson.Types.Class.ToJSON (Data.Hetero.DynDict.DynDict '[])
+ Data.Hetero.DynDict: instance Data.Hetero.Dict.ShowDict kvs => GHC.Show.Show (Data.Hetero.DynDict.DynDict kvs)
+ Data.Hetero.DynDict: size :: DynDict kvs -> Int
- Data.Hetero.Dict: [Cons] :: v -> KVList kvs -> KVList ((k := v) : kvs)
+ Data.Hetero.Dict: [Cons] :: !v -> KVList kvs -> KVList ((k := v) : kvs)
- Data.Hetero.Dict: add :: (NotHasKey k kvs) => proxy k -> v -> Store kvs -> Store ((k := v) : kvs)
+ Data.Hetero.Dict: add :: (NotHasKey k kvs) => Proxy k -> v -> Store kvs -> Store ((k := v) : kvs)
- Data.Hetero.Dict: get :: InDict k v kvs => proxy k -> Dict kvs -> v
+ Data.Hetero.Dict: get :: InDict k v kvs => Proxy k -> Dict kvs -> v
- Data.Hetero.DynDict: [Cons] :: v -> KVList kvs -> KVList ((k := v) : kvs)
+ Data.Hetero.DynDict: [Cons] :: !v -> KVList kvs -> KVList ((k := v) : kvs)
- Data.Hetero.DynDict: add :: (NotHasKey k kvs) => proxy k -> v -> DynDict kvs -> DynDict ((k := v) : kvs)
+ Data.Hetero.DynDict: add :: (NotHasKey k kvs) => Proxy k -> v -> DynDict kvs -> DynDict ((k := v) : kvs)
- Data.Hetero.DynDict: get :: InDict k v kvs => proxy k -> DynDict kvs -> v
+ Data.Hetero.DynDict: get :: InDict k v kvs => Proxy k -> DynDict kvs -> v
- Data.Hetero.DynDict: modify :: (InDict k v kvs) => proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs
+ Data.Hetero.DynDict: modify :: (InDict k v kvs) => Proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs
- Data.Hetero.DynDict: set :: (InDict k v kvs) => proxy k -> v -> DynDict kvs -> DynDict kvs
+ Data.Hetero.DynDict: set :: (InDict k v kvs) => Proxy k -> v -> DynDict kvs -> DynDict kvs
- Data.Hetero.KVList: [Cons] :: v -> KVList kvs -> KVList ((k := v) : kvs)
+ Data.Hetero.KVList: [Cons] :: !v -> KVList kvs -> KVList ((k := v) : kvs)
Files
- ChangeLog.md +6/−0
- Data/Hetero/Dict.hs +10/−7
- Data/Hetero/DynDict.hs +66/−54
- Data/Hetero/KVList.hs +2/−2
- README.md +56/−75
- bench/Bench.hs +77/−12
- bench/ProxySymbolTH.hs +79/−0
- hetero-dict.cabal +7/−4
ChangeLog.md view
@@ -1,5 +1,11 @@ # Revision history for hetero-dict +## 0.1.1.0 -- 2016-05-31++* Change DynDict's implementation to linked-list.++* add From/ToJSON instance to DynDict.+ ## 0.1.0.1 -- 2016-05-31 * fix DynDict's Show instance.
Data/Hetero/Dict.hs view
@@ -28,6 +28,9 @@ -- 3. Following access will be a simple O(1) array indexing, -- with index computed at compile time so you can't get missing keys. --+-- In theory, it's faster than linked-list based data structures when n is large,+-- but it needs more benchmark to be sure.+-- -- Typical usage: a heterogeneous lookup table, indexed by type level string. -- -- @@@ -42,7 +45,7 @@ module Data.Hetero.Dict ( -- ** Store- Store+ Store(..) , emptyStore , add -- ** Dict@@ -87,7 +90,7 @@ -- emptyStore :: Store '[] emptyStore = Store 0 Empty-{-# INLINABLE emptyStore #-}+{-# INLINE emptyStore #-} -- | O(1) add key value pair to 'Store'.@@ -100,9 +103,9 @@ -- Store {bar = "baz" :: [Char], foo = 12 :: Int} -- @ ---add :: (NotHasKey k kvs) => proxy k -> v -> Store kvs -> Store (k ':= v ': kvs)+add :: (NotHasKey k kvs) => Proxy k -> v -> Store kvs -> Store (k ':= v ': kvs) add _ v (Store l c) = Store (l + 1) (Cons v c)-{-# INLINABLE add #-}+{-# INLINE add #-} -------------------------------------------------------------------------------- @@ -135,14 +138,14 @@ mkDict store = runST $ mkDict' store {-# INLINABLE mkDict #-} -getImpl :: forall i proxy k kvs v. ('Index i ~ Ix k kvs, KnownNat i) => proxy (k :: Symbol) -> Dict kvs -> v+getImpl :: forall i k kvs v. ('Index i ~ Ix k kvs, KnownNat i) => Proxy (k :: Symbol) -> Dict kvs -> v getImpl _ (Dict d) = unsafeCoerce $ d `P.indexArray` fromIntegral (natVal (Proxy :: Proxy i)) {-# INLINABLE getImpl #-} -- | Constraint ensure 'Dict' must contain k-v pair. -- class InDict (k :: Symbol) (v :: *) (kvs :: [KV *]) | k kvs -> v where- get' :: proxy k -> Dict kvs -> v+ get' :: Proxy k -> Dict kvs -> v #if __GLASGOW_HASKELL__ >= 710 instance {-# OVERLAPPING #-} InDict k v (k ':= v ': kvs) where@@ -158,7 +161,7 @@ -- | O(1) get value using associated key from 'Dict'. ---get :: InDict k v kvs => proxy k -> Dict kvs -> v+get :: InDict k v kvs => Proxy k -> Dict kvs -> v get = get' {-# INLINE get #-}
Data/Hetero/DynDict.hs view
@@ -15,10 +15,13 @@ {-# LANGUAGE OverlappingInstances #-} #endif --- | Fast persistent heterogeneous sequence.+-- | Fast persistent heterogeneous list. ----- This module define 'DynDict', which use 'S.Seq' as underline data structure,--- so all operations(add, get, modify, set)'s time complexity are similar.+-- This module define 'DynDict', which wrap a 'KVList' linked-list,+-- benchmark showed that it's faster than previouse @Data.Sequence@ version,+-- since usually the element number is small(<20).+-- so even operations(add, get, modify, set)'s time complexity+-- are not as good as 'Seq', it's constantly faster in practice. -- -- Typical usage: a heterogeneous state store, indexed by type level string. --@@ -44,107 +47,116 @@ , get , modify , set+ , size -- ** re-export from KVList , key , KV(..) , KVList(..) , NotHasKey , Ix- -- ** Internal helpers- , ShowDynDict(..) ) where import Data.Hetero.KVList+import Data.Hetero.Dict (Store(..), ShowDict(..), mkDict) import Data.List (intercalate)-import Data.Proxy-import qualified Data.Sequence as S-import Data.Typeable (TypeRep, Typeable, typeOf)-import GHC.Exts (Any) import GHC.TypeLits-import Unsafe.Coerce+import Data.Proxy (Proxy(..))+import Data.Aeson (ToJSON(..), FromJSON(..), Value(Object))+import qualified Data.HashMap.Strict as HM+import qualified Data.Text as T -------------------------------------------------------------------------------- -- | heterogeneous persistent sequence. ----- The underline data structure is 'S.Seq'.--- support efficient 'add', 'get' and 'modify' operations.-newtype DynDict (kvs :: [KV *]) = DynDict (S.Seq Any)-+newtype DynDict (kvs :: [KV *]) = DynDict (KVList kvs) -- | A empty 'DynDict'. -- empty :: DynDict '[]-empty = DynDict S.empty-{-# INLINABLE empty #-}+empty = DynDict Empty+{-# INLINE empty #-} -- | O(1) insert new k-v pair into 'DynDict'.-add :: (NotHasKey k kvs) => proxy k -> v -> DynDict kvs -> DynDict (k ':= v ': kvs)-add _ v (DynDict d) = DynDict (unsafeCoerce v S.<| d)+add :: (NotHasKey k kvs) => Proxy k -> v -> DynDict kvs -> DynDict (k ':= v ': kvs)+add _ v (DynDict kvs) = DynDict (Cons v kvs) {-# INLINE add #-} -getImpl :: forall i proxy k kvs v. ('Index i ~ Ix k kvs, KnownNat i) => proxy (k :: Symbol) -> DynDict kvs -> v-getImpl _ (DynDict d) = unsafeCoerce $ d `S.index` fromIntegral (natVal (Proxy :: Proxy i))-{-# INLINABLE getImpl #-}--modifyImpl :: forall i proxy k kvs v. ('Index i ~ Ix k kvs, KnownNat i) => proxy (k :: Symbol) -> (v -> v) -> DynDict kvs -> DynDict kvs-modifyImpl _ f (DynDict d) = DynDict $- S.adjust (unsafeCoerce . f . unsafeCoerce) (fromIntegral (natVal (Proxy :: Proxy i))) d-{-# INLINABLE modifyImpl #-}- -- | Constraint ensure 'DynDict' must contain k-v pair. -- class InDict (k :: Symbol) (v :: *) (kvs :: [KV *]) | k kvs -> v where- get' :: proxy k -> DynDict kvs -> v- modify' :: proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs+ get' :: Proxy k -> DynDict kvs -> v+ modify' :: Proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs #if __GLASGOW_HASKELL__ >= 710 instance {-# OVERLAPPING #-} InDict k v (k ':= v ': kvs) where #else instance InDict k v (k ':= v ': kvs) where #endif- get' = getImpl+ get' _ (DynDict (Cons v _)) = v {-# INLINE get' #-}- modify' = modifyImpl+ modify' _ f (DynDict (Cons v kvs)) = DynDict $ Cons (f v) kvs {-# INLINE modify' #-} instance (InDict k v kvs, 'Index i ~ Ix k (k' ':= v' ': kvs), KnownNat i) => InDict k v (k' ':= v' ': kvs) where- get' = getImpl+ get' p (DynDict (Cons _ kvs)) = get' p (DynDict kvs) {-# INLINE get' #-}- modify' = modifyImpl+ modify' p f (DynDict (Cons v kvs)) =+ let DynDict kvs' = modify' p f (DynDict kvs)+ in DynDict (Cons v kvs') {-# INLINE modify' #-} --- | O(log(min(i,n-i))) get value using associated key.+-- | O(m) get value using associated key. ---get :: InDict k v kvs => proxy k -> DynDict kvs -> v+get :: InDict k v kvs => Proxy k -> DynDict kvs -> v get = get'-{-# INLINE get #-} --- | O(log(min(i,n-i))) modify value by associated key.-modify :: (InDict k v kvs) => proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs+-- | O(m) modify value by associated key.+modify :: (InDict k v kvs) => Proxy k -> (v -> v) -> DynDict kvs -> DynDict kvs modify = modify'-{-# INLINE modify #-} --- | O(log(min(i,n-i))) modify value by associated key.-set :: (InDict k v kvs) => proxy k -> v -> DynDict kvs -> DynDict kvs+-- | O(m) modify value by associated key.+set :: (InDict k v kvs) => Proxy k -> v -> DynDict kvs -> DynDict kvs set p v = modify' p (const v) {-# INLINE set #-} +-- | O(n) size+size :: DynDict kvs -> Int+size (DynDict Empty) = 0+size (DynDict (Cons _ kvs)) = 1 + size (DynDict kvs)+{-# INLINE size #-}+ -------------------------------------------------------------------------------- --- | Helper class for defining store's 'Show' instance.-class ShowDynDict (kvs :: [KV *]) where- showDict :: Int -> DynDict kvs -> [(String, String, TypeRep)]+instance ShowDict kvs => Show (DynDict kvs) where+ show d@(DynDict kvs) = "DynDict {" +++ (intercalate ", " . map (\(k, v, t) -> k ++ " = " ++ v ++ " :: " ++ show t) $ showDict 0 (mkDict s))+ ++ "}"+ where+ s = Store (size d) kvs -instance ShowDynDict '[] where- showDict _ _ = []+instance ToJSON (DynDict '[]) where+ toJSON _ = Object HM.empty -instance (KnownSymbol k, Typeable v, Show v, ShowDynDict kvs) => ShowDynDict (k ':= v ': kvs) where- showDict i (DynDict t) =- (symbolVal (Proxy :: Proxy k), show (unsafeCoerce $ t `S.index` i :: v), typeOf (undefined :: v)):- showDict (i + 1) (unsafeCoerce $ DynDict t :: DynDict kvs)+instance (KnownSymbol k, ToJSON v, ToJSON (DynDict kvs)) => ToJSON (DynDict (k ':= v ': kvs)) where+ toJSON (DynDict (Cons v kvs)) =+ let (Object obj) = toJSON (DynDict kvs)+ k = T.pack (symbolVal (Proxy :: Proxy k))+ obj' = HM.insert k (toJSON v) obj+ in Object obj' -instance ShowDynDict kvs => Show (DynDict kvs) where- show d = "DynDict {" ++- (intercalate ", " . map (\(k, v, t) -> k ++ " = " ++ v ++ " :: " ++ show t) $ showDict 0 d)- ++ "}"+instance FromJSON (DynDict '[]) where+ parseJSON (Object _) = return (DynDict Empty)+ parseJSON _ = fail "expect an object"++instance (KnownSymbol k, FromJSON v, FromJSON (DynDict kvs)) => FromJSON (DynDict (k ':= v ': kvs)) where+ parseJSON v@(Object obj) =+ let kString = symbolVal (Proxy :: Proxy k)+ k = T.pack kString+ in case HM.lookup k obj of+ Just v' -> do+ DynDict kvs <- parseJSON v+ v'' <- parseJSON v'+ return (DynDict (Cons v'' kvs))+ Nothing -> fail ("missing key: " ++ kString)+ parseJSON _ = fail "expect an object"
Data/Hetero/KVList.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE BangPatterns #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-}@@ -37,7 +38,7 @@ -- | A simple heterogeneous kv linked-list. -- data KVList (kvs :: [KV *]) where- Cons :: v -> KVList kvs -> KVList (k ':= v ': kvs)+ Cons :: !v -> KVList kvs -> KVList (k ':= v ': kvs) Empty :: KVList '[] @@ -86,7 +87,6 @@ Ix' i k '[] = 'NotFoundKey k Ix' i k (k ':= v ': kvs) = 'Index i Ix' i k (k' ':= v ': kvs) = Ix' (i + 1) k kvs- -- | Indexing a key at compile time. --
README.md view
@@ -8,8 +8,10 @@ 1. `Dict` which use boxed array, it's read-only with O(1) get. -1. `DynDict` which use `Seq` from `Data.Sequence`, it has O(log(min(i,n-i))) get, modify and O(1) add.+1. `DynDict` which use linked-list(see module document for details). +Features simple api, good performance and good error message, `Show` and `From/ToJSON` instances are provided.+ Example ------- @@ -44,97 +46,76 @@ Benchmark --------- -We use [hvect](http://hackage.haskell.org/package/hvect) package as a linked-list based reference.+We use [hvect](http://hackage.haskell.org/package/hvect) and [vinyl](http://hackage.haskell.org/package/vinyl) as references. ```-benchmarking n = 3/Build Dict-time 11.78 ns (11.63 ns .. 11.93 ns)- 0.999 R² (0.999 R² .. 1.000 R²)-mean 11.72 ns (11.62 ns .. 11.82 ns)-std dev 336.9 ps (282.4 ps .. 406.1 ps)-variance introduced by outliers: 48% (moderately inflated)--benchmarking n = 3/Build DynDict-time 18.10 ns (17.96 ns .. 18.24 ns)- 0.998 R² (0.995 R² .. 1.000 R²)-mean 18.42 ns (18.10 ns .. 19.84 ns)-std dev 1.676 ns (550.3 ps .. 3.725 ns)-variance introduced by outliers: 90% (severely inflated)--benchmarking n = 3/Build HVect-time 16.39 ns (15.90 ns .. 17.05 ns)- 0.994 R² (0.990 R² .. 0.998 R²)-mean 16.72 ns (16.34 ns .. 17.31 ns)-std dev 1.686 ns (1.254 ns .. 2.193 ns)-variance introduced by outliers: 92% (severely inflated)- benchmarking n = 3/Index Dict-time 56.35 ns (54.47 ns .. 58.42 ns)- 0.990 R² (0.986 R² .. 0.995 R²)-mean 55.85 ns (54.31 ns .. 57.90 ns)-std dev 5.972 ns (4.253 ns .. 7.946 ns)-variance introduced by outliers: 92% (severely inflated)+time 8.656 ns (8.396 ns .. 9.040 ns)+ 0.987 R² (0.964 R² .. 0.999 R²)+mean 8.784 ns (8.503 ns .. 9.318 ns)+std dev 1.322 ns (767.5 ps .. 2.059 ns)+variance introduced by outliers: 97% (severely inflated) benchmarking n = 3/Index DynDict-time 72.03 ns (70.14 ns .. 74.63 ns)- 0.989 R² (0.980 R² .. 0.995 R²)-mean 75.49 ns (73.19 ns .. 78.75 ns)-std dev 9.245 ns (7.589 ns .. 11.76 ns)-variance introduced by outliers: 94% (severely inflated)+time 6.400 ns (6.340 ns .. 6.467 ns)+ 0.999 R² (0.999 R² .. 1.000 R²)+mean 6.394 ns (6.348 ns .. 6.454 ns)+std dev 183.8 ps (153.3 ps .. 230.6 ps)+variance introduced by outliers: 49% (moderately inflated) benchmarking n = 3/Index HVect-time 69.21 ns (67.27 ns .. 71.86 ns)- 0.994 R² (0.989 R² .. 0.999 R²)-mean 68.69 ns (67.69 ns .. 70.13 ns)-std dev 4.080 ns (2.918 ns .. 5.949 ns)-variance introduced by outliers: 78% (severely inflated)--benchmarking n = 15/Build Dict-time 10.80 ns (10.65 ns .. 10.97 ns)- 0.997 R² (0.995 R² .. 1.000 R²)-mean 10.85 ns (10.71 ns .. 11.15 ns)-std dev 628.9 ps (344.3 ps .. 1.078 ns)-variance introduced by outliers: 79% (severely inflated)--benchmarking n = 15/Build DynDict-time 37.11 ns (36.55 ns .. 37.94 ns)- 0.997 R² (0.994 R² .. 0.999 R²)-mean 37.77 ns (37.04 ns .. 38.72 ns)-std dev 2.827 ns (2.096 ns .. 3.682 ns)-variance introduced by outliers: 86% (severely inflated)+time 14.12 ns (13.99 ns .. 14.25 ns)+ 0.999 R² (0.999 R² .. 1.000 R²)+mean 14.09 ns (13.95 ns .. 14.25 ns)+std dev 520.1 ps (445.4 ps .. 608.7 ps)+variance introduced by outliers: 60% (severely inflated) -benchmarking n = 15/Build HVect-time 15.82 ns (15.23 ns .. 16.59 ns)- 0.991 R² (0.985 R² .. 0.999 R²)-mean 15.61 ns (15.31 ns .. 16.07 ns)-std dev 1.221 ns (803.7 ps .. 1.757 ns)-variance introduced by outliers: 87% (severely inflated)+benchmarking n = 3/Index Vinyl+time 6.735 ns (6.635 ns .. 6.849 ns)+ 0.999 R² (0.998 R² .. 1.000 R²)+mean 6.706 ns (6.656 ns .. 6.763 ns)+std dev 186.4 ps (142.7 ps .. 252.2 ps)+variance introduced by outliers: 47% (moderately inflated) benchmarking n = 15/Index Dict-time 281.6 ns (279.6 ns .. 283.8 ns)+time 9.482 ns (9.395 ns .. 9.564 ns) 0.999 R² (0.999 R² .. 1.000 R²)-mean 281.7 ns (279.9 ns .. 283.9 ns)-std dev 6.878 ns (5.580 ns .. 8.830 ns)-variance introduced by outliers: 34% (moderately inflated)+mean 9.460 ns (9.387 ns .. 9.529 ns)+std dev 252.6 ps (212.2 ps .. 328.3 ps)+variance introduced by outliers: 44% (moderately inflated) benchmarking n = 15/Index DynDict-time 659.2 ns (652.3 ns .. 665.6 ns)- 0.999 R² (0.998 R² .. 0.999 R²)-mean 662.4 ns (656.8 ns .. 669.8 ns)-std dev 22.28 ns (17.73 ns .. 30.08 ns)-variance introduced by outliers: 48% (moderately inflated)+time 10.66 ns (10.47 ns .. 10.94 ns)+ 0.998 R² (0.996 R² .. 1.000 R²)+mean 10.52 ns (10.43 ns .. 10.65 ns)+std dev 389.0 ps (286.7 ps .. 575.8 ps)+variance introduced by outliers: 61% (severely inflated) benchmarking n = 15/Index HVect-time 693.4 ns (687.0 ns .. 698.7 ns)+time 14.35 ns (14.24 ns .. 14.46 ns) 0.999 R² (0.999 R² .. 1.000 R²)-mean 690.7 ns (683.2 ns .. 695.8 ns)-std dev 20.66 ns (16.34 ns .. 29.80 ns)-variance introduced by outliers: 42% (moderately inflated)+mean 14.35 ns (14.20 ns .. 14.51 ns)+std dev 513.6 ps (395.2 ps .. 693.4 ps)+variance introduced by outliers: 59% (severely inflated) +benchmarking n = 15/Index Vinyl+time 11.62 ns (11.47 ns .. 11.75 ns)+ 0.999 R² (0.999 R² .. 0.999 R²)+mean 11.63 ns (11.52 ns .. 11.74 ns)+std dev 373.0 ps (314.5 ps .. 452.0 ps)+variance introduced by outliers: 54% (severely inflated)+ benchmarking n = 15/Modify DynDict-time 98.74 ns (97.12 ns .. 100.8 ns)+time 10.30 ns (10.13 ns .. 10.50 ns) 0.998 R² (0.997 R² .. 0.999 R²)-mean 98.60 ns (97.18 ns .. 100.1 ns)-std dev 5.091 ns (3.999 ns .. 6.935 ns)-variance introduced by outliers: 72% (severely inflated)+mean 10.26 ns (10.18 ns .. 10.38 ns)+std dev 335.2 ps (260.3 ps .. 427.1 ps)+variance introduced by outliers: 55% (severely inflated)++benchmarking n = 15/Modify Vinyl+time 11.74 ns (11.60 ns .. 11.86 ns)+ 0.999 R² (0.999 R² .. 0.999 R²)+mean 11.67 ns (11.55 ns .. 11.79 ns)+std dev 398.9 ps (330.5 ps .. 487.9 ps)+variance introduced by outliers: 56% (severely inflated) ```
bench/Bench.hs view
@@ -2,8 +2,15 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DataKinds #-} +-- mostly for fget_qux0+{-# LANGUAGE TypeOperators, ExplicitNamespaces #-}+ module Main (main) where +import Data.Vinyl (Rec(..), FieldRec, type (∈))++import ProxySymbolTH -- stage restriction+ import Criterion.Main import qualified Data.Hetero.Dict as D import qualified Data.Hetero.DynDict as DD@@ -19,14 +26,28 @@ small :: [Benchmark] small =- [ bench "Build Dict" $ nf (D.get [key|qux0|]) dict- , bench "Build DynDict" $ nf (DD.get [key|qux0|]) dynDict- , bench "Build HVect" $ nf ((SSucc SZero)!!) hvect- , bench "Index Dict" $ nf getAllDict dict- , bench "Index DynDict" $ nf getAllDynDict dynDict- , bench "Index HVect" $ nf getAllHVect hvect+ [ bench "Index Dict" $ nf (D.get [key|qux0|]) dict+ , bench "Index DynDict" $ nf (DD.get [key|qux0|]) dynDict+ , bench "Index HVect" $ nf ((SSucc SZero) !!) hvect+ , bench "Index Vinyl" $ nf (fget_qux0) vinyl ] where++ vinyl+ = field [ps|foo0|] (1 :: Int)+ :& field [ps|bar0|] "bar"+ :& field [ps|qux0|] True+ :& RNil++ getAllVinyl d =+ ( fget [ps|foo0|] d :: Int+ --NOTE the annotations are unnecessary with:+ -- type Bar0 = '("bar0",String)+ -- fget [pr|Bar0|] -- pr from Data.Tagged.TH+ , fget [ps|bar0|] d :: String+ , fget [ps|qux0|] d :: Bool+ )+ hvect = (1 :: Int) :&: (1 :: Int) :&: "bar" :&: True@@ -62,15 +83,56 @@ large :: [Benchmark] large =- [ bench "Build Dict" $ nf (D.get [key|qux0|]) dict- , bench "Build DynDict" $ nf (DD.get [key|qux0|]) dynDict- , bench "Build HVect" $ nf ((SSucc SZero)!!) hvect- , bench "Index Dict" $ nf getAllDict dict- , bench "Index DynDict" $ nf getAllDynDict dynDict- , bench "Index HVect" $ nf getAllHVect hvect+ [ bench "Index Dict" $ nf (D.get [key|qux0|]) dict+ , bench "Index DynDict" $ nf (DD.get [key|qux0|]) dynDict+ , bench "Index HVect" $ nf ((SSucc SZero)!!) hvect+ , bench "Index Vinyl" $ nf (fget_qux0) vinyl+ , bench "Modify DynDict" $ nf (DD.get [key|qux0|] . DD.modify [key|qux0|] not) dynDict+ , bench "Modify Vinyl" $ nf (fget_qux0 . fmodify [ps|qux0|] not) vinyl ]+ where++ getAllVinyl d = (+ ( fget [ps|foo0|] d :: Int+ , fget [ps|foo1|] d :: Int+ , fget [ps|foo2|] d :: Int+ , fget [ps|foo3|] d :: Int+ , fget [ps|foo4|] d :: Int+ ),+ ( fget [ps|bar0|] d :: String+ , fget [ps|bar1|] d :: String+ , fget [ps|bar2|] d :: String+ , fget [ps|bar3|] d :: String+ , fget [ps|bar4|] d :: String+ ),+ ( fget [ps|qux0|] d :: Bool+ , fget [ps|qux1|] d :: Bool+ , fget [ps|qux2|] d :: Bool+ , fget [ps|qux3|] d :: Bool+ , fget [ps|qux4|] d :: Bool+ ))++ vinyl+ = field [ps|foo0|] (1 :: Int)+ :& field [ps|foo1|] (1 :: Int)+ :& field [ps|foo2|] (1 :: Int)+ :& field [ps|foo3|] (1 :: Int)+ :& field [ps|foo4|] (1 :: Int)+ :& field [ps|bar0|] "bar"+ :& field [ps|bar1|] "bar"+ :& field [ps|bar2|] "bar"+ :& field [ps|bar3|] "bar"+ :& field [ps|bar4|] "bar"+ :& field [ps|qux0|] True+ :& field [ps|qux1|] True+ :& field [ps|qux2|] True+ :& field [ps|qux3|] True+ :& field [ps|qux4|] True+ :& RNil++ getAllDict d = ( ( D.get [key|foo0|] d , D.get [key|foo1|] d@@ -182,3 +244,6 @@ . DD.add [key|qux3|] True . DD.add [key|qux4|] True $ DD.empty++fget_qux0 :: ('("qux0",Bool) ∈ fields) => FieldRec fields -> Bool+fget_qux0 = fget [ps|qux0|]
+ bench/ProxySymbolTH.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}+{-# LANGUAGE RecordWildCards, NamedFieldPuns #-}++{-# LANGUAGE DataKinds, ScopedTypeVariables, ExplicitNamespaces, TypeOperators, FlexibleContexts, PolyKinds #-}++module ProxySymbolTH where -- TODO i'll move this out somewhere++import Data.Vinyl+-- import Data.Vinyl.Notation (type (∈))++import Language.Haskell.TH+import Language.Haskell.TH.Quote+import Data.Proxy+import GHC.TypeLits++--------------------------------------------------------------------------------++-- | get a value from its key in the record+fget+ :: forall s a fields proxy. ('(s,a) ∈ fields)+ => proxy s -> FieldRec fields -> a+fget _ record = getField $ rget (Proxy :: Proxy '(s,a)) record++fmodify+ :: forall s a fields proxy. ('(s,a) ∈ fields)+ => proxy s -> (a -> a) -> FieldRec fields -> FieldRec fields+fmodify _ function input = output+ where+ output = rput b input+ b = fieldMap function a+ a = rget (Proxy :: Proxy '(s,a)) input++-- | construct a key-value pair+field :: (KnownSymbol s) => proxy s -> a -> ElField '(s,a)+field _ a = Field a++--------------------------------------------------------------------------------++-- | splices a proxy for a type-level string+ps :: QuasiQuoter+ps = defaultQuasiQuoter{quoteExp}+ where+ quoteExp = proxySymbolQ++proxySymbolQ :: String -> ExpQ+proxySymbolQ = proxyExpQ . litT . strTyLit++proxyTypeQ :: TypeQ -> TypeQ+proxyTypeQ t = appT (conT proxy_tc) t++proxyExpQ :: TypeQ -> ExpQ+proxyExpQ t = sigE (conE proxy_d) (proxyTypeQ t)++-- makeSymbol :: String -> Type+-- makeSymbol = LitT . StrTyLit++proxy_tc :: Name+proxy_tc = ''Proxy++proxy_d :: Name+proxy_d = 'Proxy++defaultQuasiQuoter :: QuasiQuoter+defaultQuasiQuoter = QuasiQuoter{..}+ where++ quoteExp :: String -> Q Exp+ quoteExp = error $ message "expressions"++ quotePat :: String -> Q Pat+ quotePat = error $ message "patterns"++ quoteType :: String -> Q Type+ quoteType = error $ message "types"++ quoteDec :: String -> Q [Dec]+ quoteDec = error $ message "declarations"++ message x = "this QuasiQuoter doesn't support " ++ x
hetero-dict.cabal view
@@ -1,5 +1,5 @@ name: hetero-dict-version: 0.1.0.1+version: 0.1.1.0 synopsis: Fast heterogeneous data structures description: Fast heterogeneous data structures license: MIT@@ -10,7 +10,7 @@ copyright: (c) 2014-2015 Hirotomo Moriwaki, 2016 Winterland category: Data build-type: Simple-extra-source-files: ChangeLog.md, README.md+extra-source-files: ChangeLog.md, README.md, bench/ProxySymbolTH.hs cabal-version: >=1.10 library@@ -21,17 +21,20 @@ build-depends: base >=4.7 && <5.0 , primitive >=0.5 && <0.7 , template-haskell >=2.9 && <2.12- , containers >=0.4.2.0 && <0.6+ , aeson >=0.7+ , unordered-containers >=0.2.5+ , text >=1.1 ghc-options: -Wall -O2 default-language: Haskell2010 benchmark criterion- build-depends: base, deepseq, hvect, hetero-dict, criterion == 1.1.*+ build-depends: base, deepseq, template-haskell, vinyl ==0.5.2, hvect, hetero-dict, criterion == 1.1.* default-language: Haskell2010 hs-source-dirs: bench main-is: Bench.hs type: exitcode-stdio-1.0+ ghc-options: -O2 source-repository head type: git