on main help sign in
.github/workflows/ci.yml+3−4
since splitsnapshots
unifiedsplit
@@ -1,11 +1,10 @@
1name: CI
1name: CI
2
2
3+# Trigger the workflow on push or pull request, but only for the main branch
3on:
4on:
4− # Trigger the workflow on push or pull request,
5− # but only for the master branch
6 push:
5 push:
7 branches:
6 branches:
8− - master
7+ - main
9 pull_request:
8 pull_request:
10
9
11jobs:
10jobs:
@@ -24,7 +23,7 @@ jobs:
24
23
25 steps:
24 steps:
26 - uses: actions/checkout@v2
25 - uses: actions/checkout@v2
27− if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'
26+ if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/main'
28
27
29 - uses: actions/setup-haskell@v1
28 - uses: actions/setup-haskell@v1
30 name: Setup Haskell
29 name: Setup Haskell
.travis.yml+10−28
@@ -1,46 +1,28 @@
1sudo: true
1sudo: true
2−language: haskell
2+language: generic
3
3
4git:
4git:
5 depth: 5
5 depth: 5
6
6
7−cabal: "3.0"
8−
9cache:
7cache:
10 directories:
8 directories:
11− - "$HOME/.cabal/store"
12 - "$HOME/.stack"
9 - "$HOME/.stack"
13− - "$TRAVIS_BUILD_DIR/.stack-work"
14
10
15matrix:
11matrix:
16 include:
12 include:
17− - ghc: 8.2.2
18− - ghc: 8.4.4
19− - ghc: 8.6.5
20− - ghc: 8.8.3
13+ - env: GHC='8.8.3'
21− - ghc: 8.10.1
14+ os: linux
22−
23− - ghc: 8.8.3
15+ - env: GHC='8.8.3'
24− env: STACK_YAML="$TRAVIS_BUILD_DIR/stack.yaml"
16+ os: osx
25
17
26install:
18install:
27− - |
19+ # install stack and build project
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
20+ - curl -sSL https://get.haskellstack.org/ | sh
33− stack --version
21+ - stack --version
34− stack build --system-ghc --test --no-run-tests
22+ - stack build --test --no-run-tests --bench --no-run-benchmarks
35− fi
36
23
37script:
24script:
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
25+ - stack test --no-terminal
43− fi
44
26
45notifications:
27notifications:
46 email: false
28 email: false
CHANGELOG.md+1−1
@@ -53,4 +53,4 @@ The changelog is available [on GitHub][2].
53* Initially created.
53* Initially created.
54
54
55[1]: https://pvp.haskell.org
55[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
56+[2]: https://github.com/kowainik/typerep-map/releases
README.md+13−7
@@ -1,15 +1,21 @@
1# typerep-map
1# typerep-map
2
2
3−![electricity](https://user-images.githubusercontent.com/8126674/44323413-788dd700-a484-11e8-842e-f224cfaa4206.png)
3+![logo](https://user-images.githubusercontent.com/8126674/44323413-788dd700-a484-11e8-842e-f224cfaa4206.png)
4+
5+[![GitHub CI](https://github.com/kowainik/typerep-map/workflows/CI/badge.svg)](https://github.com/kowainik/typerep-map/actions)
4[![Build status](https://img.shields.io/travis/kowainik/typerep-map.svg?logo=travis)](https://travis-ci.org/kowainik/typerep-map)
6[![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)
5[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
8[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
6[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
9[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
7[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
10[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
8−[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
11+[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE)
12+
9
13
10`typerep-map` introduces `TMap` and `TypeRepMap` — data structures 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.
14`typerep-map` introduces `TMap` and `TypeRepMap` — data structures 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.
11
15
16+For the more details on the implementation see the following blog post:
17+
12−For the more details on the implementation see [this blog post](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step).
18+* [typerep-map step by step](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step)
13
19
14## Usage example
20## Usage example
15
21
@@ -49,10 +55,10 @@ False
49Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
55Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
50on structure with size `10^4`:
56on structure with size `10^4`:
51
57
52−| | ghc-8.2.2 | ghc-8.4.3 |
58+| | ghc-8.2.2 | ghc-8.4.3 | ghc-8.8.3 | ghc-8.10.1 |
53−|----------------|-----------|-----------|
59+|----------------|-----------|-----------|-----------|------------|
54−| `DMap TypeRep` | 517.5 ns | 779.9 ns |
60+| `DMap TypeRep` | 517.5 ns | 779.9 ns | 1.559 μs | 1.786 μs |
55−| `typerep-map` | 205.3 ns | 187.2 ns |
61+| `typerep-map` | 205.3 ns | 187.2 ns | 190.1 ns | 169.1 ns |
56
62
57 ghc-8.2.2 | ghc-8.4.3
63 ghc-8.2.2 | ghc-8.4.3
58:---------:|:-----------:
64:---------:|:-----------:
appveyor.ymlnew file+35
@@ -0,0 +1,35 @@
1+clone_folder: "c:\\WORK"
2+clone_depth: 5
3+
4+# Do not build feature branch with open Pull Requests
5+skip_branch_with_pr: true
6+
7+platform:
8+ - x86
9+ - x86_64
10+
11+cache:
12+ - "C:\\SR"
13+ - dist-newstyle
14+
15+environment:
16+ global:
17+ CABOPTS: --store-dir=C:\\SR
18+
19+ matrix:
20+ - GHCVER: 8.8.3
21+
22+install:
23+ - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2
24+ - choco install -y cabal --version 3.0.0.0
25+ - choco install -y ghc --version 8.8.3
26+ - refreshenv
27+
28+before_build:
29+ - cabal --version
30+ - ghc --version
31+ - cabal %CABOPTS% update
32+
33+build_script:
34+ - cabal %CABOPTS% build --enable-tests --write-ghc-environment-files=always
35+ - cabal %CABOPTS% test --enable-tests
benchmark/CMap.hs+46−39
@@ -1,70 +1,77 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
3−{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
8
8
9−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10−
11module CMap
9module CMap
12− ( spec
10+ ( spec
13− ) where
11+ ) where
14−
15−import Criterion.Main (bench, nf, env, whnf)
16
12
17import Prelude hiding (lookup)
13import Prelude hiding (lookup)
18
14
19−import Spec
15+import Criterion.Main (bench, env, nf, whnf)
16+import Data.Kind (Type)
20import Data.Maybe (fromJust)
17import Data.Maybe (fromJust)
21import Data.Proxy (Proxy (..))
18import Data.Proxy (Proxy (..))
22import Data.Typeable (Typeable)
19import Data.Typeable (Typeable)
23−import GHC.TypeLits
20+import GHC.TypeLits (type (+), KnownNat, Nat)
24
21
25import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
22import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
26
23
24+import Spec (BenchSpec (..))
25+
26+
27spec :: BenchSpec
27spec :: BenchSpec
28spec = BenchSpec
28spec = BenchSpec
29− { benchLookup = Just $ \name ->
29+ { benchLookup = Just $ \name ->
30− env (mkMap 10000) $ \ ~bigMap ->
30+ env (mkMap 10000) $ \ ~bigMap ->
31− bench name $ nf tenLookups bigMap
31+ bench name $ nf tenLookups bigMap
32− , benchInsertSmall = Just $ \name ->
32+ , benchInsertSmall = Just $ \name ->
33− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
33+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
34− , benchInsertBig = Just $ \name ->
34+ , benchInsertBig = Just $ \name ->
35− env (mkMap 10000) $ \ ~(bigMap) ->
35+ env (mkMap 10000) $ \ ~bigMap ->
36− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
36+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
37− , benchUpdateSmall = Just $ \name ->
37+ , benchUpdateSmall = Just $ \name ->
38− env (mkMap 10) $ \ ~(smallMap) ->
38+ env (mkMap 10) $ \ ~smallMap ->
39− bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
39+ bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
40− , benchUpdateBig = Just $ \name ->
40+ , benchUpdateBig = Just $ \name ->
41− env (mkMap 10000) $ \ ~(bigMap) ->
41+ env (mkMap 10000) $ \ ~bigMap ->
42− bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
42+ bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
43− }
43+ }
44
44
45+tenLookups
45−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
46+ :: TypeRepMap (Proxy :: Nat -> Type)
46− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
47+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
47− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
48+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
48− )
49+ )
49tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
50tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
50 where
51 where
51 lp :: forall (a::Nat). Typeable a => Proxy a
52 lp :: forall (a::Nat). Typeable a => Proxy a
52 lp = fromJust $ lookup tmap
53 lp = fromJust $ lookup tmap
53
54
55+inserts
54−inserts :: forall a . (KnownNat a)
56+ :: forall a . (KnownNat a)
55− => TypeRepMap (Proxy :: Nat -> *)
57+ => TypeRepMap (Proxy :: Nat -> Type)
56− -> Int
58+ -> Int
57− -> Proxy (a :: Nat)
59+ -> Proxy (a :: Nat)
58− -> TypeRepMap (Proxy :: Nat -> *)
60+ -> TypeRepMap (Proxy :: Nat -> Type)
59inserts !c 0 _ = c
61inserts !c 0 _ = c
60inserts !c n x = inserts
62inserts !c n x = inserts
61− (insert x c)
63+ (insert x c)
62− (n-1)
64+ (n-1)
63− (Proxy :: Proxy (a+1))
65+ (Proxy :: Proxy (a+1))
64
66
65−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> *))
67+mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
66mkMap n = pure $ buildBigMap n (Proxy :: Proxy 0) empty
68mkMap n = pure $ buildBigMap n (Proxy :: Proxy 0) empty
67
69
68−buildBigMap :: forall a . (KnownNat a) => Int -> Proxy (a :: Nat) -> TypeRepMap (Proxy :: Nat -> *) -> TypeRepMap (Proxy :: Nat -> *)
70+buildBigMap
71+ :: forall a . (KnownNat a)
72+ => Int
73+ -> Proxy (a :: Nat)
74+ -> TypeRepMap (Proxy :: Nat -> Type)
75+ -> TypeRepMap (Proxy :: Nat -> Type)
69buildBigMap 1 x = insert x
76buildBigMap 1 x = insert x
70buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
77buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/CacheMap.hs+48−45
@@ -1,76 +1,79 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
3−{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeOperators #-}
6{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
8
8
9−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10−
11module CacheMap
9module CacheMap
12− ( spec
10+ ( spec
13− ) where
11+ ) where
14−
15−import Criterion.Main (bench, nf, whnf, env)
16−import Spec
17
12
18import Prelude hiding (lookup)
13import Prelude hiding (lookup)
19
14
15+import Criterion.Main (bench, env, nf, whnf)
16+import Data.Kind (Type)
20import Data.Maybe (fromJust)
17import Data.Maybe (fromJust)
21import Data.Proxy (Proxy (..))
18import Data.Proxy (Proxy (..))
22import Data.Typeable (Typeable)
19import Data.Typeable (Typeable)
23import GHC.Exts (fromList)
20import GHC.Exts (fromList)
24−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
22+
23+import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
24+
25+import Spec (BenchSpec (..))
25
26
26−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), lookup, insert, empty)
27
27
28spec :: BenchSpec
28spec :: BenchSpec
29spec = BenchSpec
29spec = BenchSpec
30− { benchLookup = Just $ \name ->
30+ { benchLookup = Just $ \name ->
31− env (mkMap 10000) $ \ ~bigMap ->
31+ env (mkMap 10000) $ \ ~bigMap ->
32− bench name $ nf tenLookups bigMap
32+ bench name $ nf tenLookups bigMap
33− , benchInsertSmall = Just $ \name ->
33+ , benchInsertSmall = Just $ \name ->
34− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
34+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
35− , benchInsertBig = Just $ \name ->
35+ , benchInsertBig = Just $ \name ->
36− env (mkMap 10000) $ \ ~(bigMap) ->
36+ env (mkMap 10000) $ \ ~bigMap ->
37− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
37+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
38− , benchUpdateSmall = Just $ \name ->
38+ , benchUpdateSmall = Just $ \name ->
39− env (mkMap 10) $ \ ~(smallMap) ->
39+ env (mkMap 10) $ \ ~smallMap ->
40− bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
40+ bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
41− , benchUpdateBig = Just $ \name ->
41+ , benchUpdateBig = Just $ \name ->
42− env (mkMap 10000) $ \ ~(bigMap) ->
42+ env (mkMap 10000) $ \ ~bigMap ->
43− bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
43+ bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
44− }
44+ }
45
45
46+tenLookups
46−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
47+ :: TypeRepMap (Proxy :: Nat -> Type)
47− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
48+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
48− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
49+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
49− )
50+ )
50tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
51tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
51 where
52 where
52− lp :: forall (a::Nat). Typeable a => Proxy a
53+ lp :: forall (a :: Nat) . Typeable a => Proxy a
53 lp = fromJust $ lookup tmap
54 lp = fromJust $ lookup tmap
54
55
56+inserts
55−inserts :: forall a . (KnownNat a)
57+ :: forall a . (KnownNat a)
56− => TypeRepMap (Proxy :: Nat -> *)
58+ => TypeRepMap (Proxy :: Nat -> Type)
57− -> Int
59+ -> Int
58− -> Proxy (a :: Nat)
60+ -> Proxy (a :: Nat)
59− -> TypeRepMap (Proxy :: Nat -> *)
61+ -> TypeRepMap (Proxy :: Nat -> Type)
60inserts !c 0 _ = c
62inserts !c 0 _ = c
61inserts !c n x = inserts
63inserts !c n x = inserts
62− (insert x c)
64+ (insert x c)
63− (n-1)
65+ (n-1)
64− (Proxy :: Proxy (a+1))
66+ (Proxy :: Proxy (a + 1))
65
67
66−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> *))
68+mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
67mkMap n = pure $ fromList $ buildBigMap n (Proxy :: Proxy 0) []
69mkMap n = pure $ fromList $ buildBigMap n (Proxy :: Proxy 0) []
68
70
69
71
72+buildBigMap
70−buildBigMap :: forall a . (KnownNat a)
73+ :: forall a . (KnownNat a)
71− => Int
74+ => Int
72− -> Proxy (a :: Nat)
75+ -> Proxy (a :: Nat)
73− -> [WrapTypeable (Proxy :: Nat -> *)]
76+ -> [WrapTypeable (Proxy :: Nat -> Type)]
74− -> [WrapTypeable (Proxy :: Nat -> *)]
77+ -> [WrapTypeable (Proxy :: Nat -> Type)]
75buildBigMap 1 x = (WrapTypeable x :)
78buildBigMap 1 x = (WrapTypeable x :)
76buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
79buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hs+45−42
@@ -1,79 +1,81 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
3{-# LANGUAGE InstanceSigs #-}
4{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
9
9
10−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11−
12module DMap
10module DMap
13− ( spec
11+ ( spec
14− ) where
12+ ) where
15−
16−import Criterion.Main (bench, env, nf, whnf)
17
13
18import Prelude hiding (lookup)
14import Prelude hiding (lookup)
19
15
20import Control.DeepSeq (NFData (..))
16import Control.DeepSeq (NFData (..))
17+import Criterion.Main (bench, env, nf, whnf)
18+import Data.Kind (Type)
21import Data.Maybe (fromJust)
19import Data.Maybe (fromJust)
22import Data.Proxy (Proxy (..))
20import Data.Proxy (Proxy (..))
23−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
24−import Spec
25import Type.Reflection (TypeRep, Typeable, typeRep)
22import Type.Reflection (TypeRep, Typeable, typeRep)
26import Type.Reflection.Unsafe (typeRepFingerprint)
23import Type.Reflection.Unsafe (typeRepFingerprint)
27
24
28import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
25import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
29import Data.Some (Some (Some))
26import Data.Some (Some (Some))
30
27
31−type TypeRepMap = DMap TypeRep
28+import Spec (BenchSpec (..))
32
29
33
30
31+type TypeRepMap = DMap TypeRep
32+
34spec :: BenchSpec
33spec :: BenchSpec
35spec = BenchSpec
34spec = BenchSpec
36− { benchLookup = Just $ \name ->
35+ { benchLookup = Just $ \name ->
37− env mkBigMap $ \ ~(DMapNF bigMap) ->
36+ env mkBigMap $ \ ~(DMapNF bigMap) ->
38− bench name $ nf tenLookups bigMap
37+ bench name $ nf tenLookups bigMap
39− , benchInsertSmall = Just $ \name ->
38+ , benchInsertSmall = Just $ \name ->
40− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
39+ bench name $ whnf (inserts empty 10) (Proxy @ 99999)
41− , benchInsertBig = Just $ \name ->
40+ , benchInsertBig = Just $ \name ->
42− env mkBigMap $ \ ~(DMapNF bigMap) ->
41+ env mkBigMap $ \ ~(DMapNF bigMap) ->
43− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
42+ bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
44− , benchUpdateSmall = Nothing -- Not implemented
43+ , benchUpdateSmall = Nothing -- Not implemented
45− , benchUpdateBig = Nothing -- Not implemented
44+ , benchUpdateBig = Nothing -- Not implemented
46− }
45+ }
47
46
47+tenLookups
48−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
48+ :: TypeRepMap (Proxy :: Nat -> Type)
49− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
49+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
50− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
50+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
51− )
51+ )
52tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
52tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
53 where
53 where
54 lp :: forall (a :: Nat) . Typeable a => Proxy a
54 lp :: forall (a :: Nat) . Typeable a => Proxy a
55 lp = fromJust $ lookup (typeRep @a) tmap
55 lp = fromJust $ lookup (typeRep @a) tmap
56
56
57+inserts
57−inserts :: forall a . (KnownNat a)
58+ :: forall a . (KnownNat a)
58− => TypeRepMap (Proxy :: Nat -> *)
59+ => TypeRepMap (Proxy :: Nat -> Type)
59− -> Int
60+ -> Int
60− -> Proxy (a :: Nat)
61+ -> Proxy (a :: Nat)
61− -> TypeRepMap (Proxy :: Nat -> *)
62+ -> TypeRepMap (Proxy :: Nat -> Type)
62inserts !c 0 _ = c
63inserts !c 0 _ = c
63inserts !c n x = inserts
64inserts !c n x = inserts
64− (insert (typeRep @ a) x c)
65+ (insert (typeRep @ a) x c)
65− (n-1)
66+ (n-1)
66− (Proxy :: Proxy (a+1))
67+ (Proxy :: Proxy (a+1))
67
68
68-- TypeRepMap of 10000 elements
69-- TypeRepMap of 10000 elements
69−mkBigMap :: IO (DMapNF (Proxy :: Nat -> *))
70+mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
70mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
71mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
71
72
73+buildBigMap
72−buildBigMap :: forall a . (KnownNat a)
74+ :: forall a . (KnownNat a)
73− => Int
75+ => Int
74− -> Proxy (a :: Nat)
76+ -> Proxy (a :: Nat)
75− -> TypeRepMap (Proxy :: Nat -> *)
77+ -> TypeRepMap (Proxy :: Nat -> Type)
76− -> TypeRepMap (Proxy :: Nat -> *)
78+ -> TypeRepMap (Proxy :: Nat -> Type)
77buildBigMap 1 x = insert (typeRep @a) x
79buildBigMap 1 x = insert (typeRep @a) x
78buildBigMap n x = insert (typeRep @a) x
80buildBigMap n x = insert (typeRep @a) x
79 . buildBigMap (n - 1) (Proxy @(a + 1))
81 . buildBigMap (n - 1) (Proxy @(a + 1))
@@ -82,5 +84,6 @@ buildBigMap n x = insert (typeRep @a) x
82newtype DMapNF f = DMapNF (TypeRepMap f)
84newtype DMapNF f = DMapNF (TypeRepMap f)
83
85
84instance NFData (DMapNF f) where
86instance NFData (DMapNF f) where
87+ rnf :: DMapNF f -> ()
85− rnf (DMapNF x) =
88+ rnf (DMapNF x) =
86− rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
89+ rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
benchmark/Main.hs+37−32
@@ -1,47 +1,52 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2
2
3−module Main where
3+module Main
4+ ( main
5+ ) where
4
6
5−import Criterion.Main (defaultMain, bgroup)
7+import Criterion.Main (bgroup, defaultMain)
8+
9+import Spec (BenchSpec (..))
6
10
7−import Spec
8−import qualified CMap
9import qualified CacheMap
11import qualified CacheMap
12+import qualified CMap
10#if ( __GLASGOW_HASKELL__ >= 802 )
13#if ( __GLASGOW_HASKELL__ >= 802 )
11import qualified DMap
14import qualified DMap
12#endif
15#endif
13import qualified OptimalVector as OptVec
16import qualified OptimalVector as OptVec
14
17
18+
15main :: IO ()
19main :: IO ()
16main = do
20main = do
17− let specs = [("CMap", CMap.spec)
21+ let specs = [("CMap", CMap.spec)
18− ,("CacheMap", CacheMap.spec)
22+ ,("CacheMap", CacheMap.spec)
19#if ( __GLASGOW_HASKELL__ >= 802 )
23#if ( __GLASGOW_HASKELL__ >= 802 )
20− , ("DMap", DMap.spec)
24+ , ("DMap", DMap.spec)
21#endif
25#endif
22− , ("OptVec", OptVec.spec)
26+ , ("OptVec", OptVec.spec)
23− ]
27+ ]
24− -- This code creates a benchmark group. Given a getter
28+ {- This code creates a benchmark group. Given a getter
25− -- (that is test description) it gets a benchmark generation
29+ (that is test description) it gets a benchmark generation
26− -- function from each module spec. Benchmark generation
30+ function from each module spec. Benchmark generation
27− -- function takes a label and generate benchmarks. It's
31+ function takes a label and generate benchmarks. It's
28− -- possible to introduce parameters passing in the same way.
32+ possible to introduce parameters passing in the same way.
33+ --}
29− mkGroup getBenchmark =
34+ let mkGroup getBenchmark =
30− [ mkBenchmark label
35+ [ mkBenchmark label
31− | (label, spec) <- specs
36+ | (label, spec) <- specs
32− -- Here we use pure to force pattern matching in List
37+ -- Here we use pure to force pattern matching in List
33− -- then in case of pattern match failure `mzero` will
38+ -- then in case of pattern match failure `mzero` will
34− -- be called, so benchmark will be ignored.
39+ -- be called, so benchmark will be ignored.
35− , Just mkBenchmark <- pure $ getBenchmark spec
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+ ]
36 ]
52 ]
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+29−28
@@ -1,59 +1,60 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
3−{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
9
9
10−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11−
12module OptimalVector
10module OptimalVector
13 ( spec
11 ( spec
14 ) where
12 ) where
15
13
16−import Spec
17−import Criterion.Main (bench, nf, env)
18−
19import Prelude hiding (lookup)
14import Prelude hiding (lookup)
20
15
16+import Criterion.Main (bench, env, nf)
17+import Data.Kind (Type)
21import Data.Maybe (fromJust)
18import Data.Maybe (fromJust)
22import Data.Proxy (Proxy (..))
19import Data.Proxy (Proxy (..))
23import Data.Typeable (Typeable)
20import Data.Typeable (Typeable)
24−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
25
22
26import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
23import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
27
24
25+import Spec (BenchSpec (..))
26+
27+
28spec :: BenchSpec
28spec :: BenchSpec
29spec = BenchSpec
29spec = BenchSpec
30− { benchLookup = Just $ \name ->
30+ { benchLookup = Just $ \name ->
31− env mkBigMap $ \ ~bigMap ->
31+ env mkBigMap $ \ ~bigMap ->
32− bench name $ nf tenLookups bigMap
32+ bench name $ nf tenLookups bigMap
33− , benchInsertSmall = Nothing -- Not implemented
33+ , benchInsertSmall = Nothing -- Not implemented
34− , benchInsertBig = Nothing -- Not implemented
34+ , benchInsertBig = Nothing -- Not implemented
35− , benchUpdateSmall = Nothing -- Not implemented
35+ , benchUpdateSmall = Nothing -- Not implemented
36− , benchUpdateBig = Nothing -- Not implemented
36+ , benchUpdateBig = Nothing -- Not implemented
37− }
37+ }
38−
38+
39+tenLookups
39−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
40+ :: TypeRepMap (Proxy :: Nat -> Type)
40− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
41+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
41− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
42+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
42− )
43+ )
43tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
44tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
44 where
45 where
45 lp :: forall (a::Nat). Typeable a => Proxy a
46 lp :: forall (a::Nat). Typeable a => Proxy a
46 lp = fromJust $ lookup tmap
47 lp = fromJust $ lookup tmap
47
48
48-- TypeRepMap of 10000 elements
49-- TypeRepMap of 10000 elements
49−mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> *))
50+mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> Type))
50mkBigMap = pure $ fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
51mkBigMap = pure $ fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
51
52
53+buildBigMap
52−buildBigMap :: forall a . (KnownNat a)
54+ :: forall a . (KnownNat a)
53− => Int
55+ => Int
54− -> Proxy (a :: Nat)
56+ -> Proxy (a :: Nat)
55− -> [TF (Proxy :: Nat -> *)]
57+ -> [TF (Proxy :: Nat -> Type)]
56− -> [TF (Proxy :: Nat -> *)]
58+ -> [TF (Proxy :: Nat -> Type)]
57buildBigMap 1 x = (TF x :)
59buildBigMap 1 x = (TF x :)
58buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
60buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
59−
benchmark/Spec.hs+61−50
@@ -1,53 +1,64 @@
1−-- | Specification of the benchmarks.
1+{- | Specification of the benchmarks.
2+
2−-- This module keeps a list of all bencharks, this way
3+This module keeps a list of all bencharks, this way
3−-- we can group benchmark by the interesting function, not
4+we can group benchmark by the interesting function, not
4−-- by the implementation.
5+by the implementation.
6+-}
5module Spec
7module Spec
6− ( BenchSpec(..)
8+ ( BenchSpec(..)
7− ) where
9+ ) where
8−
10+
9−import Criterion
11+import Criterion (Benchmark)
10−
12+
13+
11−-- | List of benchmarks that each module should provide.
14+{- | List of benchmarks that each module should provide.
12−-- If implementation can express the benchmark then it
15+If implementation can express the benchmark then it
13−-- can return @Nothing@ in that benchmark.
16+can return @Nothing@ in that benchmark.
14−--
17+
15−-- Map should contain elements from @1@ to @size of map@
18+Map should contain elements from @1@ to @size of map@
16−-- inserted in ascending order (later that requirement may
19+inserted in ascending order (later that requirement may
17−-- change).
20+change).
21+-}
18data BenchSpec = BenchSpec
22data BenchSpec = BenchSpec
19− { benchLookup :: Maybe (String -> Benchmark)
20− -- ^ Basic lookup we look 10 values inside 10k map.
23+ { {- | Basic lookup we look 10 values inside 10k map.
21− --
24+
22− -- Implementation may look like:
25+ Implementation may look like:
26+
23− -- @
27+ @
24− -- tenLookups :: TypeRepMap (Proxy :: Nat -> *)
28+ tenLookups :: TypeRepMap (Proxy :: Nat -> *)
25− -- -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
29+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
26− -- , Proxy 50, Proxy 60, Proxy 70, Proxy 80
30+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
27− -- )
31+ )
28− -- tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
32+ tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
29− -- @
33+ @
34+ -}
30− , benchInsertSmall :: Maybe (String -> Benchmark)
35+ benchLookup :: Maybe (String -> Benchmark)
31− -- ^ Insert 10 elements into an empty map.
36+ {- ^ Insert 10 elements into an empty map.
32− --
37+
33− -- Implementation may look like:
38+ 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− }
53
39
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+23−22
@@ -1,31 +1,30 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
1{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
3−{-# LANGUAGE KindSignatures #-}
4{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeOperators #-}
7{-# LANGUAGE TypeOperators #-}
8{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
9
9
10−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11−
12module Vector
10module Vector
13− ( benchVector
11+ ( benchVector
14− , prepareBenchVector
12+ , prepareBenchVector
15− ) where
13+ ) where
16−
17−import Criterion.Main (Benchmark, bench, bgroup, nf)
18
14
19import Prelude hiding (lookup)
15import Prelude hiding (lookup)
20
16
21import Control.DeepSeq (rnf)
17import Control.DeepSeq (rnf)
22−import Control.Exception
18+import Control.Exception (evaluate)
19+import Criterion.Main (Benchmark, bench, bgroup, nf)
20+import Data.Kind (Type)
23import Data.Maybe (fromJust)
21import Data.Maybe (fromJust)
24import Data.Proxy (Proxy (..))
22import Data.Proxy (Proxy (..))
25import Data.Typeable (Typeable)
23import Data.Typeable (Typeable)
26−import GHC.TypeLits
24+import GHC.TypeLits (type (+), KnownNat, Nat)
25+
26+import Data.TypeRep.Vector (TF (..), TypeRepVector, fingerprints, fromList, lookup)
27
27
28−import Data.TypeRep.Vector
29
28
30benchVector :: Benchmark
29benchVector :: Benchmark
31benchVector = bgroup "vector"
30benchVector = bgroup "vector"
@@ -34,24 +33,26 @@ benchVector = bgroup "vector"
34 -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
33 -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
35 ]
34 ]
36
35
36+tenLookups
37−tenLookups :: TypeRepVector (Proxy :: Nat -> *)
37+ :: TypeRepVector (Proxy :: Nat -> Type)
38− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
38+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
39+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40− )
40+ )
41tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
41tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
42 where
42 where
43 lp :: forall (a::Nat). Typeable a => Proxy a
43 lp :: forall (a::Nat). Typeable a => Proxy a
44 lp = fromJust $ lookup tmap
44 lp = fromJust $ lookup tmap
45
45
46-- TypeRepMap of 10000 elements
46-- TypeRepMap of 10000 elements
47−bigMap :: TypeRepVector (Proxy :: Nat -> *)
47+bigMap :: TypeRepVector (Proxy :: Nat -> Type)
48bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
48bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
49
49
50+buildBigMap
50−buildBigMap :: forall a . (KnownNat a)
51+ :: forall a . (KnownNat a)
51− => Int
52+ => Int
52− -> Proxy (a :: Nat)
53+ -> Proxy (a :: Nat)
53− -> [TF (Proxy :: Nat -> *)]
54+ -> [TF (Proxy :: Nat -> Type)]
54− -> [TF (Proxy :: Nat -> *)]
55+ -> [TF (Proxy :: Nat -> Type)]
55buildBigMap 1 x = (TF x :)
56buildBigMap 1 x = (TF x :)
56buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
57buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
57
58
src/Data/TMap.hs+48−2
@@ -37,14 +37,19 @@ module Data.TMap
37 , delete
37 , delete
38 , unionWith
38 , unionWith
39 , union
39 , union
40+ , intersectionWith
41+ , intersection
40 , map
42 , map
41 , adjust
43 , adjust
44+ , alter
42
45
43 -- * Query
46 -- * Query
44 , lookup
47 , lookup
45 , member
48 , member
46 , size
49 , size
47 , keys
50 , keys
51+ , keysWith
52+ , toListWith
48 ) where
53 ) where
49
54
50import Prelude hiding (lookup, map)
55import Prelude hiding (lookup, map)
@@ -52,7 +57,7 @@ import Prelude hiding (lookup, map)
52import Data.Functor.Identity (Identity (..))
57import Data.Functor.Identity (Identity (..))
53import Data.Typeable (Typeable)
58import Data.Typeable (Typeable)
54import GHC.Exts (coerce)
59import GHC.Exts (coerce)
55−import Type.Reflection (SomeTypeRep)
60+import Type.Reflection (SomeTypeRep, TypeRep)
56
61
57import qualified Data.TypeRepMap as F
62import qualified Data.TypeRepMap as F
58
63
@@ -87,6 +92,7 @@ one x = coerce (F.one @a @Identity $ coerce x)
87{- |
92{- |
88
93
89Insert a value into a 'TMap'.
94Insert a value into a 'TMap'.
95+TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
90
96
91prop> size (insert v tm) >= size tm
97prop> size (insert v tm) >= size tm
92prop> member @a (insert (x :: a) tm) == True
98prop> member @a (insert (x :: a) tm) == True
@@ -98,6 +104,9 @@ insert x = coerce (F.insert @a @Identity $ coerce x)
98
104
99{- | Delete a value from a 'TMap'.
105{- | Delete a value from a 'TMap'.
100
106
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+
101prop> size (delete @a tm) <= size tm
110prop> size (delete @a tm) <= size tm
102prop> member @a (delete @a tm) == False
111prop> member @a (delete @a tm) == False
103
112
@@ -127,6 +136,24 @@ union :: TMap -> TMap -> TMap
127union = F.union
136union = F.union
128{-# INLINE union #-}
137{-# INLINE union #-}
129
138
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+
130{- | Lookup a value of the given type in a 'TMap'.
157{- | Lookup a value of the given type in a 'TMap'.
131
158
132>>> x = lookup $ insert (11 :: Int) empty
159>>> x = lookup $ insert (11 :: Int) empty
@@ -160,6 +187,16 @@ keys :: TMap -> [SomeTypeRep]
160keys = F.keys
187keys = F.keys
161{-# INLINE keys #-}
188{-# INLINE keys #-}
162
189
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+
163-- | Map a function over the values.
200-- | Map a function over the values.
164map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
201map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
165map f = F.hoistWithKey (liftToIdentity f)
202map f = F.hoistWithKey (liftToIdentity f)
@@ -170,5 +207,14 @@ adjust :: Typeable a => (a -> a) -> TMap -> TMap
170adjust f = F.adjust (liftToIdentity f)
207adjust f = F.adjust (liftToIdentity f)
171{-# INLINE adjust #-}
208{-# INLINE adjust #-}
172
209
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+
173−liftToIdentity :: forall a. Typeable a => (a -> a) -> Identity a -> Identity a
219+liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
174liftToIdentity = coerce
220liftToIdentity = coerce
src/Data/TypeRepMap.hs+6−1
@@ -5,7 +5,7 @@ Copyright: (c) 2017-2020 Kowainik
5SPDX-License-Identifier: MPL-2.0
5SPDX-License-Identifier: MPL-2.0
6Maintainer: Kowainik <xrom.xkov@gmail.com>
6Maintainer: Kowainik <xrom.xkov@gmail.com>
7
7
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
9sort of parametrization may be familiar to users of @vinyl@ records.
9sort of parametrization may be familiar to users of @vinyl@ records.
10
10
11@'TypeRepMap' f@ is a more efficient replacement for @DMap
11@'TypeRepMap' f@ is a more efficient replacement for @DMap
@@ -57,17 +57,22 @@ module Data.TypeRepMap
57 , insert
57 , insert
58 , delete
58 , delete
59 , adjust
59 , adjust
60+ , alter
60 , hoist
61 , hoist
61 , hoistA
62 , hoistA
62 , hoistWithKey
63 , hoistWithKey
63 , unionWith
64 , unionWith
64 , union
65 , union
66+ , intersectionWith
67+ , intersection
65
68
66 -- * Query
69 -- * Query
67 , lookup
70 , lookup
68 , member
71 , member
69 , size
72 , size
70 , keys
73 , keys
74+ , keysWith
75+ , toListWith
71
76
72 -- * 'IsList'
77 -- * 'IsList'
73 , WrapTypeable (..)
78 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+198−50
@@ -1,10 +1,11 @@
1+{-# OPTIONS_GHC -Wno-missing-export-lists #-}
2+
1{-# LANGUAGE AllowAmbiguousTypes #-}
3{-# LANGUAGE AllowAmbiguousTypes #-}
2{-# LANGUAGE BangPatterns #-}
4{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE CPP #-}
5{-# LANGUAGE CPP #-}
4{-# LANGUAGE DataKinds #-}
6{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE GADTs #-}
7{-# LANGUAGE GADTs #-}
6{-# LANGUAGE InstanceSigs #-}
8{-# LANGUAGE InstanceSigs #-}
7−{-# LANGUAGE KindSignatures #-}
8{-# LANGUAGE MagicHash #-}
9{-# LANGUAGE MagicHash #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE Rank2Types #-}
11{-# LANGUAGE Rank2Types #-}
@@ -44,9 +45,9 @@ import Data.Kind (Type)
44import Data.Type.Equality ((:~:) (..), TestEquality (..))
45import Data.Type.Equality ((:~:) (..), TestEquality (..))
45import Data.List (intercalate, nubBy)
46import Data.List (intercalate, nubBy)
46import Data.Maybe (fromMaybe)
47import Data.Maybe (fromMaybe)
47−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', readArray, sizeofArray,
48+import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
48 thawArray, unsafeFreezeArray, writeArray)
49 thawArray, unsafeFreezeArray, writeArray)
49−import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
50+import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
50import Data.Semigroup (Semigroup (..), All(..))
51import Data.Semigroup (Semigroup (..), All(..))
51import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
52import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
52import GHC.Exts (IsList (..), inline, sortWith)
53import GHC.Exts (IsList (..), inline, sortWith)
@@ -63,7 +64,6 @@ import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeab
63import Type.Reflection.Unsafe (typeRepFingerprint)
64import Type.Reflection.Unsafe (typeRepFingerprint)
64import Unsafe.Coerce (unsafeCoerce)
65import Unsafe.Coerce (unsafeCoerce)
65
66
66−import qualified Data.Map.Strict as Map
67import qualified GHC.Exts as GHC (fromList, toList)
67import qualified GHC.Exts as GHC (fromList, toList)
68
68
69{- |
69{- |
@@ -169,33 +169,49 @@ prop> member @a (one (x :: f a)) == True
169
169
170-}
170-}
171one :: forall a f . Typeable a => f a -> TypeRepMap f
171one :: forall a f . Typeable a => f a -> TypeRepMap f
172−one x = insert x empty
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)
173{-# INLINE one #-}
178{-# INLINE one #-}
174
179
175{- |
180{- |
176
181
177Insert a value into a 'TypeRepMap'.
182Insert a value into a 'TypeRepMap'.
183+TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
178
184
179prop> size (insert v tm) >= size tm
185prop> size (insert v tm) >= size tm
180prop> member @a (insert (x :: f a) tm) == True
186prop> member @a (insert (x :: f a) tm) == True
181
187
182-}
188-}
183insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
189insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
184−insert x = fromTriples . addX . toTriples
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)}
185 where
195 where
186− tripleX :: (Fingerprint, Any, Any)
196+ changeAnyArr :: Int -> Array Any -> Array Any
187− tripleX@(fpX, _, _) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
197+ changeAnyArr i trAs = runST $ do
188−
198+ let n = sizeofArray trAs
189− addX :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
199+ mutArr <- thawArray trAs 0 n
190− addX l = tripleX : deleteByFst fpX l
200+ writeArray mutArr i $ toAny x
201+ unsafeFreezeArray mutArr
191{-# INLINE insert #-}
202{-# INLINE insert #-}
192
203
193-- Extract the kind of a type. We use it to work around lack of syntax for
204-- Extract the kind of a type. We use it to work around lack of syntax for
194-- inferred type variables (which are not subject to type applications).
205-- inferred type variables (which are not subject to type applications).
195type KindOf (a :: k) = k
206type KindOf (a :: k) = k
196
207
208+type ArgKindOf (f :: k -> l) = k
209+
197{- | Delete a value from a 'TypeRepMap'.
210{- | Delete a value from a 'TypeRepMap'.
198
211
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+
199prop> size (delete @a tm) <= size tm
215prop> size (delete @a tm) <= size tm
200prop> member @a (delete @a tm) == False
216prop> member @a (delete @a tm) == False
201
217
@@ -208,30 +224,60 @@ False
208True
224True
209-}
225-}
210delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
226delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
211−delete = fromTriples . deleteByFst (typeFp @a) . toTriples
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
212{-# INLINE delete #-}
234{-# INLINE delete #-}
213
235
236+deleteFirst :: (a -> Bool) -> [a] -> [a]
237+deleteFirst _ [] = []
238+deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239+
214{- |
240{- |
215−Update a value at a specific key with the result of the provided function. When
241+Update a value at a specific key with the result of the provided function.
216−the key is not a member of the map, the original map is returned.
242+When the key is not a member of the map, the original map is returned.
217
243
218>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
244>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
219>>> lookup @String $ adjust (fmap (++ "ww")) trmap
245>>> lookup @String $ adjust (fmap (++ "ww")) trmap
220Just (Identity "aww")
246Just (Identity "aww")
221-}
247-}
222adjust :: forall a f . Typeable a => (f a -> f a) -> TypeRepMap f -> TypeRepMap f
248adjust :: 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
223−adjust fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
264+alter fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
224− Nothing -> tr
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
225− Just i -> tr {trAnys = changeAnyArr i (trAnys tr)}
272+ Just v -> tr{trAnys = replaceAnyAt i (toAny v) (trAnys tr)}
226 where
273 where
227− changeAnyArr :: Int -> Array Any -> Array Any
274+ replaceAnyAt :: Int -> Any -> Array Any -> Array Any
228− changeAnyArr i trAs = runST $ do
275+ replaceAnyAt i v trAs = runST $ do
229 let n = sizeofArray trAs
276 let n = sizeofArray trAs
230 mutArr <- thawArray trAs 0 n
277 mutArr <- thawArray trAs 0 n
231− a <- toAny . fun . fromAny <$> readArray mutArr i
232− writeArray mutArr i a
278+ writeArray mutArr i v
233 unsafeFreezeArray mutArr
279 unsafeFreezeArray mutArr
234−{-# INLINE adjust #-}
280+{-# INLINE alter #-}
235
281
236{- | Map over the elements of a 'TypeRepMap'.
282{- | Map over the elements of a 'TypeRepMap'.
237
283
@@ -265,33 +311,89 @@ hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
265 withTr t = withTypeable t f
311 withTr t = withTypeable t f
266{-# INLINE hoistWithKey #-}
312{-# INLINE hoistWithKey #-}
267
313
268−-- | The union of two 'TypeRepMap's using a combining function.
314+-- | The union of two 'TypeRepMap's using a combining function for conflicting entries. @O(n + m)@
269unionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
315unionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
270−unionWith f m1 m2 = fromTriples
316+unionWith f ma mb = do
271− $ toTripleList
317+ fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
272− $ Map.unionWith combine
273− (fromTripleList $ toTriples m1)
274− (fromTripleList $ toTriples m2)
275 where
318 where
276 f' :: forall x. TypeRep x -> f x -> f x -> f x
319 f' :: forall x. TypeRep x -> f x -> f x -> f x
277 f' tr = withTypeable tr f
320 f' tr = withTypeable tr f
278
321
279− combine :: (Any, Any) -> (Any, Any) -> (Any, Any)
322+ combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
280− combine (av, ak) (bv, _) = (toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
323+ combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
281−
324+
282− fromTripleList :: Ord a => [(a, b, c)] -> Map.Map a (b, c)
325+ -- Merges two typrepmaps into a sorted, dedup'd list of triples.
283− fromTripleList = Map.fromList . map (\(a, b, c) -> (a, (b, c)))
326+ -- Using 'toSortedTriples' allows us to assume the triples are sorted by fingerprint,
284−
327+ -- Given O(n) performance from 'toSortedTriples', and given that we can merge-sort in
285− toTripleList :: Map.Map a (b, c) -> [(a, b, c)]
328+ -- O(n + m) time, then can '.fromSortedTriples' back into cachedBinarySearch order in O(n + m)
286− toTripleList = map (\(a, (b, c)) -> (a, b, c)) . Map.toList
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
287{-# INLINE unionWith #-}
347{-# INLINE unionWith #-}
288
348
289−-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
349+-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
290-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
350-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
291union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
351union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
292union = unionWith const
352union = unionWith const
293{-# INLINE union #-}
353{-# INLINE union #-}
294
354
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+
295{- | Check if a value of the given type is present in a 'TypeRepMap'.
397{- | Check if a value of the given type is present in a 'TypeRepMap'.
296
398
297>>> member @Char $ one (Identity 'a')
399>>> member @Char $ one (Identity 'a')
@@ -327,9 +429,22 @@ size = sizeofPrimArray . fingerprintAs
327
429
328-- | Return the list of 'SomeTypeRep' from the keys.
430-- | Return the list of 'SomeTypeRep' from the keys.
329keys :: TypeRepMap f -> [SomeTypeRep]
431keys :: TypeRepMap f -> [SomeTypeRep]
330−keys TypeRepMap{..} = SomeTypeRep . anyToTypeRep <$> toList trKeys
432+keys = keysWith SomeTypeRep
331{-# INLINE keys #-}
433{-# INLINE keys #-}
332
434
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+
333-- | Binary searched based on this article
448-- | Binary searched based on this article
334-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
449-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
335-- with modification for our two-vector search case.
450-- with modification for our two-vector search case.
@@ -373,8 +488,20 @@ typeFp = typeRepFingerprint $ typeRep @a
373toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
488toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
374toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
489toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
375
490
376−deleteByFst :: Eq a => a -> [(a, b, c)] -> [(a, b, c)]
491+-- | Efficiently get sorted triples from a map in O(n) time
377−deleteByFst x = filter ((/= x) . fst3)
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)
378
505
379nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
506nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
380nubByFst = nubBy ((==) `on` fst3)
507nubByFst = nubBy ((==) `on` fst3)
@@ -394,9 +521,6 @@ data WrapTypeable f where
394instance Show (WrapTypeable f) where
521instance Show (WrapTypeable f) where
395 show (WrapTypeable (_ :: f a)) = show $ calcFp @a
522 show (WrapTypeable (_ :: f a)) = show $ calcFp @a
396
523
397−wrapTypeable :: TypeRep a -> f a -> WrapTypeable f
398−wrapTypeable tr = withTypeable tr WrapTypeable
399−
400{- |
524{- |
401
525
402prop> fromList . toList == 'id'
526prop> fromList . toList == 'id'
@@ -424,19 +548,19 @@ instance IsList (TypeRepMap f) where
424 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
548 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
425
549
426 toList :: TypeRepMap f -> [WrapTypeable f]
550 toList :: TypeRepMap f -> [WrapTypeable f]
427− toList = map toWrapTypeable . toTriples
551+ toList = toListWith WrapTypeable
428− where
429− toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
430− toWrapTypeable (_, an, k) = wrapTypeable (unsafeCoerce k) (fromAny an)
431
552
432calcFp :: forall a . Typeable a => Fingerprint
553calcFp :: forall a . Typeable a => Fingerprint
433calcFp = typeRepFingerprint $ typeRep @a
554calcFp = typeRepFingerprint $ typeRep @a
434
555
435fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
556fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
557+fromTriples = fromSortedTriples . sortWith fst3 . nubByFst
558+
559+fromSortedTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
436−fromTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
560+fromSortedTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
437 where
561 where
438 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
562 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
439− (fps, ans, ks) = unzip3 $ fromSortedList $ sortWith fst3 $ nubByFst kvs
563+ (fps, ans, ks) = unzip3 $ fromSortedList kvs
440
564
441----------------------------------------------------------------------------
565----------------------------------------------------------------------------
442-- Tree-like conversion
566-- Tree-like conversion
@@ -463,11 +587,35 @@ fromSortedList l = runST $ do
463 writeArray result i (indexArray origin newFirst)
587 writeArray result i (indexArray origin newFirst)
464 loop (2 * i + 2) (newFirst + 1)
588 loop (2 * i + 2) (newFirst + 1)
465
589
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+
466----------------------------------------------------------------------------
614----------------------------------------------------------------------------
467-- Helper functions.
615-- Helper functions.
468----------------------------------------------------------------------------
616----------------------------------------------------------------------------
469
617
470−-- | Check that invariant of the structure is hold.
618+-- | Check that invariant of the structure holds.
471-- The structure maintains the following invariant.
619-- The structure maintains the following invariant.
472-- For each element @A@ at index @i@:
620-- For each element @A@ at index @i@:
473--
621--
test/Test/TypeRep/TypeRepMap.hs+62−4
@@ -5,14 +5,18 @@ module Test.TypeRep.TypeRepMap
5import Prelude hiding (lookup)
5import Prelude hiding (lookup)
6
6
7import Data.Functor.Identity (Identity (..))
7import Data.Functor.Identity (Identity (..))
8+import Data.List (sortBy)
9+import Data.Ord (comparing)
10+import Data.Typeable (cast)
8−import GHC.Exts (fromList)
11+import GHC.Exts (fromList, toList)
12+import Type.Reflection (SomeTypeRep(..), typeRep)
9−import Test.Hspec (Spec, describe, it, shouldBe)
13+import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList, shouldSatisfy, expectationFailure)
10
14
11−import Data.TMap (TMap, empty, insert, lookup, one, size, union)
15+import Data.TMap (TMap, empty, insert, lookup, one, size, union, keys)
12import Data.TypeRepMap.Internal (WrapTypeable (..))
16import Data.TypeRepMap.Internal (WrapTypeable (..))
13
17
14
18
15−-- Simple test for 'lookup', 'insert' and 'size' functions.
19+-- Simple test for 'lookup', 'insert', 'size', 'keys', 'toList' functions.
16typeRepMapSpec :: Spec
20typeRepMapSpec :: Spec
17typeRepMapSpec = describe "TypeRepMap" $ do
21typeRepMapSpec = describe "TypeRepMap" $ do
18 describe "Lookup Test" $ do
22 describe "Lookup Test" $ do
@@ -39,6 +43,60 @@ typeRepMapSpec = describe "TypeRepMap" $ do
39 lookup m `shouldBe` Just True
43 lookup m `shouldBe` Just True
40 lookup @Int m `shouldBe` Nothing
44 lookup @Int m `shouldBe` Nothing
41
45
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
42
100
43mapOf10 :: TMap
101mapOf10 :: TMap
44mapOf10 = insert True
102mapOf10 = insert True
test/Test/TypeRep/TypeRepMapProperty.hs+70−29
@@ -1,7 +1,6 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE DataKinds #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
6
5
7{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
6{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
@@ -15,14 +14,14 @@ import Prelude hiding (lookup)
15import Data.Proxy (Proxy (..))
14import Data.Proxy (Proxy (..))
16import Data.Semigroup (Semigroup (..))
15import Data.Semigroup (Semigroup (..))
17import GHC.Exts (fromList)
16import GHC.Exts (fromList)
18−import GHC.Stack (HasCallStack)
19import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
17import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
20−import Hedgehog (MonadGen, PropertyT, assert, forAll, property, (===))
18+import Hedgehog (MonadGen, assert, forAll, (===))
21import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
19import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
22import Test.Hspec.Hedgehog (hedgehog)
20import Test.Hspec.Hedgehog (hedgehog)
23
21
24import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
22import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
25− lookup, member)
23+ lookup, member, union, generateOrderMapping, fromSortedList,
24+ adjust, alter, intersection)
26
25
27import qualified Hedgehog.Gen as Gen
26import qualified Hedgehog.Gen as Gen
28import qualified Hedgehog.Range as Range
27import qualified Hedgehog.Range as Range
@@ -36,6 +35,13 @@ typeRepMapPropertySpec = describe "TypeRepMap Property tests" $ do
36 deleteMemberSpec
35 deleteMemberSpec
37 insertInvariantSpec
36 insertInvariantSpec
38 deleteInvariantSpec
37 deleteInvariantSpec
38+ alterInsertSpec
39+ alterDeleteSpec
40+ alterAdjustSpec
41+ alterModifySpec
42+ intersectionSpec
43+ describe "Internal helpers" $ do
44+ generateOrderMappingInvariantSpec
39 describe "Instance Laws" $ do
45 describe "Instance Laws" $ do
40 semigroupAssocSpec
46 semigroupAssocSpec
41 monoidIdentitySpec
47 monoidIdentitySpec
@@ -82,45 +88,66 @@ deleteInvariantSpec = it "invariantCheck (delete k b) == True" $ hedgehog $ do
82 WrapTypeable (_ :: IntProxy n) <- forAll genTF
88 WrapTypeable (_ :: IntProxy n) <- forAll genTF
83 assert $ invariantCheck (delete @n m)
89 assert $ invariantCheck (delete @n m)
84
90
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+
85----------------------------------------------------------------------------
128----------------------------------------------------------------------------
86−-- Semigroup and Monoid laws
129+-- Internal helpers
87----------------------------------------------------------------------------
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]
88
136
89−#if __GLASGOW_HASKELL__ < 806
137+----------------------------------------------------------------------------
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)
138+-- Semigroup and Monoid laws
97−
139+----------------------------------------------------------------------------
98−instance Eq (FpMap f) where
99− FpMap (TypeRepMap as1 bs1 _ _) == FpMap (TypeRepMap as2 bs2 _ _) =
100− as1 == as2 && bs1 == bs2
101−#endif
102
140
103semigroupAssocSpec :: Property
141semigroupAssocSpec :: Property
104semigroupAssocSpec = it "x <> (y <> z) == (x <> y) <> z" $ hedgehog $ do
142semigroupAssocSpec = it "x <> (y <> z) == (x <> y) <> z" $ hedgehog $ do
105−#if __GLASGOW_HASKELL__ >= 806
106 x <- forAll genMap
143 x <- forAll genMap
107 y <- forAll genMap
144 y <- forAll genMap
108 z <- forAll genMap
145 z <- forAll genMap
109−#else
110− x <- FpMap <$> forAll genMap
111− y <- FpMap <$> forAll genMap
112− z <- FpMap <$> forAll genMap
113−#endif
114 (x <> (y <> z)) === ((x <> y) <> z)
146 (x <> (y <> z)) === ((x <> y) <> z)
115
147
116monoidIdentitySpec :: Property
148monoidIdentitySpec :: Property
117monoidIdentitySpec = it "x <> mempty == mempty <> x == x" $ hedgehog $ do
149monoidIdentitySpec = it "x <> mempty == mempty <> x == x" $ hedgehog $ do
118−#if __GLASGOW_HASKELL__ >= 806
119 x <- forAll genMap
150 x <- forAll genMap
120−#else
121− x <- FpMap <$> forAll genMap
122−#endif
123−
124 x <> mempty === x
151 x <> mempty === x
125 mempty <> x === x
152 mempty <> x === x
126
153
@@ -141,3 +168,17 @@ genTF = do
141 case someNatVal randNat of
168 case someNatVal randNat of
142 Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
169 Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
143 Nothing -> error "Invalid test generator"
170 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,5 +1,4 @@
1−{-# LANGUAGE KindSignatures #-}
2−{-# LANGUAGE PolyKinds #-}
1+{-# LANGUAGE PolyKinds #-}
3
2
4{- |
3{- |
5Copyright: (c) 2017-2020 Kowainik
4Copyright: (c) 2017-2020 Kowainik
@@ -21,6 +20,7 @@ module Data.TypeRep.CMap
21import Prelude hiding (lookup)
20import Prelude hiding (lookup)
22
21
23import Control.DeepSeq
22import Control.DeepSeq
23+import Data.Kind (Type)
24import Data.Proxy (Proxy (..))
24import Data.Proxy (Proxy (..))
25import Data.Typeable (TypeRep, Typeable, typeRep)
25import Data.Typeable (TypeRep, Typeable, typeRep)
26import GHC.Base (Any)
26import GHC.Base (Any)
@@ -30,7 +30,7 @@ import qualified Data.Map.Lazy as LMap
30
30
31
31
32-- | Map-like data structure with types served as the keys.
32-- | Map-like data structure with types served as the keys.
33−newtype TypeRepMap (f :: k -> *) = TypeRepMap
33+newtype TypeRepMap (f :: k -> Type) = TypeRepMap
34 { unMap :: LMap.Map TypeRep Any
34 { unMap :: LMap.Map TypeRep Any
35 }
35 }
36
36
typerep-extra-impls/Data/TypeRep/OptimalVector.hs+7−8
@@ -1,9 +1,8 @@
1−{-# LANGUAGE BangPatterns #-}
1+{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
2+{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE KindSignatures #-}
4−{-# LANGUAGE MagicHash #-}
3+{-# LANGUAGE MagicHash #-}
5−{-# LANGUAGE PolyKinds #-}
4+{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE TypeFamilies #-}
7
6
8{- |
7{- |
9Copyright: (c) 2017-2020 Kowainik
8Copyright: (c) 2017-2020 Kowainik
@@ -62,7 +61,7 @@ empty :: TypeRepMap f
62empty = TypeRepMap mempty mempty mempty
61empty = TypeRepMap mempty mempty mempty
63
62
64-- | Inserts the value with its type as a key.
63-- | Inserts the value with its type as a key.
65−insert :: forall a f . Typeable a => a -> TypeRepMap f -> TypeRepMap f
64+insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
66insert = undefined
65insert = undefined
67
66
68-- | Looks up the value at the type.
67-- | Looks up the value at the type.
@@ -112,7 +111,7 @@ binarySearch (Fingerprint a b) fpAs fpBs =
112data TF f where
111data TF f where
113 TF :: Typeable a => f a -> TF f
112 TF :: Typeable a => f a -> TF f
114
113
115−fromF :: Typeable a => f a -> Proxy a
114+fromF :: f a -> Proxy a
116fromF _ = Proxy
115fromF _ = Proxy
117
116
118fromList :: forall f . [TF f] -> TypeRepMap f
117fromList :: forall f . [TF f] -> TypeRepMap f
typerep-extra-impls/Data/TypeRep/Vector.hs+2−2
@@ -144,7 +144,7 @@ empty :: TypeRepVector f
144empty = TypeRepVect mempty mempty
144empty = TypeRepVect mempty mempty
145
145
146-- | Inserts the value with its type as a key.
146-- | Inserts the value with its type as a key.
147−insert :: forall a f . Typeable a => a -> TypeRepVector f -> TypeRepVector f
147+insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
148insert = undefined
148insert = undefined
149
149
150-- | Looks up the value at the type.
150-- | Looks up the value at the type.
@@ -164,7 +164,7 @@ size = Unboxed.length . fingerprints
164data TF f where
164data TF f where
165 TF :: Typeable a => f a -> TF f
165 TF :: Typeable a => f a -> TF f
166
166
167−fromF :: Typeable a => f a -> Proxy a
167+fromF :: f a -> Proxy a
168fromF _ = Proxy
168fromF _ = Proxy
169
169
170fromList :: forall f . [TF f] -> TypeRepVector f
170fromList :: forall f . [TF f] -> TypeRepVector f
typerep-map.cabal+18−4
@@ -48,8 +48,23 @@ common common-options
48 RecordWildCards
48 RecordWildCards
49 ScopedTypeVariables
49 ScopedTypeVariables
50 TypeApplications
50 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
51− if impl(ghc >= 8.8.1)
60+ if impl(ghc >= 8.4)
61+ ghc-options: -Wmissing-export-lists
62+ -Wpartial-fields
63+ if impl(ghc >= 8.8)
52 ghc-options: -Wmissing-deriving-strategies
64 ghc-options: -Wmissing-deriving-strategies
65+ -Werror=missing-deriving-strategies
66+ if impl(ghc >= 8.10)
67+ ghc-options: -Wunused-packages
53
68
54library
69library
55 import: common-options
70 import: common-options
@@ -58,8 +73,7 @@ library
58 Data.TypeRepMap
73 Data.TypeRepMap
59 Data.TypeRepMap.Internal
74 Data.TypeRepMap.Internal
60
75
61− build-depends: containers >= 0.5.10.2 && < 0.7
62− , ghc-prim >= 0.5.1.1 && < 0.7
76+ build-depends: ghc-prim >= 0.5.1.1 && < 0.7
63 , primitive ^>= 0.7.0
77 , primitive ^>= 0.7.0
64 , deepseq ^>= 1.4
78 , deepseq ^>= 1.4
65
79
@@ -111,7 +125,7 @@ benchmark typerep-map-benchmark
111 build-depends: criterion >= 1.4.1.0 && < 1.6
125 build-depends: criterion >= 1.4.1.0 && < 1.6
112 , deepseq ^>= 1.4.3.0
126 , deepseq ^>= 1.4.3.0
113 , dependent-map >= 0.2.4.0 && < 0.5
127 , dependent-map >= 0.2.4.0 && < 0.5
114− , dependent-sum ^>= 0.5
128+ , dependent-sum >= 0.5 && < 0.8
115 , ghc-typelits-knownnat >= 0.4.2 && < 0.8
129 , ghc-typelits-knownnat >= 0.4.2 && < 0.8
116 , typerep-map
130 , typerep-map
117 , typerep-extra-impls
131 , typerep-extra-impls