on 5a45366 help sign in
20 files changed+394−717
since splitsnapshots
unifiedsplit
.github/workflows/ci.yml+4−3 .travis.yml+28−10 CHANGELOG.md+1−1 README.md+1−1 benchmark/CMap.hs+39−46 benchmark/CacheMap.hs+45−48 benchmark/DMap.hs+42−45 benchmark/Main.hs+32−37 benchmark/OptimalVector.hs+28−29 benchmark/Spec.hs+50−61 benchmark/Vector.hs+22−23 src/Data/TMap.hs+2−48 src/Data/TypeRepMap.hs+1−6 src/Data/TypeRepMap/Internal.hs+50−198 test/Test/TypeRep/TypeRepMap.hs+4−62 test/Test/TypeRep/TypeRepMapProperty.hs+29−70 typerep-extra-impls/Data/TypeRep/CMap.hs+3−3 typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−7 typerep-extra-impls/Data/TypeRep/Vector.hs+2−2 typerep-map.cabal+3−17
.github/workflows/ci.yml+4−3
@@ -1,10 +1,11 @@
11name: CI
22
3−# Trigger the workflow on push or pull request, but only for the main branch
43on:
4+ # Trigger the workflow on push or pull request,
5+ # but only for the master branch
56 push:
67 branches:
7− - main
8+ - master
89 pull_request:
910
1011jobs:
@@ -23,7 +24,7 @@ jobs:
2324
2425 steps:
2526 - uses: actions/checkout@v2
26− if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/main'
27+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'
2728
2829 - uses: actions/setup-haskell@v1
2930 name: Setup Haskell
.travis.yml+28−10
@@ -1,28 +1,46 @@
11sudo: true
2−language: generic
2+language: haskell
33
44git:
55 depth: 5
66
7+cabal: "3.0"
8+
79cache:
810 directories:
11+ - "$HOME/.cabal/store"
912 - "$HOME/.stack"
13+ - "$TRAVIS_BUILD_DIR/.stack-work"
1014
1115matrix:
1216 include:
13− - env: GHC='8.8.3'
14− os: linux
15− - env: GHC='8.8.3'
16− os: osx
17+ - ghc: 8.2.2
18+ - ghc: 8.4.4
19+ - ghc: 8.6.5
20+ - ghc: 8.8.3
21+ - ghc: 8.10.1
22+
23+ - ghc: 8.8.3
24+ env: STACK_YAML="$TRAVIS_BUILD_DIR/stack.yaml"
1725
1826install:
19− # install stack and build project
20− - curl -sSL https://get.haskellstack.org/ | sh
21− - stack --version
22− - stack build --test --no-run-tests --bench --no-run-benchmarks
27+ - |
28+ if [ -z "$STACK_YAML" ]; then
29+ cabal update
30+ cabal build --enable-tests --enable-benchmarks
31+ else
32+ curl -sSL https://get.haskellstack.org/ | sh
33+ stack --version
34+ stack build --system-ghc --test --no-run-tests
35+ fi
2336
2437script:
25− - stack test --no-terminal
38+ - |
39+ if [ -z "$STACK_YAML" ]; then
40+ cabal test --enable-tests
41+ else
42+ stack build --system-ghc --test --bench --no-run-benchmarks --no-terminal --ghc-options=-Werror
43+ fi
2644
2745notifications:
2846 email: false
CHANGELOG.md+1−1
@@ -53,4 +53,4 @@ The changelog is available [on GitHub][2].
5353* Initially created.
5454
5555[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
56+[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
README.md+1−1
@@ -4,7 +4,7 @@
44
55[![GitHub CI](https://github.com/kowainik/typerep-map/workflows/CI/badge.svg)](https://github.com/kowainik/typerep-map/actions)
66[![Build status](https://img.shields.io/travis/kowainik/typerep-map.svg?logo=travis)](https://travis-ci.org/kowainik/typerep-map)
7−[![Windows build status](https://ci.appveyor.com/api/projects/status/github/kowainik/typerep-map?branch=main&svg=true)](https://ci.appveyor.com/project/kowainik/typerep-map)
7+[![Windows build status](https://ci.appveyor.com/api/projects/status/github/kowainik/typerep-map?branch=master&svg=true)](https://ci.appveyor.com/project/kowainik/typerep-map)
88[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
99[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
1010[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
benchmark/CMap.hs+39−46
@@ -1,77 +1,70 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE TypeFamilies #-}
66{-# LANGUAGE TypeOperators #-}
77{-# LANGUAGE UndecidableInstances #-}
88
9+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10+
911module CMap
10− ( spec
11− ) where
12+ ( spec
13+ ) where
14+
15+import Criterion.Main (bench, nf, env, whnf)
1216
1317import Prelude hiding (lookup)
1418
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
19+import Spec
1720import Data.Maybe (fromJust)
1821import Data.Proxy (Proxy (..))
1922import Data.Typeable (Typeable)
20−import GHC.TypeLits (type (+), KnownNat, Nat)
23+import GHC.TypeLits
2124
2225import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
2326
24−import Spec (BenchSpec (..))
25−
26−
2727spec :: BenchSpec
2828spec = BenchSpec
29− { benchLookup = Just $ \name ->
30− env (mkMap 10000) $ \ ~bigMap ->
31− bench name $ nf tenLookups bigMap
32− , benchInsertSmall = Just $ \name ->
33− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
34− , benchInsertBig = Just $ \name ->
35− env (mkMap 10000) $ \ ~bigMap ->
36− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
37− , benchUpdateSmall = Just $ \name ->
38− env (mkMap 10) $ \ ~smallMap ->
39− bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
40− , benchUpdateBig = Just $ \name ->
41− env (mkMap 10000) $ \ ~bigMap ->
42− bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
43− }
29+ { benchLookup = Just $ \name ->
30+ env (mkMap 10000) $ \ ~bigMap ->
31+ bench name $ nf tenLookups bigMap
32+ , benchInsertSmall = Just $ \name ->
33+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
34+ , benchInsertBig = Just $ \name ->
35+ env (mkMap 10000) $ \ ~(bigMap) ->
36+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
37+ , benchUpdateSmall = Just $ \name ->
38+ env (mkMap 10) $ \ ~(smallMap) ->
39+ bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
40+ , benchUpdateBig = Just $ \name ->
41+ env (mkMap 10000) $ \ ~(bigMap) ->
42+ bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
43+ }
4444
45−tenLookups
46− :: TypeRepMap (Proxy :: Nat -> Type)
47− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
48− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
49− )
45+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
46+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
47+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
48+ )
5049tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5150 where
5251 lp :: forall (a::Nat). Typeable a => Proxy a
5352 lp = fromJust $ lookup tmap
5453
55−inserts
56− :: forall a . (KnownNat a)
57− => TypeRepMap (Proxy :: Nat -> Type)
58− -> Int
59− -> Proxy (a :: Nat)
60− -> TypeRepMap (Proxy :: Nat -> Type)
54+inserts :: forall a . (KnownNat a)
55+ => TypeRepMap (Proxy :: Nat -> *)
56+ -> Int
57+ -> Proxy (a :: Nat)
58+ -> TypeRepMap (Proxy :: Nat -> *)
6159inserts !c 0 _ = c
6260inserts !c n x = inserts
63− (insert x c)
64− (n-1)
65− (Proxy :: Proxy (a+1))
61+ (insert x c)
62+ (n-1)
63+ (Proxy :: Proxy (a+1))
6664
67−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
65+mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> *))
6866mkMap n = pure $ buildBigMap n (Proxy :: Proxy 0) empty
6967
70−buildBigMap
71− :: forall a . (KnownNat a)
72− => Int
73− -> Proxy (a :: Nat)
74− -> TypeRepMap (Proxy :: Nat -> Type)
75− -> TypeRepMap (Proxy :: Nat -> Type)
68+buildBigMap :: forall a . (KnownNat a) => Int -> Proxy (a :: Nat) -> TypeRepMap (Proxy :: Nat -> *) -> TypeRepMap (Proxy :: Nat -> *)
7669buildBigMap 1 x = insert x
7770buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/CacheMap.hs+45−48
@@ -1,79 +1,76 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE TypeFamilies #-}
66{-# LANGUAGE TypeOperators #-}
77{-# LANGUAGE UndecidableInstances #-}
88
9+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10+
911module CacheMap
10− ( spec
11− ) where
12+ ( spec
13+ ) where
14+
15+import Criterion.Main (bench, nf, whnf, env)
16+import Spec
1217
1318import Prelude hiding (lookup)
1419
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
1720import Data.Maybe (fromJust)
1821import Data.Proxy (Proxy (..))
1922import Data.Typeable (Typeable)
2023import GHC.Exts (fromList)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
22−
23−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
24−
25−import Spec (BenchSpec (..))
24+import GHC.TypeLits
2625
26+import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), lookup, insert, empty)
2727
2828spec :: BenchSpec
2929spec = BenchSpec
30− { benchLookup = Just $ \name ->
31− env (mkMap 10000) $ \ ~bigMap ->
32− bench name $ nf tenLookups bigMap
33− , benchInsertSmall = Just $ \name ->
34− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
35− , benchInsertBig = Just $ \name ->
36− env (mkMap 10000) $ \ ~bigMap ->
37− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
38− , benchUpdateSmall = Just $ \name ->
39− env (mkMap 10) $ \ ~smallMap ->
40− bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
41− , benchUpdateBig = Just $ \name ->
42− env (mkMap 10000) $ \ ~bigMap ->
43− bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
44− }
30+ { benchLookup = Just $ \name ->
31+ env (mkMap 10000) $ \ ~bigMap ->
32+ bench name $ nf tenLookups bigMap
33+ , benchInsertSmall = Just $ \name ->
34+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
35+ , benchInsertBig = Just $ \name ->
36+ env (mkMap 10000) $ \ ~(bigMap) ->
37+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
38+ , benchUpdateSmall = Just $ \name ->
39+ env (mkMap 10) $ \ ~(smallMap) ->
40+ bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
41+ , benchUpdateBig = Just $ \name ->
42+ env (mkMap 10000) $ \ ~(bigMap) ->
43+ bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
44+ }
4545
46−tenLookups
47− :: TypeRepMap (Proxy :: Nat -> Type)
48− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
49− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
50− )
46+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
47+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
48+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
49+ )
5150tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5251 where
53− lp :: forall (a :: Nat) . Typeable a => Proxy a
52+ lp :: forall (a::Nat). Typeable a => Proxy a
5453 lp = fromJust $ lookup tmap
5554
56−inserts
57− :: forall a . (KnownNat a)
58− => TypeRepMap (Proxy :: Nat -> Type)
59− -> Int
60− -> Proxy (a :: Nat)
61− -> TypeRepMap (Proxy :: Nat -> Type)
55+inserts :: forall a . (KnownNat a)
56+ => TypeRepMap (Proxy :: Nat -> *)
57+ -> Int
58+ -> Proxy (a :: Nat)
59+ -> TypeRepMap (Proxy :: Nat -> *)
6260inserts !c 0 _ = c
6361inserts !c n x = inserts
64− (insert x c)
65− (n-1)
66− (Proxy :: Proxy (a + 1))
62+ (insert x c)
63+ (n-1)
64+ (Proxy :: Proxy (a+1))
6765
68−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
66+mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> *))
6967mkMap n = pure $ fromList $ buildBigMap n (Proxy :: Proxy 0) []
7068
7169
72−buildBigMap
73− :: forall a . (KnownNat a)
74− => Int
75− -> Proxy (a :: Nat)
76− -> [WrapTypeable (Proxy :: Nat -> Type)]
77− -> [WrapTypeable (Proxy :: Nat -> Type)]
70+buildBigMap :: forall a . (KnownNat a)
71+ => Int
72+ -> Proxy (a :: Nat)
73+ -> [WrapTypeable (Proxy :: Nat -> *)]
74+ -> [WrapTypeable (Proxy :: Nat -> *)]
7875buildBigMap 1 x = (WrapTypeable x :)
7976buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hs+42−45
@@ -1,81 +1,79 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
43{-# LANGUAGE InstanceSigs #-}
4+{-# LANGUAGE KindSignatures #-}
55{-# LANGUAGE PolyKinds #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
99
10+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11+
1012module DMap
11− ( spec
12− ) where
13+ ( spec
14+ ) where
15+
16+import Criterion.Main (bench, env, nf, whnf)
1317
1418import Prelude hiding (lookup)
1519
1620import Control.DeepSeq (NFData (..))
17−import Criterion.Main (bench, env, nf, whnf)
18−import Data.Kind (Type)
1921import Data.Maybe (fromJust)
2022import Data.Proxy (Proxy (..))
21−import GHC.TypeLits (type (+), KnownNat, Nat)
23+import GHC.TypeLits
24+import Spec
2225import Type.Reflection (TypeRep, Typeable, typeRep)
2326import Type.Reflection.Unsafe (typeRepFingerprint)
2427
2528import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
2629import Data.Some (Some (Some))
2730
28−import Spec (BenchSpec (..))
29−
30−
3131type TypeRepMap = DMap TypeRep
3232
33+
3334spec :: BenchSpec
3435spec = BenchSpec
35− { benchLookup = Just $ \name ->
36− env mkBigMap $ \ ~(DMapNF bigMap) ->
37− bench name $ nf tenLookups bigMap
38− , benchInsertSmall = Just $ \name ->
39− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
40− , benchInsertBig = Just $ \name ->
41− env mkBigMap $ \ ~(DMapNF bigMap) ->
42− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
43− , benchUpdateSmall = Nothing -- Not implemented
44− , benchUpdateBig = Nothing -- Not implemented
45− }
36+ { benchLookup = Just $ \name ->
37+ env mkBigMap $ \ ~(DMapNF bigMap) ->
38+ bench name $ nf tenLookups bigMap
39+ , benchInsertSmall = Just $ \name ->
40+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
41+ , benchInsertBig = Just $ \name ->
42+ env mkBigMap $ \ ~(DMapNF bigMap) ->
43+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
44+ , benchUpdateSmall = Nothing -- Not implemented
45+ , benchUpdateBig = Nothing -- Not implemented
46+ }
4647
47−tenLookups
48− :: TypeRepMap (Proxy :: Nat -> Type)
49− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
50− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
51− )
48+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
49+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
50+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
51+ )
5252tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5353 where
5454 lp :: forall (a :: Nat) . Typeable a => Proxy a
5555 lp = fromJust $ lookup (typeRep @a) tmap
5656
57−inserts
58− :: forall a . (KnownNat a)
59− => TypeRepMap (Proxy :: Nat -> Type)
60− -> Int
61− -> Proxy (a :: Nat)
62− -> TypeRepMap (Proxy :: Nat -> Type)
57+inserts :: forall a . (KnownNat a)
58+ => TypeRepMap (Proxy :: Nat -> *)
59+ -> Int
60+ -> Proxy (a :: Nat)
61+ -> TypeRepMap (Proxy :: Nat -> *)
6362inserts !c 0 _ = c
6463inserts !c n x = inserts
65− (insert (typeRep @ a) x c)
66− (n-1)
67− (Proxy :: Proxy (a+1))
64+ (insert (typeRep @ a) x c)
65+ (n-1)
66+ (Proxy :: Proxy (a+1))
6867
6968-- TypeRepMap of 10000 elements
70−mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
69+mkBigMap :: IO (DMapNF (Proxy :: Nat -> *))
7170mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
7271
73−buildBigMap
74− :: forall a . (KnownNat a)
75− => Int
76− -> Proxy (a :: Nat)
77− -> TypeRepMap (Proxy :: Nat -> Type)
78− -> TypeRepMap (Proxy :: Nat -> Type)
72+buildBigMap :: forall a . (KnownNat a)
73+ => Int
74+ -> Proxy (a :: Nat)
75+ -> TypeRepMap (Proxy :: Nat -> *)
76+ -> TypeRepMap (Proxy :: Nat -> *)
7977buildBigMap 1 x = insert (typeRep @a) x
8078buildBigMap n x = insert (typeRep @a) x
8179 . buildBigMap (n - 1) (Proxy @(a + 1))
@@ -84,6 +82,5 @@ buildBigMap n x = insert (typeRep @a) x
8482newtype DMapNF f = DMapNF (TypeRepMap f)
8583
8684instance NFData (DMapNF f) where
87− rnf :: DMapNF f -> ()
88− rnf (DMapNF x) =
89− rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
85+ rnf (DMapNF x) =
86+ rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
benchmark/Main.hs+32−37
@@ -1,52 +1,47 @@
11{-# LANGUAGE CPP #-}
22
3−module Main
4− ( main
5− ) where
3+module Main where
64
7−import Criterion.Main (bgroup, defaultMain)
5+import Criterion.Main (defaultMain, bgroup)
86
9−import Spec (BenchSpec (..))
10−
11−import qualified CacheMap
7+import Spec
128import qualified CMap
9+import qualified CacheMap
1310#if ( __GLASGOW_HASKELL__ >= 802 )
1411import qualified DMap
1512#endif
1613import qualified OptimalVector as OptVec
1714
18−
1915main :: IO ()
2016main = do
21− let specs = [("CMap", CMap.spec)
22− ,("CacheMap", CacheMap.spec)
17+ let specs = [("CMap", CMap.spec)
18+ ,("CacheMap", CacheMap.spec)
2319#if ( __GLASGOW_HASKELL__ >= 802 )
24− , ("DMap", DMap.spec)
20+ , ("DMap", DMap.spec)
2521#endif
26− , ("OptVec", OptVec.spec)
27− ]
28− {- This code creates a benchmark group. Given a getter
29− (that is test description) it gets a benchmark generation
30− function from each module spec. Benchmark generation
31− function takes a label and generate benchmarks. It's
32− possible to introduce parameters passing in the same way.
33− --}
34− let mkGroup getBenchmark =
35− [ mkBenchmark label
36− | (label, spec) <- specs
37− -- Here we use pure to force pattern matching in List
38− -- then in case of pattern match failure `mzero` will
39− -- be called, so benchmark will be ignored.
40− , Just mkBenchmark <- pure $ getBenchmark spec
41− ]
42− defaultMain
43− [ bgroup "lookup" $ mkGroup benchLookup
44− , bgroup "insert"
45− [ bgroup "10 elements to empty" $ mkGroup benchInsertSmall
46− , bgroup "1 element to big map" $ mkGroup benchInsertBig
47− ]
48− , bgroup "update"
49− [ bgroup "10 elements to empty" $ mkGroup benchUpdateSmall
50− , bgroup "1 element to big map" $ mkGroup benchUpdateBig
51− ]
22+ , ("OptVec", OptVec.spec)
23+ ]
24+ -- This code creates a benchmark group. Given a getter
25+ -- (that is test description) it gets a benchmark generation
26+ -- function from each module spec. Benchmark generation
27+ -- function takes a label and generate benchmarks. It's
28+ -- possible to introduce parameters passing in the same way.
29+ mkGroup getBenchmark =
30+ [ mkBenchmark label
31+ | (label, spec) <- specs
32+ -- Here we use pure to force pattern matching in List
33+ -- then in case of pattern match failure `mzero` will
34+ -- be called, so benchmark will be ignored.
35+ , Just mkBenchmark <- pure $ getBenchmark spec
5236 ]
37+ defaultMain
38+ [ bgroup "lookup" $ mkGroup benchLookup
39+ , bgroup "insert"
40+ [ bgroup "10 elements to empty" $ mkGroup benchInsertSmall
41+ , bgroup "1 element to big map" $ mkGroup benchInsertBig
42+ ]
43+ , bgroup "update"
44+ [ bgroup "10 elements to empty" $ mkGroup benchUpdateSmall
45+ , bgroup "1 element to big map" $ mkGroup benchUpdateBig
46+ ]
47+ ]
benchmark/OptimalVector.hs+28−29
@@ -1,60 +1,59 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
99
10+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11+
1012module OptimalVector
1113 ( spec
1214 ) where
1315
16+import Spec
17+import Criterion.Main (bench, nf, env)
18+
1419import Prelude hiding (lookup)
1520
16−import Criterion.Main (bench, env, nf)
17−import Data.Kind (Type)
1821import Data.Maybe (fromJust)
1922import Data.Proxy (Proxy (..))
2023import Data.Typeable (Typeable)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
24+import GHC.TypeLits
2225
2326import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
2427
25−import Spec (BenchSpec (..))
26−
27−
2828spec :: BenchSpec
2929spec = BenchSpec
30− { benchLookup = Just $ \name ->
31− env mkBigMap $ \ ~bigMap ->
32− bench name $ nf tenLookups bigMap
33− , benchInsertSmall = Nothing -- Not implemented
34− , benchInsertBig = Nothing -- Not implemented
35− , benchUpdateSmall = Nothing -- Not implemented
36− , benchUpdateBig = Nothing -- Not implemented
37− }
38−
39−tenLookups
40− :: TypeRepMap (Proxy :: Nat -> Type)
41− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
42− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
43− )
30+ { benchLookup = Just $ \name ->
31+ env mkBigMap $ \ ~bigMap ->
32+ bench name $ nf tenLookups bigMap
33+ , benchInsertSmall = Nothing -- Not implemented
34+ , benchInsertBig = Nothing -- Not implemented
35+ , benchUpdateSmall = Nothing -- Not implemented
36+ , benchUpdateBig = Nothing -- Not implemented
37+ }
38+
39+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
40+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
41+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
42+ )
4443tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4544 where
4645 lp :: forall (a::Nat). Typeable a => Proxy a
4746 lp = fromJust $ lookup tmap
4847
4948-- TypeRepMap of 10000 elements
50−mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> Type))
49+mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> *))
5150mkBigMap = pure $ fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
5251
53−buildBigMap
54− :: forall a . (KnownNat a)
55− => Int
56− -> Proxy (a :: Nat)
57− -> [TF (Proxy :: Nat -> Type)]
58− -> [TF (Proxy :: Nat -> Type)]
52+buildBigMap :: forall a . (KnownNat a)
53+ => Int
54+ -> Proxy (a :: Nat)
55+ -> [TF (Proxy :: Nat -> *)]
56+ -> [TF (Proxy :: Nat -> *)]
5957buildBigMap 1 x = (TF x :)
6058buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
59+
benchmark/Spec.hs+50−61
@@ -1,64 +1,53 @@
1−{- | Specification of the benchmarks.
2−
3−This module keeps a list of all bencharks, this way
4−we can group benchmark by the interesting function, not
5−by the implementation.
6−-}
1+-- | Specification of the benchmarks.
2+-- This module keeps a list of all bencharks, this way
3+-- we can group benchmark by the interesting function, not
4+-- by the implementation.
75module Spec
8− ( BenchSpec(..)
9− ) where
10−
11−import Criterion (Benchmark)
12−
13−
14−{- | List of benchmarks that each module should provide.
15−If implementation can express the benchmark then it
16−can return @Nothing@ in that benchmark.
17−
18−Map should contain elements from @1@ to @size of map@
19−inserted in ascending order (later that requirement may
20−change).
21−-}
6+ ( BenchSpec(..)
7+ ) where
8+
9+import Criterion
10+
11+-- | List of benchmarks that each module should provide.
12+-- If implementation can express the benchmark then it
13+-- can return @Nothing@ in that benchmark.
14+--
15+-- Map should contain elements from @1@ to @size of map@
16+-- inserted in ascending order (later that requirement may
17+-- change).
2218data BenchSpec = BenchSpec
23− { {- | Basic lookup we look 10 values inside 10k map.
24−
25− Implementation may look like:
26−
27− @
28− tenLookups :: TypeRepMap (Proxy :: Nat -> *)
29− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
30− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
31− )
32− tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
33− @
34− -}
35− benchLookup :: Maybe (String -> Benchmark)
36− {- ^ Insert 10 elements into an empty map.
37−
38− Implementation may look like:
19+ { benchLookup :: Maybe (String -> Benchmark)
20+ -- ^ Basic lookup we look 10 values inside 10k map.
21+ --
22+ -- Implementation may look like:
23+ -- @
24+ -- tenLookups :: TypeRepMap (Proxy :: Nat -> *)
25+ -- -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
26+ -- , Proxy 50, Proxy 60, Proxy 70, Proxy 80
27+ -- )
28+ -- tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
29+ -- @
30+ , benchInsertSmall :: Maybe (String -> Benchmark)
31+ -- ^ Insert 10 elements into an empty map.
32+ --
33+ -- Implementation may look like:
34+ -- @
35+ -- inserts :: forall a . (KnownNat a)
36+ -- => TypeRepMap (Proxy :: Nat -> *)
37+ -- -> Int
38+ -- -> Proxy (a :: Nat)
39+ -- -> TypeRepMap (Proxy :: Nat -> *)
40+ -- inserts !c 0 _ = c
41+ -- inserts !c n x = inserts (insert x c) (n-1) (Proxy :: Proxy (a+1))
42+ -- @
43+ , benchInsertBig :: Maybe (String -> Benchmark)
44+ -- ^ Insert 10 elements into a big map. Implementation is like
45+ -- a small map, but should insert values into 10k elements map.
46+ , benchUpdateSmall :: Maybe (String -> Benchmark)
47+ -- ^ Insert 10 elements into map of 10 elements, where each key
48+ -- was already inserted in the map
49+ , benchUpdateBig :: Maybe (String -> Benchmark)
50+ -- ^ Insert 10 elements into map of 10k elements, where each key
51+ -- was already inserted in the map
52+ }
3953
40− @
41− inserts
42− :: forall a . (KnownNat a)
43− => TypeRepMap (Proxy :: Nat -> *)
44− -> Int
45− -> Proxy (a :: Nat)
46− -> TypeRepMap (Proxy :: Nat -> *)
47− inserts !c 0 _ = c
48− inserts !c n x = inserts (insert x c) (n-1) (Proxy :: Proxy (a+1))
49− @
50− -}
51− , benchInsertSmall :: Maybe (String -> Benchmark)
52− {- ^ Insert 10 elements into a big map. Implementation is like
53− a small map, but should insert values into 10k elements map.
54− -}
55− , benchInsertBig :: Maybe (String -> Benchmark)
56− {- ^ Insert 10 elements into map of 10 elements, where each key
57− was already inserted in the map
58− -}
59− , benchUpdateSmall :: Maybe (String -> Benchmark)
60− {- ^ Insert 10 elements into map of 10k elements, where each key
61− was already inserted in the map
62− -}
63− , benchUpdateBig :: Maybe (String -> Benchmark)
64− }
benchmark/Vector.hs+22−23
@@ -1,30 +1,31 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
99
10+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11+
1012module Vector
11− ( benchVector
12− , prepareBenchVector
13− ) where
13+ ( benchVector
14+ , prepareBenchVector
15+ ) where
16+
17+import Criterion.Main (Benchmark, bench, bgroup, nf)
1418
1519import Prelude hiding (lookup)
1620
1721import Control.DeepSeq (rnf)
18−import Control.Exception (evaluate)
19−import Criterion.Main (Benchmark, bench, bgroup, nf)
20−import Data.Kind (Type)
22+import Control.Exception
2123import Data.Maybe (fromJust)
2224import Data.Proxy (Proxy (..))
2325import Data.Typeable (Typeable)
24−import GHC.TypeLits (type (+), KnownNat, Nat)
25−
26−import Data.TypeRep.Vector (TF (..), TypeRepVector, fingerprints, fromList, lookup)
26+import GHC.TypeLits
2727
28+import Data.TypeRep.Vector
2829
2930benchVector :: Benchmark
3031benchVector = bgroup "vector"
@@ -33,26 +34,24 @@ benchVector = bgroup "vector"
3334 -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
3435 ]
3536
36−tenLookups
37− :: TypeRepVector (Proxy :: Nat -> Type)
38− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40− )
37+tenLookups :: TypeRepVector (Proxy :: Nat -> *)
38+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40+ )
4141tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4242 where
4343 lp :: forall (a::Nat). Typeable a => Proxy a
4444 lp = fromJust $ lookup tmap
4545
4646-- TypeRepMap of 10000 elements
47−bigMap :: TypeRepVector (Proxy :: Nat -> Type)
47+bigMap :: TypeRepVector (Proxy :: Nat -> *)
4848bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
4949
50−buildBigMap
51− :: forall a . (KnownNat a)
52− => Int
53− -> Proxy (a :: Nat)
54− -> [TF (Proxy :: Nat -> Type)]
55− -> [TF (Proxy :: Nat -> Type)]
50+buildBigMap :: forall a . (KnownNat a)
51+ => Int
52+ -> Proxy (a :: Nat)
53+ -> [TF (Proxy :: Nat -> *)]
54+ -> [TF (Proxy :: Nat -> *)]
5655buildBigMap 1 x = (TF x :)
5756buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
5857
src/Data/TMap.hs+2−48
@@ -37,19 +37,14 @@ module Data.TMap
3737 , delete
3838 , unionWith
3939 , union
40− , intersectionWith
41− , intersection
4240 , map
4341 , adjust
44− , alter
4542
4643 -- * Query
4744 , lookup
4845 , member
4946 , size
5047 , keys
51− , keysWith
52− , toListWith
5348 ) where
5449
5550import Prelude hiding (lookup, map)
@@ -57,7 +52,7 @@ import Prelude hiding (lookup, map)
5752import Data.Functor.Identity (Identity (..))
5853import Data.Typeable (Typeable)
5954import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
55+import Type.Reflection (SomeTypeRep)
6156
6257import qualified Data.TypeRepMap as F
6358
@@ -92,7 +87,6 @@ one x = coerce (F.one @a @Identity $ coerce x)
9287{- |
9388
9489Insert a value into a 'TMap'.
95−TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
9690
9791prop> size (insert v tm) >= size tm
9892prop> member @a (insert (x :: a) tm) == True
@@ -104,9 +98,6 @@ insert x = coerce (F.insert @a @Identity $ coerce x)
10498
10599{- | Delete a value from a 'TMap'.
106100
107−TMap optimizes for fast reads rather than modifications, as a trade-off deletes are @O(n)@,
108−with an @O(log(n))@ optimization for when the element is already missing.
109−
110101prop> size (delete @a tm) <= size tm
111102prop> member @a (delete @a tm) == False
112103
@@ -136,24 +127,6 @@ union :: TMap -> TMap -> TMap
136127union = F.union
137128{-# INLINE union #-}
138129
139−-- | The intersection of two 'TMap's using a combining function.
140−--
141−-- @O(n + m)@
142−intersectionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
143−intersectionWith f = F.intersectionWith fId
144− where
145− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
146− fId y1 y2 = f (coerce y1) (coerce y2)
147−{-# INLINE intersectionWith #-}
148−
149−-- | The intersection of two 'TMap's.
150−-- It keeps all values from the first map whose keys are present in the second.
151−--
152−-- @O(n + m)@
153−intersection :: TMap -> TMap -> TMap
154−intersection = F.intersection
155−{-# INLINE intersection #-}
156−
157130{- | Lookup a value of the given type in a 'TMap'.
158131
159132>>> x = lookup $ insert (11 :: Int) empty
@@ -187,16 +160,6 @@ keys :: TMap -> [SomeTypeRep]
187160keys = F.keys
188161{-# INLINE keys #-}
189162
190−-- | Return the list of keys by wrapping them with a user-provided function.
191−keysWith :: (forall a. TypeRep a -> r) -> TMap -> [r]
192−keysWith = F.keysWith
193−{-# INLINE keysWith #-}
194−
195−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
196−toListWith :: (forall a. Typeable a => a -> r) -> TMap -> [r]
197−toListWith = F.toListWith
198−{-# INLINE toListWith #-}
199−
200163-- | Map a function over the values.
201164map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202165map f = F.hoistWithKey (liftToIdentity f)
@@ -207,14 +170,5 @@ adjust :: Typeable a => (a -> a) -> TMap -> TMap
207170adjust f = F.adjust (liftToIdentity f)
208171{-# INLINE adjust #-}
209172
210−-- | Updates a value at a specific key, whether or not it exists.
211−-- This can be used to insert, delete, or update a value of a given type in the map.
212−alter :: Typeable a => (Maybe a -> Maybe a) -> TMap -> TMap
213−alter f = F.alter (liftF f)
214− where
215− liftF :: forall a. (Maybe a -> Maybe a) -> Maybe (Identity a) -> Maybe (Identity a)
216− liftF = coerce
217−{-# INLINE alter #-}
218−
219−liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
173+liftToIdentity :: forall a. Typeable a => (a -> a) -> Identity a -> Identity a
220174liftToIdentity = coerce
src/Data/TypeRepMap.hs+1−6
@@ -5,7 +5,7 @@ Copyright: (c) 2017-2020 Kowainik
55SPDX-License-Identifier: MPL-2.0
66Maintainer: Kowainik <xrom.xkov@gmail.com>
77
8−A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
8+ A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
99sort of parametrization may be familiar to users of @vinyl@ records.
1010
1111@'TypeRepMap' f@ is a more efficient replacement for @DMap
@@ -57,22 +57,17 @@ module Data.TypeRepMap
5757 , insert
5858 , delete
5959 , adjust
60− , alter
6160 , hoist
6261 , hoistA
6362 , hoistWithKey
6463 , unionWith
6564 , union
66− , intersectionWith
67− , intersection
6865
6966 -- * Query
7067 , lookup
7168 , member
7269 , size
7370 , keys
74− , keysWith
75− , toListWith
7671
7772 -- * 'IsList'
7873 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+50−198
@@ -1,11 +1,10 @@
1−{-# OPTIONS_GHC -Wno-missing-export-lists #-}
2−
31{-# LANGUAGE AllowAmbiguousTypes #-}
42{-# LANGUAGE BangPatterns #-}
53{-# LANGUAGE CPP #-}
64{-# LANGUAGE DataKinds #-}
75{-# LANGUAGE GADTs #-}
86{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE KindSignatures #-}
98{-# LANGUAGE MagicHash #-}
109{-# LANGUAGE PolyKinds #-}
1110{-# LANGUAGE Rank2Types #-}
@@ -45,9 +44,9 @@ import Data.Kind (Type)
4544import Data.Type.Equality ((:~:) (..), TestEquality (..))
4645import Data.List (intercalate, nubBy)
4746import Data.Maybe (fromMaybe)
48−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
47+import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', readArray, sizeofArray,
4948 thawArray, unsafeFreezeArray, writeArray)
50−import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
49+import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
5150import Data.Semigroup (Semigroup (..), All(..))
5251import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
5352import GHC.Exts (IsList (..), inline, sortWith)
@@ -64,6 +63,7 @@ import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeab
6463import Type.Reflection.Unsafe (typeRepFingerprint)
6564import Unsafe.Coerce (unsafeCoerce)
6665
66+import qualified Data.Map.Strict as Map
6767import qualified GHC.Exts as GHC (fromList, toList)
6868
6969{- |
@@ -169,49 +169,33 @@ prop> member @a (one (x :: f a)) == True
169169
170170-}
171171one :: forall a f . Typeable a => f a -> TypeRepMap f
172−one x = TypeRepMap (primArrayFromListN 1 [fa])
173− (primArrayFromListN 1 [fb])
174− (pure @Array v)
175− (pure @Array k)
176− where
177− (Fingerprint fa fb, v, k) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
172+one x = insert x empty
178173{-# INLINE one #-}
179174
180175{- |
181176
182177Insert a value into a 'TypeRepMap'.
183−TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
184178
185179prop> size (insert v tm) >= size tm
186180prop> member @a (insert (x :: f a) tm) == True
187181
188182-}
189183insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
190−insert x m
191− | size m == 0 = one x
192− | otherwise = case cachedBinarySearch (typeFp @a) (fingerprintAs m) (fingerprintBs m) of
193− Nothing -> union m $ one x
194− Just i -> m {trAnys = changeAnyArr i (trAnys m)}
184+insert x = fromTriples . addX . toTriples
195185 where
196− changeAnyArr :: Int -> Array Any -> Array Any
197− changeAnyArr i trAs = runST $ do
198− let n = sizeofArray trAs
199− mutArr <- thawArray trAs 0 n
200− writeArray mutArr i $ toAny x
201− unsafeFreezeArray mutArr
186+ tripleX :: (Fingerprint, Any, Any)
187+ tripleX@(fpX, _, _) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
188+
189+ addX :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
190+ addX l = tripleX : deleteByFst fpX l
202191{-# INLINE insert #-}
203192
204193-- Extract the kind of a type. We use it to work around lack of syntax for
205194-- inferred type variables (which are not subject to type applications).
206195type KindOf (a :: k) = k
207196
208−type ArgKindOf (f :: k -> l) = k
209−
210197{- | Delete a value from a 'TypeRepMap'.
211198
212−TypeRepMap optimizes for fast reads rather than modifications, as a trade-off deletes are
213−@O(n)@, with an @O(log(n))@ optimization for when the element is already missing.
214−
215199prop> size (delete @a tm) <= size tm
216200prop> member @a (delete @a tm) == False
217201
@@ -224,60 +208,30 @@ False
224208True
225209-}
226210delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
227−delete m
228− -- Lookups are fast, so check if we even have the element first.
229− | not (member @a m) = m
230− -- We know we have the element, If the map has exactly one element, we can return the empty map
231− | size m == 1 = empty
232− -- Otherwise, filter out the element in linear time.
233− | otherwise = fromSortedTriples . deleteFirst ((== typeFp @a) . fst3) . toSortedTriples $ m
211+delete = fromTriples . deleteByFst (typeFp @a) . toTriples
234212{-# INLINE delete #-}
235213
236−deleteFirst :: (a -> Bool) -> [a] -> [a]
237−deleteFirst _ [] = []
238−deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239−
240214{- |
241−Update a value at a specific key with the result of the provided function.
242−When the key is not a member of the map, the original map is returned.
215+Update a value at a specific key with the result of the provided function. When
216+the key is not a member of the map, the original map is returned.
243217
244218>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
245219>>> lookup @String $ adjust (fmap (++ "ww")) trmap
246220Just (Identity "aww")
247221-}
248222adjust :: forall a f . Typeable a => (f a -> f a) -> TypeRepMap f -> TypeRepMap f
249−adjust fun = alter (fmap fun)
250−{-# INLINE adjust #-}
251−
252−{- |
253−Updates a value at a specific key, whether or not it exists.
254−This can be used to insert, delete, or update a value of a given type in the map.
255−
256−>>> func = (\case Nothing -> Just (Identity "new"); Just (Identity s) -> Just (Identity (reverse s)))
257−>>> lookup @String $ alter func empty
258−Just (Identity "new")
259−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "helllo"]
260−>>> lookup @String $ alter func trmap
261−>>> Just (Identity "olleh")
262−-}
263−alter :: forall a f . Typeable a => (Maybe (f a) -> Maybe (f a)) -> TypeRepMap f -> TypeRepMap f
264−alter fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
265− Nothing ->
266− case (fun Nothing) of
267− Nothing -> tr
268− Just v -> insert v tr
269− Just i ->
270− case fun (Just . fromAny $ indexArray (trAnys tr) i) of
271− Nothing -> delete @a tr
272− Just v -> tr{trAnys = replaceAnyAt i (toAny v) (trAnys tr)}
223+adjust fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
224+ Nothing -> tr
225+ Just i -> tr {trAnys = changeAnyArr i (trAnys tr)}
273226 where
274− replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275− replaceAnyAt i v trAs = runST $ do
227+ changeAnyArr :: Int -> Array Any -> Array Any
228+ changeAnyArr i trAs = runST $ do
276229 let n = sizeofArray trAs
277230 mutArr <- thawArray trAs 0 n
278− writeArray mutArr i v
231+ a <- toAny . fun . fromAny <$> readArray mutArr i
232+ writeArray mutArr i a
279233 unsafeFreezeArray mutArr
280−{-# INLINE alter #-}
234+{-# INLINE adjust #-}
281235
282236{- | Map over the elements of a 'TypeRepMap'.
283237
@@ -311,89 +265,33 @@ hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
311265 withTr t = withTypeable t f
312266{-# INLINE hoistWithKey #-}
313267
314−-- | The union of two 'TypeRepMap's using a combining function for conflicting entries. @O(n + m)@
268+-- | The union of two 'TypeRepMap's using a combining function.
315269unionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
316−unionWith f ma mb = do
317− fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
270+unionWith f m1 m2 = fromTriples
271+ $ toTripleList
272+ $ Map.unionWith combine
273+ (fromTripleList $ toTriples m1)
274+ (fromTripleList $ toTriples m2)
318275 where
319276 f' :: forall x. TypeRep x -> f x -> f x -> f x
320277 f' tr = withTypeable tr f
321278
322− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
323− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
324−
325− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
326− -- Using 'toSortedTriples' allows us to assume the triples are sorted by fingerprint,
327− -- Given O(n) performance from 'toSortedTriples', and given that we can merge-sort in
328− -- O(n + m) time, then can '.fromSortedTriples' back into cachedBinarySearch order in O(n + m)
329− -- that gives a total of O(n + m).
330− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
331− -- We've addressed all elements from both maps
332− mergeMaps as [] = as
333− mergeMaps [] bs = bs
334− -- Merge
335− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
336− case compare af bf of
337− -- Fingerprints are equal, union the elements using our function
338− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
339− -- fingerprints
340− EQ -> combine a b : mergeMaps as bs
341− -- First fingerprint must not be in the second map or we would have seen it by now
342− -- Add it to the result as-is
343− LT -> a : mergeMaps as (b : bs)
344− -- Second fingerprint must not be in the first map or we would have seen it by now
345− -- Add it to the result as-is
346− GT -> b : mergeMaps (a:as) bs
279+ combine :: (Any, Any) -> (Any, Any) -> (Any, Any)
280+ combine (av, ak) (bv, _) = (toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
281+
282+ fromTripleList :: Ord a => [(a, b, c)] -> Map.Map a (b, c)
283+ fromTripleList = Map.fromList . map (\(a, b, c) -> (a, (b, c)))
284+
285+ toTripleList :: Map.Map a (b, c) -> [(a, b, c)]
286+ toTripleList = map (\(a, (b, c)) -> (a, b, c)) . Map.toList
347287{-# INLINE unionWith #-}
348288
349−-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
289+-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
350290-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
351291union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352292union = unionWith const
353293{-# INLINE union #-}
354294
355−-- | The 'intersection' of two 'TypeRepMap's using a combining function
356−--
357−-- @O(n + m)@
358−intersectionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
359−intersectionWith f ma mb =
360− fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
361− where
362− f' :: forall x. TypeRep x -> f x -> f x -> f x
363− f' tr = withTypeable tr f
364−
365− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
366− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
367−
368− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
369− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
370− -- If either list is empty, the intersection must be finished.
371− mergeMaps _ [] = []
372− mergeMaps [] _ = []
373− -- Merge the two maps considering one element at a time.
374− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
375− case compare af bf of
376− -- Fingerprints are equal, union the elements using our function
377− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
378− -- fingerprints
379− EQ -> combine a b : mergeMaps as bs
380− -- First fingerprint must not be in the second map or we would have seen it by now
381− -- Skip it an move on
382− LT -> mergeMaps as (b : bs)
383− -- Second fingerprint must not be in the first map or we would have seen it by now
384− -- Skip it an move on
385− GT -> mergeMaps (a:as) bs
386−{-# INLINE intersectionWith #-}
387−
388−-- | The intersection of two 'TypeRepMap's.
389−-- It keeps all values from the first map whose keys are present in the second.
390−--
391−-- @O(n + m)@
392−intersection :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
393−intersection = intersectionWith const
394−{-# INLINE intersection #-}
395−
396−
397295{- | Check if a value of the given type is present in a 'TypeRepMap'.
398296
399297>>> member @Char $ one (Identity 'a')
@@ -429,22 +327,9 @@ size = sizeofPrimArray . fingerprintAs
429327
430328-- | Return the list of 'SomeTypeRep' from the keys.
431329keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
330+keys TypeRepMap{..} = SomeTypeRep . anyToTypeRep <$> toList trKeys
433331{-# INLINE keys #-}
434332
435−-- | Return the list of keys by wrapping them with a user-provided function.
436−keysWith :: (forall (a :: ArgKindOf f). TypeRep a -> r) -> TypeRepMap f -> [r]
437−keysWith f TypeRepMap{..} = f . anyToTypeRep <$> toList trKeys
438−{-# INLINE keysWith #-}
439−
440−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
441−toListWith :: forall f r. (forall (a :: ArgKindOf f). Typeable a => f a -> r) -> TypeRepMap f -> [r]
442−toListWith f = map toF . toTriples
443− where
444− withTypeRep :: TypeRep a -> f a -> r
445− withTypeRep tr an = withTypeable tr $ f an
446− toF (_, an, k) = withTypeRep (unsafeCoerce k) (fromAny an)
447−
448333-- | Binary searched based on this article
449334-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450335-- with modification for our two-vector search case.
@@ -488,20 +373,8 @@ typeFp = typeRepFingerprint $ typeRep @a
488373toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
489374toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
490375
491−-- | Efficiently get sorted triples from a map in O(n) time
492−--
493−-- We assume the incoming TypeRepMap is already sorted into 'cachedBinarySearch' order using fromSortedList.
494−-- Then we can construct the index mapping from the "cached" ordering into monotonically
495−-- increasing order using 'generateOrderMapping' with the length of the TRM. This takes @O(n).
496−-- We then pull those indexes from the source TRM to get the sorted triples in a total of @O(n).
497−toSortedTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
498−toSortedTriples tm = trip <$> ordering
499− where
500− trip i = ( Fingerprint (indexPrimArray (fingerprintAs tm) i) (indexPrimArray (fingerprintBs tm) i)
501− , indexArray (trAnys tm) i
502− , indexArray (trKeys tm) i)
503− ordering :: [ Int ]
504− ordering = generateOrderMapping (size tm)
376+deleteByFst :: Eq a => a -> [(a, b, c)] -> [(a, b, c)]
377+deleteByFst x = filter ((/= x) . fst3)
505378
506379nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
507380nubByFst = nubBy ((==) `on` fst3)
@@ -521,6 +394,9 @@ data WrapTypeable f where
521394instance Show (WrapTypeable f) where
522395 show (WrapTypeable (_ :: f a)) = show $ calcFp @a
523396
397+wrapTypeable :: TypeRep a -> f a -> WrapTypeable f
398+wrapTypeable tr = withTypeable tr WrapTypeable
399+
524400{- |
525401
526402prop> fromList . toList == 'id'
@@ -548,19 +424,19 @@ instance IsList (TypeRepMap f) where
548424 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549425
550426 toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
427+ toList = map toWrapTypeable . toTriples
428+ where
429+ toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
430+ toWrapTypeable (_, an, k) = wrapTypeable (unsafeCoerce k) (fromAny an)
552431
553432calcFp :: forall a . Typeable a => Fingerprint
554433calcFp = typeRepFingerprint $ typeRep @a
555434
556435fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
557−fromTriples = fromSortedTriples . sortWith fst3 . nubByFst
558−
559−fromSortedTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
560−fromSortedTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
436+fromTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
561437 where
562438 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
563− (fps, ans, ks) = unzip3 $ fromSortedList kvs
439+ (fps, ans, ks) = unzip3 $ fromSortedList $ sortWith fst3 $ nubByFst kvs
564440
565441----------------------------------------------------------------------------
566442-- Tree-like conversion
@@ -587,35 +463,11 @@ fromSortedList l = runST $ do
587463 writeArray result i (indexArray origin newFirst)
588464 loop (2 * i + 2) (newFirst + 1)
589465
590−-- Returns a list of indexes which represents the "sorted" order of an array generated by
591−-- fromSortedList of the provided length.
592−-- I.e. fmap (fromSortedList [1, 2, 3, 4, 5, 6] !!) (generateOrderMapping 6) == [1, 2, 3, 4, 5, 6]
593−--
594−-- >>> generateOrderMapping 6
595−-- [3,1,4,0,5,2]
596−--
597−-- >>> generateOrderMapping 8
598−-- [7,3,1,4,0,5,2,6]
599−generateOrderMapping :: Int -> [Int]
600−generateOrderMapping len = runST $ do
601− orderMappingArr <- newPrimArray len
602− _ <- loop orderMappingArr 0 0
603− primArrayToList <$> unsafeFreezePrimArray orderMappingArr
604− where
605− loop :: MutablePrimArray s Int -> Int -> Int -> ST s Int
606− loop result i first =
607− if i >= len
608− then pure first
609− else do
610− newFirst <- loop result (2 * i + 1) first
611− writePrimArray result newFirst i
612− loop result (2 * i + 2) (newFirst + 1)
613−
614466----------------------------------------------------------------------------
615467-- Helper functions.
616468----------------------------------------------------------------------------
617469
618−-- | Check that invariant of the structure holds.
470+-- | Check that invariant of the structure is hold.
619471-- The structure maintains the following invariant.
620472-- For each element @A@ at index @i@:
621473--
test/Test/TypeRep/TypeRepMap.hs+4−62
@@ -5,18 +5,14 @@ module Test.TypeRep.TypeRepMap
55import Prelude hiding (lookup)
66
77import Data.Functor.Identity (Identity (..))
8−import Data.List (sortBy)
9−import Data.Ord (comparing)
10−import Data.Typeable (cast)
11−import GHC.Exts (fromList, toList)
12−import Type.Reflection (SomeTypeRep(..), typeRep)
13−import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList, shouldSatisfy, expectationFailure)
8+import GHC.Exts (fromList)
9+import Test.Hspec (Spec, describe, it, shouldBe)
1410
15−import Data.TMap (TMap, empty, insert, lookup, one, size, union, keys)
11+import Data.TMap (TMap, empty, insert, lookup, one, size, union)
1612import Data.TypeRepMap.Internal (WrapTypeable (..))
1713
1814
19−-- Simple test for 'lookup', 'insert', 'size', 'keys', 'toList' functions.
15+-- Simple test for 'lookup', 'insert' and 'size' functions.
2016typeRepMapSpec :: Spec
2117typeRepMapSpec = describe "TypeRepMap" $ do
2218 describe "Lookup Test" $ do
@@ -43,60 +39,6 @@ typeRepMapSpec = describe "TypeRepMap" $ do
4339 lookup m `shouldBe` Just True
4440 lookup @Int m `shouldBe` Nothing
4541
46− describe "Keys Test" $ do
47− it "returns nothing on empty map" $
48− keys empty `shouldBe` []
49− it "returns the correct TypeRep" $
50− keys (one 'a') `shouldBe` [SomeTypeRep $ typeRep @Char]
51− it "returns the correct TypeReps for 10 different types" $
52− keys mapOf10 `shouldMatchList`
53− [ SomeTypeRep $ typeRep @Bool
54− , SomeTypeRep $ typeRep @[Bool]
55− , SomeTypeRep $ typeRep @(Maybe Bool)
56− , SomeTypeRep $ typeRep @(Maybe ())
57− , SomeTypeRep $ typeRep @[()]
58− , SomeTypeRep $ typeRep @()
59− , SomeTypeRep $ typeRep @String
60− , SomeTypeRep $ typeRep @(Maybe Char)
61− , SomeTypeRep $ typeRep @Char
62− , SomeTypeRep $ typeRep @Int
63− ]
64−
65− describe "ToList Test" $ do
66− it "returns nothing on empty map" $
67− toList empty `shouldSatisfy` null
68− it "returns correct result when 1 element is inserted" $
69− case toList (one 'a') of
70− [WrapTypeable (Identity x)] -> cast x `shouldBe` Just 'a'
71− _ -> expectationFailure "did not return exactly 1 result"
72− it "returns correct result when 10 elements are inserted" $ do
73− let
74− getTypeRep (WrapTypeable (Identity (_ :: a))) = SomeTypeRep $ typeRep @a
75− got = sortBy (comparing getTypeRep) (toList mapOf10)
76− expected = sortBy (comparing fst)
77− [ (SomeTypeRep $ typeRep @Bool,
78− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just True)
79− , (SomeTypeRep $ typeRep @[Bool],
80− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [True, False])
81− , (SomeTypeRep $ typeRep @(Maybe Bool),
82− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just True))
83− , (SomeTypeRep $ typeRep @(Maybe ()),
84− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just ()))
85− , (SomeTypeRep $ typeRep @[()],
86− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [()])
87− , (SomeTypeRep $ typeRep @(),
88− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ())
89− , (SomeTypeRep $ typeRep @String,
90− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ("aaa" :: String))
91− , (SomeTypeRep $ typeRep @(Maybe Char),
92− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just 'a'))
93− , (SomeTypeRep $ typeRep @Char,
94− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just 'a')
95− , (SomeTypeRep $ typeRep @Int,
96− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (11 :: Int))
97− ]
98− length got `shouldBe` 10
99− sequence_ $ zipWith snd expected got
10042
10143mapOf10 :: TMap
10244mapOf10 = insert True
test/Test/TypeRep/TypeRepMapProperty.hs+29−70
@@ -1,6 +1,7 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DataKinds #-}
33{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE KindSignatures #-}
45{-# LANGUAGE PolyKinds #-}
56
67{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
@@ -14,14 +15,14 @@ import Prelude hiding (lookup)
1415import Data.Proxy (Proxy (..))
1516import Data.Semigroup (Semigroup (..))
1617import GHC.Exts (fromList)
18+import GHC.Stack (HasCallStack)
1719import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
18−import Hedgehog (MonadGen, assert, forAll, (===))
20+import Hedgehog (MonadGen, PropertyT, assert, forAll, property, (===))
1921import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
2022import Test.Hspec.Hedgehog (hedgehog)
2123
2224import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
23− lookup, member, union, generateOrderMapping, fromSortedList,
24− adjust, alter, intersection)
25+ lookup, member)
2526
2627import qualified Hedgehog.Gen as Gen
2728import qualified Hedgehog.Range as Range
@@ -35,13 +36,6 @@ typeRepMapPropertySpec = describe "TypeRepMap Property tests" $ do
3536 deleteMemberSpec
3637 insertInvariantSpec
3738 deleteInvariantSpec
38− alterInsertSpec
39− alterDeleteSpec
40− alterAdjustSpec
41− alterModifySpec
42− intersectionSpec
43− describe "Internal helpers" $ do
44− generateOrderMappingInvariantSpec
4539 describe "Instance Laws" $ do
4640 semigroupAssocSpec
4741 monoidIdentitySpec
@@ -88,66 +82,45 @@ deleteInvariantSpec = it "invariantCheck (delete k b) == True" $ hedgehog $ do
8882 WrapTypeable (_ :: IntProxy n) <- forAll genTF
8983 assert $ invariantCheck (delete @n m)
9084
91−alterInsertSpec :: Property
92−alterInsertSpec = it "insert proxy m === alter (const (Just proxy)) m" $ hedgehog $ do
93− m <- forAll genMap
94− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
95− insert proxy m === alter (const (Just proxy)) m
96−
97−alterDeleteSpec :: Property
98−alterDeleteSpec = it "delete proxy m === alter (const Nothing) m" $ hedgehog $ do
99− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
100− m <- insert proxy <$> forAll genMap
101− delete @n @IntProxy m === alter @n @IntProxy (const Nothing) m
102−
103−alterAdjustSpec :: Property
104−alterAdjustSpec = it "adjust f m == alter (fmap f) m" $ hedgehog $ do
105− m <- forAll genMap
106− WrapTypeable (_ :: IntProxy n) <- forAll genTF
107− let f (IntProxy p n) = IntProxy p (n * 10)
108− adjust @n @IntProxy f m === alter @n @IntProxy (fmap f) m
109−
110−alterModifySpec :: Property
111−alterModifySpec = it "lookup k (alter f) == f (lookup k m)" $ hedgehog $ do
112− m <- forAll genMap
113− WrapTypeable (_ :: IntProxy n) <- forAll genTF
114− randInt <- forAll (Gen.int Range.constantBounded)
115− -- Function with some interesting behaviour, which inserts, seletes and modifies
116− let f Nothing = Just (IntProxy Proxy randInt)
117− f (Just (IntProxy p n))
118− | even n = Nothing
119− | otherwise = Just $ IntProxy p (n * 10)
120− lookup @n @IntProxy (alter @n f m) === f (lookup @n @IntProxy m)
121−
122−intersectionSpec :: Property
123−intersectionSpec = it "m `intersection` (m `union` n) == m" $ hedgehog $ do
124− m <- forAll genMap
125− n <- forAll genMap
126− m `intersection` (m `union` n) === m
127−
128−----------------------------------------------------------------------------
129−-- Internal helpers
130−----------------------------------------------------------------------------
131−generateOrderMappingInvariantSpec :: Property
132−generateOrderMappingInvariantSpec =
133− it "fmap (fromSortedList [1 .. n] !!) (generateOrderMapping n) == [1 .. n]" $ hedgehog $ do
134− n <- forAll $ Gen.int (Range.linear 0 100)
135− fmap (fromSortedList [1 .. n] !!) (generateOrderMapping n) === [1 .. n]
136−
13785----------------------------------------------------------------------------
13886-- Semigroup and Monoid laws
13987----------------------------------------------------------------------------
14088
89+#if __GLASGOW_HASKELL__ < 806
90+{- | This newtype is used to compare 'TypeRepMap's using only 'Fingerprint's.
91+It's not a good idea to write such 'Eq' instance for 'TypeRepMap' itself because
92+it doesn't compare values so it's not true equality. But this should be enough
93+for tests.
94+-}
95+newtype FpMap f = FpMap (TypeRepMap f)
96+ deriving newtype (Show, Semigroup, Monoid)
97+
98+instance Eq (FpMap f) where
99+ FpMap (TypeRepMap as1 bs1 _ _) == FpMap (TypeRepMap as2 bs2 _ _) =
100+ as1 == as2 && bs1 == bs2
101+#endif
102+
141103semigroupAssocSpec :: Property
142104semigroupAssocSpec = it "x <> (y <> z) == (x <> y) <> z" $ hedgehog $ do
105+#if __GLASGOW_HASKELL__ >= 806
143106 x <- forAll genMap
144107 y <- forAll genMap
145108 z <- forAll genMap
109+#else
110+ x <- FpMap <$> forAll genMap
111+ y <- FpMap <$> forAll genMap
112+ z <- FpMap <$> forAll genMap
113+#endif
146114 (x <> (y <> z)) === ((x <> y) <> z)
147115
148116monoidIdentitySpec :: Property
149117monoidIdentitySpec = it "x <> mempty == mempty <> x == x" $ hedgehog $ do
118+#if __GLASGOW_HASKELL__ >= 806
150119 x <- forAll genMap
120+#else
121+ x <- FpMap <$> forAll genMap
122+#endif
123+
151124 x <> mempty === x
152125 mempty <> x === x
153126
@@ -168,17 +141,3 @@ genTF = do
168141 case someNatVal randNat of
169142 Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
170143 Nothing -> error "Invalid test generator"
171−
172−----------------------------------------------------------------------------
173−-- Helpers
174−----------------------------------------------------------------------------
175−#if __GLASGOW_HASKELL__ < 806
176−{- | We add an orphan Eq instance for old GHC versions just to make testing easier.
177−It's not a good idea to write such 'Eq' instance for 'TypeRepMap' itself because
178−it doesn't compare values so it's not true equality. But this should be enough
179−for tests.
180−-}
181−instance Eq (TypeRepMap f) where
182− TypeRepMap as1 bs1 _ _ == TypeRepMap as2 bs2 _ _ =
183− as1 == as2 && bs1 == bs2
184−#endif
typerep-extra-impls/Data/TypeRep/CMap.hs+3−3
@@ -1,4 +1,5 @@
1−{-# LANGUAGE PolyKinds #-}
1+{-# LANGUAGE KindSignatures #-}
2+{-# LANGUAGE PolyKinds #-}
23
34{- |
45Copyright: (c) 2017-2020 Kowainik
@@ -20,7 +21,6 @@ module Data.TypeRep.CMap
2021import Prelude hiding (lookup)
2122
2223import Control.DeepSeq
23−import Data.Kind (Type)
2424import Data.Proxy (Proxy (..))
2525import Data.Typeable (TypeRep, Typeable, typeRep)
2626import GHC.Base (Any)
@@ -30,7 +30,7 @@ import qualified Data.Map.Lazy as LMap
3030
3131
3232-- | Map-like data structure with types served as the keys.
33−newtype TypeRepMap (f :: k -> Type) = TypeRepMap
33+newtype TypeRepMap (f :: k -> *) = TypeRepMap
3434 { unMap :: LMap.Map TypeRep Any
3535 }
3636
typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−7
@@ -1,8 +1,9 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE TypeFamilies #-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE MagicHash #-}
5+{-# LANGUAGE PolyKinds #-}
6+{-# LANGUAGE TypeFamilies #-}
67
78{- |
89Copyright: (c) 2017-2020 Kowainik
@@ -61,7 +62,7 @@ empty :: TypeRepMap f
6162empty = TypeRepMap mempty mempty mempty
6263
6364-- | Inserts the value with its type as a key.
64−insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
65+insert :: forall a f . Typeable a => a -> TypeRepMap f -> TypeRepMap f
6566insert = undefined
6667
6768-- | Looks up the value at the type.
@@ -111,7 +112,7 @@ binarySearch (Fingerprint a b) fpAs fpBs =
111112data TF f where
112113 TF :: Typeable a => f a -> TF f
113114
114−fromF :: f a -> Proxy a
115+fromF :: Typeable a => f a -> Proxy a
115116fromF _ = Proxy
116117
117118fromList :: forall f . [TF f] -> TypeRepMap f
typerep-extra-impls/Data/TypeRep/Vector.hs+2−2
@@ -144,7 +144,7 @@ empty :: TypeRepVector f
144144empty = TypeRepVect mempty mempty
145145
146146-- | Inserts the value with its type as a key.
147−insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
147+insert :: forall a f . Typeable a => a -> TypeRepVector f -> TypeRepVector f
148148insert = undefined
149149
150150-- | Looks up the value at the type.
@@ -164,7 +164,7 @@ size = Unboxed.length . fingerprints
164164data TF f where
165165 TF :: Typeable a => f a -> TF f
166166
167−fromF :: f a -> Proxy a
167+fromF :: Typeable a => f a -> Proxy a
168168fromF _ = Proxy
169169
170170fromList :: forall f . [TF f] -> TypeRepVector f
typerep-map.cabal+3−17
@@ -48,23 +48,8 @@ common common-options
4848 RecordWildCards
4949 ScopedTypeVariables
5050 TypeApplications
51− ghc-options: -Wall
52− -Wcompat
53− -Widentities
54− -Wincomplete-uni-patterns
55− -Wincomplete-record-updates
56− if impl(ghc >= 8.0)
57− ghc-options: -Wredundant-constraints
58− if impl(ghc >= 8.2)
59− ghc-options: -fhide-source-paths
60− if impl(ghc >= 8.4)
61− ghc-options: -Wmissing-export-lists
62− -Wpartial-fields
63− if impl(ghc >= 8.8)
51+ if impl(ghc >= 8.8.1)
6452 ghc-options: -Wmissing-deriving-strategies
65− -Werror=missing-deriving-strategies
66− if impl(ghc >= 8.10)
67− ghc-options: -Wunused-packages
6853
6954library
7055 import: common-options
@@ -73,7 +58,8 @@ library
7358 Data.TypeRepMap
7459 Data.TypeRepMap.Internal
7560
76− build-depends: ghc-prim >= 0.5.1.1 && < 0.7
61+ build-depends: containers >= 0.5.10.2 && < 0.7
62+ , ghc-prim >= 0.5.1.1 && < 0.7
7763 , primitive ^>= 0.7.0
7864 , deepseq ^>= 1.4
7965