on bc49de4 help sign in
34 files changed+899−2410
since splitsnapshots
unifiedsplit
.github/CODEOWNERS−1 .github/workflows/ci.yml−47 .gitignore−1 .stylish-haskell.yaml−59 .travis.yml+66−8 CHANGELOG.md+7−53 LICENSE+21−373 README.md+17−45 appveyor.yml−35 benchmark/CMap.hs+33−53 benchmark/CacheMap.hs+32−59 benchmark/DMap.hs+57−61 benchmark/Main.hs+20−41 benchmark/OptimalVector.hs+36−34 benchmark/Spec.hs−64 benchmark/Vector.hs+22−23 src/Data/TMap.hs−220 src/Data/TypeRep/Map.hs+287 src/Data/TypeRep/TMap.hs+89 src/Data/TypeRepMap.hs−81 src/Data/TypeRepMap/Internal.hs−670 stack.yaml+2−4 test/Test.hs+1−21 test/Test/TypeRep/CMap.hs+7−9 test/Test/TypeRep/CacheMap.hs+48 test/Test/TypeRep/MapProperty.hs+69 test/Test/TypeRep/TypeRepMap.hs−111 test/Test/TypeRep/TypeRepMapProperty.hs−184 test/Test/TypeRep/Vector.hs+6−8 test/Test/TypeRep/VectorOpt.hs+6−8 typerep-extra-impls/Data/TypeRep/CMap.hs+3−16 typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−20 typerep-extra-impls/Data/TypeRep/Vector.hs+2−11 typerep-map.cabal+60−90
.github/CODEOWNERSdeleted−1
@@ -1 +0,0 @@
1−* @chshersh @vrom911
.github/workflows/ci.ymldeleted−47
@@ -1,47 +0,0 @@
1−name: CI
2−
3−# Trigger the workflow on push or pull request, but only for the main branch
4−on:
5− push:
6− branches:
7− - main
8− pull_request:
9−
10−jobs:
11− build:
12− name: ghc ${{ matrix.ghc }}
13− runs-on: ubuntu-16.04
14− strategy:
15− matrix:
16− cabal: ["3.0"]
17− ghc:
18− - "8.2.2"
19− - "8.4.4"
20− - "8.6.5"
21− - "8.8.3"
22− - "8.10.1"
23−
24− steps:
25− - uses: actions/checkout@v2
26− if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/main'
27−
28− - uses: actions/setup-haskell@v1
29− name: Setup Haskell
30− with:
31− ghc-version: ${{ matrix.ghc }}
32− cabal-version: ${{ matrix.cabal }}
33−
34− - uses: actions/cache@v1
35− name: Cache ~/.cabal/store
36− with:
37− path: ~/.cabal/store
38− key: ${{ runner.os }}-${{ matrix.ghc }}-cabal
39−
40− - name: Build
41− run: |
42− cabal update
43− cabal build --enable-tests --enable-benchmarks all
44−
45− - name: Test
46− run: |
47− cabal test --enable-tests --test-show-details=direct all
.gitignore−1
@@ -24,7 +24,6 @@ cabal.project.local
2424
2525# Stack
2626.stack-work/
27−stack.yaml.lock
2827
2928### IDE/support
3029# Vim
.stylish-haskell.yamldeleted−59
@@ -1,59 +0,0 @@
1−steps:
2− - simple_align:
3− cases: true
4− top_level_patterns: true
5− records: true
6−
7− # Import cleanup
8− - imports:
9− align: none
10− list_align: after_alias
11− pad_module_names: false
12− long_list_align: inline
13− empty_list_align: inherit
14− list_padding: 4
15− separate_lists: true
16− space_surround: false
17−
18− - language_pragmas:
19− style: vertical
20− remove_redundant: true
21−
22− # Remove trailing whitespace
23− - trailing_whitespace: {}
24−
25−columns: 100
26−
27−newline: native
28−
29−language_extensions:
30− - BangPatterns
31− - ConstraintKinds
32− - DataKinds
33− - DefaultSignatures
34− - DeriveAnyClass
35− - DeriveDataTypeable
36− - DeriveGeneric
37− - DerivingStrategies
38− - FlexibleContexts
39− - FlexibleInstances
40− - FunctionalDependencies
41− - GADTs
42− - GeneralizedNewtypeDeriving
43− - InstanceSigs
44− - KindSignatures
45− - LambdaCase
46− - MultiParamTypeClasses
47− - MultiWayIf
48− - NamedFieldPuns
49− - NoImplicitPrelude
50− - OverloadedStrings
51− - QuasiQuotes
52− - RecordWildCards
53− - ScopedTypeVariables
54− - StandaloneDeriving
55− - TemplateHaskell
56− - TupleSections
57− - TypeApplications
58− - TypeFamilies
59− - ViewPatterns
.travis.yml+66−8
@@ -6,23 +6,81 @@ git:
66
77cache:
88 directories:
9+ - "$HOME/.cabal"
10+ - "$HOME/.ghc"
911 - "$HOME/.stack"
12+ - ".stack-work"
1013
1114matrix:
15+
1216 include:
13− - env: GHC='8.8.3'
17+
18+ - ghc: 8.0.2
19+ env: GHCVER='8.0.2' CABALVER='head' ALLOWNEWER='--allow-newer'
20+ os: linux
21+ addons:
22+ apt:
23+ sources:
24+ - hvr-ghc
25+ packages:
26+ - ghc-8.0.2
27+ - cabal-install-head
28+
29+ - ghc: 8.2.2
30+ env: GHCVER='8.2.2' CABALVER='head'
31+ os: linux
32+ addons:
33+ apt:
34+ sources:
35+ - hvr-ghc
36+ packages:
37+ - ghc-8.2.2
38+ - cabal-install-head
39+
40+ - ghc: 8.4.3
41+ env: GHCVER='8.4.3' CABALVER='head'
42+ os: linux
43+ addons:
44+ apt:
45+ sources:
46+ - hvr-ghc
47+ packages:
48+ - ghc-8.4.3
49+ - cabal-install-head
50+
51+ - ghc: 8.4.3
52+ env: STACK_YAML="$HOME/build/kowainik/typerep-map/stack.yaml"
1453 os: linux
15− - env: GHC='8.8.3'
16− os: osx
54+ addons:
55+ apt:
56+ packages:
57+ - libgmp-dev
1758
1859install:
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
60+ - |
61+ if [ -z "$STACK_YAML" ]; then
62+ export PATH="/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH"
63+ echo $PATH
64+ cabal new-update
65+ cabal new-build --enable-tests --enable-benchmarks
66+ else
67+ echo "$TRAVIS_BUILD_DIR"
68+ mkdir -p ~/.local/bin
69+ export PATH="$HOME/.local/bin:$PATH"
70+ travis_retry curl -L 'https://www.stackage.org/stack/linux-x86_64' | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
71+ stack --version
72+ stack setup --no-terminal --install-cabal 2.0.1.0
73+ stack ghc -- --version
74+ stack build --only-dependencies --no-terminal
75+ fi
2376
2477script:
25− - stack test --no-terminal
78+ - |
79+ if [ -z "$STACK_YAML" ]; then
80+ cabal new-test $ALLOWNEWER
81+ else
82+ stack build --test --bench --no-terminal
83+ fi
2684
2785notifications:
2886 email: false
CHANGELOG.md+7−53
@@ -1,56 +1,10 @@
1−# Changelog
1+Change log
2+==========
23
3−`typerep-map` uses [PVP Versioning][1].
4−The changelog is available [on GitHub][2].
5−
6−## 0.3.3.0 — Apr 18, 2020
7−
8−* [#83](https://github.com/kowainik/typerep-map/issues/83):
9− Support GHC-8.10.
10−* [#80](https://github.com/kowainik/typerep-map/issues/80):
11− Build on 32-bit platforms.
12−* [#78](https://github.com/kowainik/typerep-map/issues/78):
13− Support GHC-8.8.3.
14−* [#63](https://github.com/kowainik/typerep-map/pull/63):
15− Add invariant check.
16−
17−## 0.3.2 — Mar 27, 2019
18−
19−* [#47](https://github.com/kowainik/typerep-map/issues/47):
20− Add `Eq` instance for `TypeRepMap` using `-XQuantifiedConstraints`.
21−* [#70](https://github.com/kowainik/typerep-map/issues/70):
22− Bump up to `dependent-sum-0.5`.
23−
24−## 0.3.1
25−
26−* [#64](https://github.com/kowainik/typerep-map/issues/64):
27− Fix segfault in `toList`.
28−* Support GHC 8.4.4 and 8.6.3.
29−
30−## 0.3.0
31−
32−* [#46](https://github.com/kowainik/typerep-map/issues/46):
33− Make `Show` instance for `TypeRepMap` show keys.
34− Add `keys` function.
35−* [#48](https://github.com/kowainik/typerep-map/issues/48):
36− Add `adjust` function for `TypeRepMap` and `TMap`.
37−* [#30](https://github.com/kowainik/typerep-map/issues/30):
38− Rewrite `fromSortedList` to use `Array` and `MutableArray`
39− instead of `IntMap`.
40−
41−## 0.2.0
42−
43−* [#43](https://github.com/kowainik/typerep-map/issues/43):
44− Implement `IsList` instance for `TypeRepMap`.
45− Add `hoistA` function.
46−* [#39](https://github.com/kowainik/typerep-map/issues/39):
47− Implement `hoistWithKey` function.
48− Add `map` function for `TMap`.
49−* Drop support for `ghc-8.0.2`.
50−
51−## 0.1.0
52−
53−* Initially created.
4+typerep-map uses [PVP Versioning][1].
5+The change log is available [on GitHub][2].
546
557[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
8+[2]: https://github.com/vrom911/typerep-map/releases
9+# 0.1.0.0
10+* Initially created.
LICENSE+21−373
@@ -1,373 +1,21 @@
1−Mozilla Public License Version 2.0
2−==================================
3−
4−1. Definitions
5−--------------
6−
7−1.1. "Contributor"
8− means each individual or legal entity that creates, contributes to
9− the creation of, or owns Covered Software.
10−
11−1.2. "Contributor Version"
12− means the combination of the Contributions of others (if any) used
13− by a Contributor and that particular Contributor's Contribution.
14−
15−1.3. "Contribution"
16− means Covered Software of a particular Contributor.
17−
18−1.4. "Covered Software"
19− means Source Code Form to which the initial Contributor has attached
20− the notice in Exhibit A, the Executable Form of such Source Code
21− Form, and Modifications of such Source Code Form, in each case
22− including portions thereof.
23−
24−1.5. "Incompatible With Secondary Licenses"
25− means
26−
27− (a) that the initial Contributor has attached the notice described
28− in Exhibit B to the Covered Software; or
29−
30− (b) that the Covered Software was made available under the terms of
31− version 1.1 or earlier of the License, but not also under the
32− terms of a Secondary License.
33−
34−1.6. "Executable Form"
35− means any form of the work other than Source Code Form.
36−
37−1.7. "Larger Work"
38− means a work that combines Covered Software with other material, in
39− a separate file or files, that is not Covered Software.
40−
41−1.8. "License"
42− means this document.
43−
44−1.9. "Licensable"
45− means having the right to grant, to the maximum extent possible,
46− whether at the time of the initial grant or subsequently, any and
47− all of the rights conveyed by this License.
48−
49−1.10. "Modifications"
50− means any of the following:
51−
52− (a) any file in Source Code Form that results from an addition to,
53− deletion from, or modification of the contents of Covered
54− Software; or
55−
56− (b) any new file in Source Code Form that contains any Covered
57− Software.
58−
59−1.11. "Patent Claims" of a Contributor
60− means any patent claim(s), including without limitation, method,
61− process, and apparatus claims, in any patent Licensable by such
62− Contributor that would be infringed, but for the grant of the
63− License, by the making, using, selling, offering for sale, having
64− made, import, or transfer of either its Contributions or its
65− Contributor Version.
66−
67−1.12. "Secondary License"
68− means either the GNU General Public License, Version 2.0, the GNU
69− Lesser General Public License, Version 2.1, the GNU Affero General
70− Public License, Version 3.0, or any later versions of those
71− licenses.
72−
73−1.13. "Source Code Form"
74− means the form of the work preferred for making modifications.
75−
76−1.14. "You" (or "Your")
77− means an individual or a legal entity exercising rights under this
78− License. For legal entities, "You" includes any entity that
79− controls, is controlled by, or is under common control with You. For
80− purposes of this definition, "control" means (a) the power, direct
81− or indirect, to cause the direction or management of such entity,
82− whether by contract or otherwise, or (b) ownership of more than
83− fifty percent (50%) of the outstanding shares or beneficial
84− ownership of such entity.
85−
86−2. License Grants and Conditions
87−--------------------------------
88−
89−2.1. Grants
90−
91−Each Contributor hereby grants You a world-wide, royalty-free,
92−non-exclusive license:
93−
94−(a) under intellectual property rights (other than patent or trademark)
95− Licensable by such Contributor to use, reproduce, make available,
96− modify, display, perform, distribute, and otherwise exploit its
97− Contributions, either on an unmodified basis, with Modifications, or
98− as part of a Larger Work; and
99−
100−(b) under Patent Claims of such Contributor to make, use, sell, offer
101− for sale, have made, import, and otherwise transfer either its
102− Contributions or its Contributor Version.
103−
104−2.2. Effective Date
105−
106−The licenses granted in Section 2.1 with respect to any Contribution
107−become effective for each Contribution on the date the Contributor first
108−distributes such Contribution.
109−
110−2.3. Limitations on Grant Scope
111−
112−The licenses granted in this Section 2 are the only rights granted under
113−this License. No additional rights or licenses will be implied from the
114−distribution or licensing of Covered Software under this License.
115−Notwithstanding Section 2.1(b) above, no patent license is granted by a
116−Contributor:
117−
118−(a) for any code that a Contributor has removed from Covered Software;
119− or
120−
121−(b) for infringements caused by: (i) Your and any other third party's
122− modifications of Covered Software, or (ii) the combination of its
123− Contributions with other software (except as part of its Contributor
124− Version); or
125−
126−(c) under Patent Claims infringed by Covered Software in the absence of
127− its Contributions.
128−
129−This License does not grant any rights in the trademarks, service marks,
130−or logos of any Contributor (except as may be necessary to comply with
131−the notice requirements in Section 3.4).
132−
133−2.4. Subsequent Licenses
134−
135−No Contributor makes additional grants as a result of Your choice to
136−distribute the Covered Software under a subsequent version of this
137−License (see Section 10.2) or under the terms of a Secondary License (if
138−permitted under the terms of Section 3.3).
139−
140−2.5. Representation
141−
142−Each Contributor represents that the Contributor believes its
143−Contributions are its original creation(s) or it has sufficient rights
144−to grant the rights to its Contributions conveyed by this License.
145−
146−2.6. Fair Use
147−
148−This License is not intended to limit any rights You have under
149−applicable copyright doctrines of fair use, fair dealing, or other
150−equivalents.
151−
152−2.7. Conditions
153−
154−Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155−in Section 2.1.
156−
157−3. Responsibilities
158−-------------------
159−
160−3.1. Distribution of Source Form
161−
162−All distribution of Covered Software in Source Code Form, including any
163−Modifications that You create or to which You contribute, must be under
164−the terms of this License. You must inform recipients that the Source
165−Code Form of the Covered Software is governed by the terms of this
166−License, and how they can obtain a copy of this License. You may not
167−attempt to alter or restrict the recipients' rights in the Source Code
168−Form.
169−
170−3.2. Distribution of Executable Form
171−
172−If You distribute Covered Software in Executable Form then:
173−
174−(a) such Covered Software must also be made available in Source Code
175− Form, as described in Section 3.1, and You must inform recipients of
176− the Executable Form how they can obtain a copy of such Source Code
177− Form by reasonable means in a timely manner, at a charge no more
178− than the cost of distribution to the recipient; and
179−
180−(b) You may distribute such Executable Form under the terms of this
181− License, or sublicense it under different terms, provided that the
182− license for the Executable Form does not attempt to limit or alter
183− the recipients' rights in the Source Code Form under this License.
184−
185−3.3. Distribution of a Larger Work
186−
187−You may create and distribute a Larger Work under terms of Your choice,
188−provided that You also comply with the requirements of this License for
189−the Covered Software. If the Larger Work is a combination of Covered
190−Software with a work governed by one or more Secondary Licenses, and the
191−Covered Software is not Incompatible With Secondary Licenses, this
192−License permits You to additionally distribute such Covered Software
193−under the terms of such Secondary License(s), so that the recipient of
194−the Larger Work may, at their option, further distribute the Covered
195−Software under the terms of either this License or such Secondary
196−License(s).
197−
198−3.4. Notices
199−
200−You may not remove or alter the substance of any license notices
201−(including copyright notices, patent notices, disclaimers of warranty,
202−or limitations of liability) contained within the Source Code Form of
203−the Covered Software, except that You may alter any license notices to
204−the extent required to remedy known factual inaccuracies.
205−
206−3.5. Application of Additional Terms
207−
208−You may choose to offer, and to charge a fee for, warranty, support,
209−indemnity or liability obligations to one or more recipients of Covered
210−Software. However, You may do so only on Your own behalf, and not on
211−behalf of any Contributor. You must make it absolutely clear that any
212−such warranty, support, indemnity, or liability obligation is offered by
213−You alone, and You hereby agree to indemnify every Contributor for any
214−liability incurred by such Contributor as a result of warranty, support,
215−indemnity or liability terms You offer. You may include additional
216−disclaimers of warranty and limitations of liability specific to any
217−jurisdiction.
218−
219−4. Inability to Comply Due to Statute or Regulation
220−---------------------------------------------------
221−
222−If it is impossible for You to comply with any of the terms of this
223−License with respect to some or all of the Covered Software due to
224−statute, judicial order, or regulation then You must: (a) comply with
225−the terms of this License to the maximum extent possible; and (b)
226−describe the limitations and the code they affect. Such description must
227−be placed in a text file included with all distributions of the Covered
228−Software under this License. Except to the extent prohibited by statute
229−or regulation, such description must be sufficiently detailed for a
230−recipient of ordinary skill to be able to understand it.
231−
232−5. Termination
233−--------------
234−
235−5.1. The rights granted under this License will terminate automatically
236−if You fail to comply with any of its terms. However, if You become
237−compliant, then the rights granted under this License from a particular
238−Contributor are reinstated (a) provisionally, unless and until such
239−Contributor explicitly and finally terminates Your grants, and (b) on an
240−ongoing basis, if such Contributor fails to notify You of the
241−non-compliance by some reasonable means prior to 60 days after You have
242−come back into compliance. Moreover, Your grants from a particular
243−Contributor are reinstated on an ongoing basis if such Contributor
244−notifies You of the non-compliance by some reasonable means, this is the
245−first time You have received notice of non-compliance with this License
246−from such Contributor, and You become compliant prior to 30 days after
247−Your receipt of the notice.
248−
249−5.2. If You initiate litigation against any entity by asserting a patent
250−infringement claim (excluding declaratory judgment actions,
251−counter-claims, and cross-claims) alleging that a Contributor Version
252−directly or indirectly infringes any patent, then the rights granted to
253−You by any and all Contributors for the Covered Software under Section
254−2.1 of this License shall terminate.
255−
256−5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257−end user license agreements (excluding distributors and resellers) which
258−have been validly granted by You or Your distributors under this License
259−prior to termination shall survive termination.
260−
261−************************************************************************
262−* *
263−* 6. Disclaimer of Warranty *
264−* ------------------------- *
265−* *
266−* Covered Software is provided under this License on an "as is" *
267−* basis, without warranty of any kind, either expressed, implied, or *
268−* statutory, including, without limitation, warranties that the *
269−* Covered Software is free of defects, merchantable, fit for a *
270−* particular purpose or non-infringing. The entire risk as to the *
271−* quality and performance of the Covered Software is with You. *
272−* Should any Covered Software prove defective in any respect, You *
273−* (not any Contributor) assume the cost of any necessary servicing, *
274−* repair, or correction. This disclaimer of warranty constitutes an *
275−* essential part of this License. No use of any Covered Software is *
276−* authorized under this License except under this disclaimer. *
277−* *
278−************************************************************************
279−
280−************************************************************************
281−* *
282−* 7. Limitation of Liability *
283−* -------------------------- *
284−* *
285−* Under no circumstances and under no legal theory, whether tort *
286−* (including negligence), contract, or otherwise, shall any *
287−* Contributor, or anyone who distributes Covered Software as *
288−* permitted above, be liable to You for any direct, indirect, *
289−* special, incidental, or consequential damages of any character *
290−* including, without limitation, damages for lost profits, loss of *
291−* goodwill, work stoppage, computer failure or malfunction, or any *
292−* and all other commercial damages or losses, even if such party *
293−* shall have been informed of the possibility of such damages. This *
294−* limitation of liability shall not apply to liability for death or *
295−* personal injury resulting from such party's negligence to the *
296−* extent applicable law prohibits such limitation. Some *
297−* jurisdictions do not allow the exclusion or limitation of *
298−* incidental or consequential damages, so this exclusion and *
299−* limitation may not apply to You. *
300−* *
301−************************************************************************
302−
303−8. Litigation
304−-------------
305−
306−Any litigation relating to this License may be brought only in the
307−courts of a jurisdiction where the defendant maintains its principal
308−place of business and such litigation shall be governed by laws of that
309−jurisdiction, without reference to its conflict-of-law provisions.
310−Nothing in this Section shall prevent a party's ability to bring
311−cross-claims or counter-claims.
312−
313−9. Miscellaneous
314−----------------
315−
316−This License represents the complete agreement concerning the subject
317−matter hereof. If any provision of this License is held to be
318−unenforceable, such provision shall be reformed only to the extent
319−necessary to make it enforceable. Any law or regulation which provides
320−that the language of a contract shall be construed against the drafter
321−shall not be used to construe this License against a Contributor.
322−
323−10. Versions of the License
324−---------------------------
325−
326−10.1. New Versions
327−
328−Mozilla Foundation is the license steward. Except as provided in Section
329−10.3, no one other than the license steward has the right to modify or
330−publish new versions of this License. Each version will be given a
331−distinguishing version number.
332−
333−10.2. Effect of New Versions
334−
335−You may distribute the Covered Software under the terms of the version
336−of the License under which You originally received the Covered Software,
337−or under the terms of any subsequent version published by the license
338−steward.
339−
340−10.3. Modified Versions
341−
342−If you create software not governed by this License, and you want to
343−create a new license for such software, you may create and use a
344−modified version of this License if you rename the license and remove
345−any references to the name of the license steward (except to note that
346−such modified license differs from this License).
347−
348−10.4. Distributing Source Code Form that is Incompatible With Secondary
349−Licenses
350−
351−If You choose to distribute Source Code Form that is Incompatible With
352−Secondary Licenses under the terms of this version of the License, the
353−notice described in Exhibit B of this License must be attached.
354−
355−Exhibit A - Source Code Form License Notice
356−-------------------------------------------
357−
358− This Source Code Form is subject to the terms of the Mozilla Public
359− License, v. 2.0. If a copy of the MPL was not distributed with this
360− file, You can obtain one at http://mozilla.org/MPL/2.0/.
361−
362−If it is not possible or desirable to put the notice in a particular
363−file, then You may include the notice in a location (such as a LICENSE
364−file in a relevant directory) where a recipient would be likely to look
365−for such a notice.
366−
367−You may add additional accurate notices of copyright ownership.
368−
369−Exhibit B - "Incompatible With Secondary Licenses" Notice
370−---------------------------------------------------------
371−
372− This Source Code Form is "Incompatible With Secondary Licenses", as
373− defined by the Mozilla Public License, v. 2.0.
1+MIT License
2+
3+Copyright (c) 2017 Veronika Romashkina
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
README.md+17−45
@@ -1,66 +1,38 @@
11# typerep-map
22
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)
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)
8−[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
3+[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg)](https://hackage.haskell.org/package/typerep-map)
4+[![Build status](https://secure.travis-ci.org/kowainik/typerep-map.svg)](https://travis-ci.org/kowainik/typerep-map)
95[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
106[![Stackage Nightly](http://stackage.org/package/typerep-map/badge/nightly)](http://stackage.org/nightly/package/typerep-map)
11−[![MPL-2.0 license](https://img.shields.io/badge/license-MPL--2.0-blue.svg)](LICENSE)
12−
13−
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.
15−
16−For the more details on the implementation see the following blog post:
17−
18−* [typerep-map step by step](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step)
7+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
198
20−## Usage example
9+`typerep-map` introduces `TypeRepMap` — data structure like [`Map`](http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#t:Map), but where types serve as keys, and values have the types specified in the corresponding key spots.
2110
2211```haskell
23−ghci> import Data.TMap
12+ghci> let typeRepMap = insert (Identity True) $ one (Identity (42 :: Int))
2413
25−ghci> tm = insert True $ one (42 :: Int)
26−
27−ghci> size tm
14+ghci> size typeRepMap
28152
2916
30−ghci> res = lookup tm
17+ghci> let res = lookup typeRepMap
3118
32−ghci> res :: Maybe Int
33−Just 42
19+ghci> res :: Maybe (Identity Int)
20+Just (Identity 42)
3421
35−ghci> res :: Maybe Bool
36−Just True
22+ghci> res :: Maybe (Identity Bool)
23+Just (Identity True)
3724
38−ghci> res :: Maybe String
25+ghci> res :: Maybe (Identity String)
3926Nothing
4027
41−ghci> lookup (insert "hello" tm) :: Maybe String
42−Just "hello"
28+ghci> lookup (insert (Identity "hello") typeRepMap) :: Maybe (Identity String)
29+Just (Identity "hello")
4330
44−ghci> member @Int tm
31+ghci> member @Int typeRepMap
4532True
4633
47−ghci> tm' = delete @Int tm
34+ghci> let trMap = delete @Int typeRepMap
4835
49−ghci> member @Int tm'
36+ghci> member @Int trMap
5037False
5138```
52−
53−## Benchmarks
54−
55−Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
56−on structure with size `10^4`:
57−
58−| | ghc-8.2.2 | ghc-8.4.3 | ghc-8.8.3 | ghc-8.10.1 |
59−|----------------|-----------|-----------|-----------|------------|
60−| `DMap TypeRep` | 517.5 ns | 779.9 ns | 1.559 μs | 1.786 μs |
61−| `typerep-map` | 205.3 ns | 187.2 ns | 190.1 ns | 169.1 ns |
62−
63− ghc-8.2.2 | ghc-8.4.3
64−:---------:|:-----------:
65−![DMap 8.2.2](https://user-images.githubusercontent.com/4276606/42495129-c700f21e-8454-11e8-98b4-ba080259c712.png) | ![DMap 8.4.3](https://user-images.githubusercontent.com/4276606/42495168-ebb1d13c-8454-11e8-9d17-f6da29d2169a.png)
66−![TMap 8.2.2](https://user-images.githubusercontent.com/4276606/42494935-3a352d96-8454-11e8-985e-ebc77cc51ca0.png) | ![TMap 8.4.3](https://user-images.githubusercontent.com/4276606/42495147-d884bdf4-8454-11e8-887f-9815fd2b8d68.png)
appveyor.ymldeleted−35
@@ -1,35 +0,0 @@
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+33−53
@@ -1,77 +1,57 @@
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+ ( benchMap
13+ , prepareBenchMap
14+ ) where
15+
16+import Criterion.Main (Benchmark, bench, bgroup, nf)
1217
1318import Prelude hiding (lookup)
1419
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
20+import Control.DeepSeq (rnf)
21+import Control.Exception
1722import Data.Maybe (fromJust)
1823import Data.Proxy (Proxy (..))
1924import Data.Typeable (Typeable)
20−import GHC.TypeLits (type (+), KnownNat, Nat)
25+import GHC.TypeLits
2126
22−import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
27+import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, keys, lookup)
2328
24−import Spec (BenchSpec (..))
29+benchMap :: Benchmark
30+benchMap = bgroup "map"
31+ [ bench "lookup" $ nf tenLookups bigMap
32+ --, bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
33+ --, bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
34+ ]
2535
26−
27−spec :: BenchSpec
28−spec = 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− }
44−
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− )
36+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
37+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
38+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
39+ )
5040tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5141 where
5242 lp :: forall (a::Nat). Typeable a => Proxy a
5343 lp = fromJust $ lookup tmap
5444
55−inserts
56− :: forall a . (KnownNat a)
57− => TypeRepMap (Proxy :: Nat -> Type)
58− -> Int
59− -> Proxy (a :: Nat)
60− -> TypeRepMap (Proxy :: Nat -> Type)
61−inserts !c 0 _ = c
62−inserts !c n x = inserts
63− (insert x c)
64− (n-1)
65− (Proxy :: Proxy (a+1))
66−
67−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
68−mkMap n = pure $ buildBigMap n (Proxy :: Proxy 0) empty
45+-- TypeRepMap of 10000 elements
46+bigMap :: TypeRepMap (Proxy :: Nat -> *)
47+bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
6948
70−buildBigMap
71− :: forall a . (KnownNat a)
72− => Int
73− -> Proxy (a :: Nat)
74− -> TypeRepMap (Proxy :: Nat -> Type)
75− -> TypeRepMap (Proxy :: Nat -> Type)
49+buildBigMap :: forall a . (KnownNat a) => Int -> Proxy (a :: Nat) -> TypeRepMap (Proxy :: Nat -> *) -> TypeRepMap (Proxy :: Nat -> *)
7650buildBigMap 1 x = insert x
7751buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
52+
53+rknf :: TypeRepMap f -> ()
54+rknf = rnf . keys
55+
56+prepareBenchMap :: IO ()
57+prepareBenchMap = evaluate (rknf bigMap)
benchmark/CacheMap.hs+32−59
@@ -1,79 +1,52 @@
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+ ( benchCacheMap
13+ ) where
14+
15+import Criterion.Main (Benchmark, bench, bgroup, nf)
1216
1317import Prelude hiding (lookup)
1418
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
1719import Data.Maybe (fromJust)
1820import Data.Proxy (Proxy (..))
1921import Data.Typeable (Typeable)
20−import GHC.Exts (fromList)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
22+import GHC.TypeLits
2223
23−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
24+import Data.TypeRep.Map (TF (..), TypeRepMap (..), fromList, lookup)
2425
25−import Spec (BenchSpec (..))
26+benchCacheMap :: Benchmark
27+benchCacheMap = bgroup "vector optimal cache"
28+ [ bench "lookup" $ nf tenLookups bigMap
29+ -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
30+ -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
31+ ]
2632
27−
28−spec :: BenchSpec
29−spec = 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− }
45−
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− )
33+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
34+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
35+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
36+ )
5137tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5238 where
53− lp :: forall (a :: Nat) . Typeable a => Proxy a
39+ lp :: forall (a::Nat). Typeable a => Proxy a
5440 lp = fromJust $ lookup tmap
5541
56−inserts
57− :: forall a . (KnownNat a)
58− => TypeRepMap (Proxy :: Nat -> Type)
59− -> Int
60− -> Proxy (a :: Nat)
61− -> TypeRepMap (Proxy :: Nat -> Type)
62−inserts !c 0 _ = c
63−inserts !c n x = inserts
64− (insert x c)
65− (n-1)
66− (Proxy :: Proxy (a + 1))
67−
68−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
69−mkMap n = pure $ fromList $ buildBigMap n (Proxy :: Proxy 0) []
70−
71−
72−buildBigMap
73− :: forall a . (KnownNat a)
74− => Int
75− -> Proxy (a :: Nat)
76− -> [WrapTypeable (Proxy :: Nat -> Type)]
77− -> [WrapTypeable (Proxy :: Nat -> Type)]
78−buildBigMap 1 x = (WrapTypeable x :)
79−buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
42+-- TypeRepMap of 10000 elements
43+bigMap :: TypeRepMap (Proxy :: Nat -> *)
44+bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
45+
46+buildBigMap :: forall a . (KnownNat a)
47+ => Int
48+ -> Proxy (a :: Nat)
49+ -> [TF (Proxy :: Nat -> *)]
50+ -> [TF (Proxy :: Nat -> *)]
51+buildBigMap 1 x = (TF x :)
52+buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hs+57−61
@@ -1,89 +1,85 @@
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 #-}
9+{-# LANGUAGE ViewPatterns #-}
10+
11+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver -fno-warn-orphans #-}
912
1013module DMap
11− ( spec
12− ) where
14+ ( benchDMap
15+ , prepareBenchDMap
16+ ) where
17+
18+import Criterion.Main (Benchmark, bench, bgroup, nf)
1319
1420import Prelude hiding (lookup)
1521
16−import Control.DeepSeq (NFData (..))
17−import Criterion.Main (bench, env, nf, whnf)
18−import Data.Kind (Type)
22+import Control.DeepSeq (rnf)
23+import Control.Exception
24+import Data.Functor.Identity (Identity (..))
1925import Data.Maybe (fromJust)
2026import Data.Proxy (Proxy (..))
21−import GHC.TypeLits (type (+), KnownNat, Nat)
27+import Data.Type.Equality ((:~:) (..))
28+import GHC.TypeLits
2229import Type.Reflection (TypeRep, Typeable, typeRep)
2330import Type.Reflection.Unsafe (typeRepFingerprint)
31+import Unsafe.Coerce (unsafeCoerce)
2432
2533import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
26−import Data.Some (Some (Some))
27−
28−import Spec (BenchSpec (..))
29−
34+import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
35+import Data.Some (Some (This))
3036
31−type TypeRepMap = DMap TypeRep
37+benchDMap :: Benchmark
38+benchDMap = bgroup "dependent map"
39+ [ bench "lookup" $ nf tenLookups bigMap
40+ -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
41+ -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
42+ ]
3243
33−spec :: BenchSpec
34−spec = 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− }
46−
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− )
44+tenLookups :: DMap TypeRep Identity
45+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
46+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
47+ )
5248tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5349 where
5450 lp :: forall (a :: Nat) . Typeable a => Proxy a
55− lp = fromJust $ lookup (typeRep @a) tmap
56−
57−inserts
58− :: forall a . (KnownNat a)
59− => TypeRepMap (Proxy :: Nat -> Type)
60− -> Int
61− -> Proxy (a :: Nat)
62− -> TypeRepMap (Proxy :: Nat -> Type)
63−inserts !c 0 _ = c
64−inserts !c n x = inserts
65− (insert (typeRep @ a) x c)
66− (n-1)
67− (Proxy :: Proxy (a+1))
51+ lp = runIdentity $ fromJust $ lookup (typeRep @(Proxy a)) tmap
6852
6953-- TypeRepMap of 10000 elements
70−mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
71−mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
54+bigMap :: DMap TypeRep Identity
55+bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
7256
73−buildBigMap
74− :: forall a . (KnownNat a)
75− => Int
76− -> Proxy (a :: Nat)
77− -> TypeRepMap (Proxy :: Nat -> Type)
78− -> TypeRepMap (Proxy :: Nat -> Type)
79−buildBigMap 1 x = insert (typeRep @a) x
80−buildBigMap n x = insert (typeRep @a) x
57+buildBigMap :: forall a . (KnownNat a)
58+ => Int
59+ -> Proxy (a :: Nat)
60+ -> DMap TypeRep Identity
61+ -> DMap TypeRep Identity
62+buildBigMap 1 x = insert (typeRep @(Proxy a)) $ Identity x
63+buildBigMap n x = insert (typeRep @(Proxy a)) (Identity x)
8164 . buildBigMap (n - 1) (Proxy @(a + 1))
8265
83−-- | Wrapper that provides NFData instance to the 'DMap' structure.
84−newtype DMapNF f = DMapNF (TypeRepMap f)
66+rknf :: DMap TypeRep f -> ()
67+rknf = rnf . map (\(This t) -> typeRepFingerprint t) . keys
68+
69+prepareBenchDMap :: IO ()
70+prepareBenchDMap = evaluate (rknf bigMap)
71+
72+instance GEq TypeRep where
73+ geq :: TypeRep a -> TypeRep b -> Maybe (a :~: b)
74+ geq (typeRepFingerprint -> a) (typeRepFingerprint -> b) =
75+ if a == b
76+ then Just $ unsafeCoerce Refl
77+ else Nothing
8578
86−instance NFData (DMapNF f) where
87− rnf :: DMapNF f -> ()
88− rnf (DMapNF x) =
89− rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
79+instance GCompare TypeRep where
80+ gcompare :: TypeRep a -> TypeRep b -> GOrdering a b
81+ gcompare (typeRepFingerprint -> a) (typeRepFingerprint -> b) =
82+ case compare a b of
83+ EQ -> unsafeCoerce GEQ
84+ LT -> GLT
85+ GT -> GGT
benchmark/Main.hs+20−41
@@ -1,52 +1,31 @@
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)
86
9−import Spec (BenchSpec (..))
10−
11−import qualified CacheMap
12−import qualified CMap
7+import CacheMap (benchCacheMap)
8+import CMap (benchMap, prepareBenchMap)
139#if ( __GLASGOW_HASKELL__ >= 802 )
14−import qualified DMap
10+import DMap (benchDMap, prepareBenchDMap)
1511#endif
16−import qualified OptimalVector as OptVec
17−
12+import OptimalVector (benchVectorOpt, prepareBenchVectorOpt)
13+--import Vector (benchVector, prepareBenchVector)
1814
1915main :: IO ()
2016main = do
21− let specs = [("CMap", CMap.spec)
22− ,("CacheMap", CacheMap.spec)
17+ prepareBenchMap
18+ --prepareBenchVector
19+ prepareBenchVectorOpt
20+#if ( __GLASGOW_HASKELL__ >= 802 )
21+ prepareBenchDMap
22+#endif
23+ defaultMain
24+ [ benchMap
25+ -- , benchVector
26+ , benchCacheMap
27+ , benchVectorOpt
2328#if ( __GLASGOW_HASKELL__ >= 802 )
24− , ("DMap", DMap.spec)
29+ , benchDMap
2530#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− ]
52− ]
31+ ]
benchmark/OptimalVector.hs+36−34
@@ -1,60 +1,62 @@
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
11− ( spec
13+ ( benchVectorOpt
14+ , prepareBenchVectorOpt
1215 ) where
1316
17+import Criterion.Main (Benchmark, bench, bgroup, nf)
18+
1419import Prelude hiding (lookup)
1520
16−import Criterion.Main (bench, env, nf)
17−import Data.Kind (Type)
21+import Control.DeepSeq (rnf)
22+import Control.Exception
1823import Data.Maybe (fromJust)
1924import Data.Proxy (Proxy (..))
2025import Data.Typeable (Typeable)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
26+import GHC.TypeLits
2227
2328import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
2429
25−import Spec (BenchSpec (..))
26−
27−
28−spec :: BenchSpec
29−spec = 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+benchVectorOpt :: Benchmark
31+benchVectorOpt = bgroup "vector optimal"
32+ [ bench "lookup" $ nf tenLookups bigMap
33+ -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
34+ -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
35+ ]
36+
37+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
38+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40+ )
4441tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4542 where
4643 lp :: forall (a::Nat). Typeable a => Proxy a
4744 lp = fromJust $ lookup tmap
4845
4946-- TypeRepMap of 10000 elements
50−mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> Type))
51−mkBigMap = pure $ fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
52−
53−buildBigMap
54− :: forall a . (KnownNat a)
55− => Int
56− -> Proxy (a :: Nat)
57− -> [TF (Proxy :: Nat -> Type)]
58− -> [TF (Proxy :: Nat -> Type)]
47+bigMap :: TypeRepMap (Proxy :: Nat -> *)
48+bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
49+
50+buildBigMap :: forall a . (KnownNat a)
51+ => Int
52+ -> Proxy (a :: Nat)
53+ -> [TF (Proxy :: Nat -> *)]
54+ -> [TF (Proxy :: Nat -> *)]
5955buildBigMap 1 x = (TF x :)
6056buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
57+
58+rknf :: TypeRepMap f -> ()
59+rknf tVect = rnf (fingerprintAs tVect, fingerprintBs tVect)
60+
61+prepareBenchVectorOpt :: IO ()
62+prepareBenchVectorOpt = evaluate (rknf bigMap)
benchmark/Spec.hsdeleted−64
@@ -1,64 +0,0 @@
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−-}
7−module 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−-}
22−data 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:
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+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.hsdeleted−220
@@ -1,220 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE Rank2Types #-}
3−
4−{- |
5−Copyright: (c) 2017-2020 Kowainik
6−SPDX-License-Identifier: MPL-2.0
7−Maintainer: Kowainik <xrom.xkov@gmail.com>
8−
9−'TMap' is a heterogeneous data structure similar in its essence to
10−'Data.Map.Map' with types as keys, where each value has the type of its key.
11−
12−Here is an example of a 'TMap' with a comparison to 'Data.Map.Map':
13−
14−@
15− 'Data.Map.Map' 'Prelude.String' 'Prelude.String' 'TMap'
16−-------------------- -----------------
17− \"Int\" -> \"5\" 'Prelude.Int' -> 5
18− \"Bool\" -> \"True\" 'Prelude.Bool' -> 'Prelude.True'
19− \"Char\" -> \"\'x\'\" 'Prelude.Char' -> \'x\'
20−@
21−
22−The runtime representation of 'TMap' is an array, not a tree. This makes
23−'lookup' significantly more efficient.
24−
25−-}
26−
27−module Data.TMap
28− ( -- * Map type
29− TMap
30−
31− -- * Construction
32− , empty
33− , one
34−
35− -- * Modification
36− , insert
37− , delete
38− , unionWith
39− , union
40− , intersectionWith
41− , intersection
42− , map
43− , adjust
44− , alter
45−
46− -- * Query
47− , lookup
48− , member
49− , size
50− , keys
51− , keysWith
52− , toListWith
53− ) where
54−
55−import Prelude hiding (lookup, map)
56−
57−import Data.Functor.Identity (Identity (..))
58−import Data.Typeable (Typeable)
59−import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
61−
62−import qualified Data.TypeRepMap as F
63−
64−-- | 'TMap' is a special case of 'F.TypeRepMap' when the interpretation is
65−-- 'Identity'.
66−type TMap = F.TypeRepMap Identity
67−
68−{- |
69−
70−A 'TMap' with no values stored in it.
71−
72−prop> size empty == 0
73−prop> member @a empty == False
74−
75−-}
76−empty :: TMap
77−empty = F.empty
78−{-# INLINE empty #-}
79−
80−{- |
81−
82−Construct a 'TMap' with a single element.
83−
84−prop> size (one x) == 1
85−prop> member @a (one (x :: a)) == True
86−
87−-}
88−one :: forall a . Typeable a => a -> TMap
89−one x = coerce (F.one @a @Identity $ coerce x)
90−{-# INLINE one #-}
91−
92−{- |
93−
94−Insert a value into a 'TMap'.
95−TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
96−
97−prop> size (insert v tm) >= size tm
98−prop> member @a (insert (x :: a) tm) == True
99−
100−-}
101−insert :: forall a . Typeable a => a -> TMap -> TMap
102−insert x = coerce (F.insert @a @Identity $ coerce x)
103−{-# INLINE insert #-}
104−
105−{- | Delete a value from a 'TMap'.
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−
110−prop> size (delete @a tm) <= size tm
111−prop> member @a (delete @a tm) == False
112−
113−>>> tm = delete @Bool $ insert True $ one 'a'
114−>>> size tm
115−1
116−>>> member @Bool tm
117−False
118−>>> member @Char tm
119−True
120−-}
121−delete :: forall a . Typeable a => TMap -> TMap
122−delete = F.delete @a @Identity
123−{-# INLINE delete #-}
124−
125−-- | The union of two 'TMap's using a combining function.
126−unionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
127−unionWith f = F.unionWith fId
128− where
129− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
130− fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
131−{-# INLINE unionWith #-}
132−
133−-- | The (left-biased) union of two 'TMap's. It prefers the first map when
134−-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
135−union :: TMap -> TMap -> TMap
136−union = F.union
137−{-# INLINE union #-}
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−
157−{- | Lookup a value of the given type in a 'TMap'.
158−
159−>>> x = lookup $ insert (11 :: Int) empty
160−>>> x :: Maybe Int
161−Just 11
162−>>> x :: Maybe ()
163−Nothing
164−-}
165−lookup :: forall a. Typeable a => TMap -> Maybe a
166−lookup = coerce (F.lookup @a @Identity)
167−{-# INLINE lookup #-}
168−
169−{- | Check if a value of the given type is present in a 'TMap'.
170−
171−>>> member @Char $ one 'a'
172−True
173−>>> member @Bool $ one 'a'
174−False
175−-}
176−member :: forall a . Typeable a => TMap -> Bool
177−member = F.member @a @Identity
178−{-# INLINE member #-}
179−
180−-- | Get the amount of elements in a 'TMap'.
181−size :: TMap -> Int
182−size = F.size
183−{-# INLINE size #-}
184−
185−-- | Returns the list of 'SomeTypeRep's from keys.
186−keys :: TMap -> [SomeTypeRep]
187−keys = F.keys
188−{-# INLINE keys #-}
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−
200−-- | Map a function over the values.
201−map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202−map f = F.hoistWithKey (liftToIdentity f)
203−{-# INLINE map #-}
204−
205−-- | Update a value with the result of the provided function.
206−adjust :: Typeable a => (a -> a) -> TMap -> TMap
207−adjust f = F.adjust (liftToIdentity f)
208−{-# INLINE adjust #-}
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−
219−liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
220−liftToIdentity = coerce
src/Data/TypeRep/Map.hsnew file+287
@@ -0,0 +1,287 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE KindSignatures #-}
6+{-# LANGUAGE MagicHash #-}
7+{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE Rank2Types #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeInType #-}
11+
12+-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
13+
14+-- | This module introduces the 'TypeRepMap' data structure and functions to work with it.
15+
16+module Data.TypeRep.Map
17+ ( -- * Map type
18+ TypeRepMap (..)
19+
20+ -- * Construction
21+ , empty
22+ , one
23+
24+ -- * Modification
25+ , insert
26+ , delete
27+ , hoist
28+ , unionWith
29+ , union
30+
31+ -- * Query
32+ , lookup
33+ , member
34+ , size
35+
36+ -- * Helpful testing functions
37+ , TF (..)
38+ , fromList
39+ , toFingerprints
40+ ) where
41+
42+import Prelude hiding (lookup)
43+
44+import Control.Arrow ((&&&))
45+import Data.Function (on)
46+import Data.IntMap.Strict (IntMap)
47+import Data.Kind (Type)
48+import Data.List (nubBy)
49+import Data.Maybe (fromJust)
50+import Data.Primitive.Array (Array, indexArray, mapArray')
51+import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
52+import Data.Proxy (Proxy (..))
53+import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
54+import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
55+import GHC.Exts (inline, sortWith)
56+import GHC.Fingerprint (Fingerprint (..))
57+import GHC.Prim (eqWord#, ltWord#)
58+import GHC.Word (Word64 (..))
59+import Unsafe.Coerce (unsafeCoerce)
60+
61+import qualified Data.IntMap.Strict as IM
62+import qualified Data.Map.Strict as Map
63+import qualified GHC.Exts as GHC (fromList, toList)
64+
65+-- | Map-like data structure that keeps types as keys.
66+data TypeRepMap (f :: k -> Type) = TypeRepMap
67+ { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64)
68+ , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64)
69+ , anys :: {-# UNPACK #-} !(Array Any)
70+ }
71+
72+-- | Shows only 'Fingerprint's.
73+instance Show (TypeRepMap f) where
74+ show = show . toFingerprints
75+
76+-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
77+toFingerprints :: TypeRepMap f -> [Fingerprint]
78+toFingerprints TypeRepMap{..} =
79+ zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
80+
81+
82+-- | Empty structure.
83+empty :: TypeRepMap f
84+empty = TypeRepMap mempty mempty mempty
85+
86+-- | Construct a 'TypeRepMap' with a single element.
87+one :: forall a f . Typeable a => f a -> TypeRepMap f
88+one x = insert x empty
89+{-# INLINE one #-}
90+
91+-- | Inserts the value with its type as a key.
92+insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
93+insert x = fromListPairs . addX . toPairList
94+ where
95+ pairX :: (Fingerprint, Any)
96+ pairX@(fpX, _) = (calcFp x, toAny x)
97+
98+ addX :: [(Fingerprint, Any)] -> [(Fingerprint, Any)]
99+ addX l = pairX : deleteByFst fpX l
100+{-# INLINE insert #-}
101+
102+-- Helper type to workaround some issues with types
103+type KindOf (a :: k) = k
104+
105+{- | Deletes value from list.
106+
107+>>> let trMap = delete @Bool $ insert (Identity True) $ one (Identity 'a')
108+>>> size trMap
109+1
110+>>> member @Bool trMap
111+False
112+>>> member @Char trMap
113+True
114+-}
115+delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
116+delete = fromListPairs . deleteByFst (typeFp @a) . toPairList
117+{-# INLINE delete #-}
118+
119+{- | Map over elements.
120+
121+>>> let trMap = insert (Identity True) $ one (Identity 'a')
122+>>> lookup @Bool trMap
123+Just (Identity True)
124+>>> lookup @Char trMap
125+Just (Identity 'a')
126+>>> let trMap2 = hoist ((:[]) . runIdentity) trMap
127+>>> lookup @Bool trMap2
128+Just [True]
129+>>> lookup @Char trMap2
130+Just "a"
131+-}
132+hoist :: (forall x. f x -> g x) -> TypeRepMap f -> TypeRepMap g
133+hoist f (TypeRepMap as bs ans) = TypeRepMap as bs $ mapArray' (toAny . f . fromAny) ans
134+{-# INLINE hoist #-}
135+
136+-- | The union with a combining function.
137+unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
138+unionWith f m1 m2 = fromListPairs
139+ $ Map.toList
140+ $ Map.unionWith combine
141+ (Map.fromList $ toPairList m1)
142+ (Map.fromList $ toPairList m2)
143+ where
144+ combine :: Any -> Any -> Any
145+ combine a b = toAny $ f (fromAny a) (fromAny b)
146+{-# INLINE unionWith #-}
147+
148+-- | The (left-biased) union of two maps. It prefers the first map when
149+-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
150+union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
151+union = unionWith const
152+{-# INLINE union #-}
153+
154+{- | Returns 'True' if there exist value of given type.
155+
156+>>> member @Char $ one (Identity 'a')
157+True
158+>>> member @Bool $ one (Identity 'a')
159+False
160+-}
161+member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
162+member trMap = case lookup @a trMap of
163+ Nothing -> False
164+ Just _ -> True
165+{-# INLINE member #-}
166+
167+{- | Looks up the value at the type.
168+
169+>>> let x = lookup $ insert (Identity (11 :: Int)) empty
170+>>> x :: Maybe (Identity Int)
171+Just (Identity 11)
172+>>> x :: Maybe (Identity ())
173+Nothing
174+-}
175+lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
176+lookup tVect = fromAny . (anys tVect `indexArray`)
177+ <$> cachedBinarySearch (typeFp @a)
178+ (fingerprintAs tVect)
179+ (fingerprintBs tVect)
180+{-# INLINE lookup #-}
181+
182+-- | Returns the size of the 'TypeRepMap'.
183+size :: TypeRepMap f -> Int
184+size = sizeofPrimArray . fingerprintAs
185+{-# INLINE size #-}
186+
187+-- | Binary searched based on this article
188+-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
189+-- with modification for our two-vector search case.
190+cachedBinarySearch :: Fingerprint -> PrimArray Word64 -> PrimArray Word64 -> Maybe Int
191+cachedBinarySearch (Fingerprint (W64# a) (W64# b)) fpAs fpBs = inline (go 0#)
192+ where
193+ go :: Int# -> Maybe Int
194+ go i = case i <# len of
195+ 0# -> Nothing
196+ _ -> let !(W64# valA) = indexPrimArray fpAs (I# i) in case a `ltWord#` valA of
197+ 0# -> case a `eqWord#` valA of
198+ 0# -> go (2# *# i +# 2#)
199+ _ -> let !(W64# valB) = indexPrimArray fpBs (I# i) in case b `eqWord#` valB of
200+ 0# -> case b `ltWord#` valB of
201+ 0# -> go (2# *# i +# 2#)
202+ _ -> go (2# *# i +# 1#)
203+ _ -> Just (I# i)
204+ _ -> go (2# *# i +# 1#)
205+
206+ len :: Int#
207+ len = let !(I# l) = sizeofPrimArray fpAs in l
208+{-# INLINE cachedBinarySearch #-}
209+
210+----------------------------------------------------------------------------
211+-- Internal functions
212+----------------------------------------------------------------------------
213+
214+toAny :: f a -> Any
215+toAny = unsafeCoerce
216+
217+fromAny :: Any -> f a
218+fromAny = unsafeCoerce
219+
220+typeFp :: forall a . Typeable a => Fingerprint
221+typeFp = typeRepFingerprint $ typeRep $ Proxy @a
222+{-# INLINE typeFp #-}
223+
224+toPairList :: TypeRepMap f -> [(Fingerprint, Any)]
225+toPairList trMap = zip (toFingerprints trMap) (GHC.toList $ anys trMap)
226+
227+deleteByFst :: Eq a => a -> [(a, b)] -> [(a, b)]
228+deleteByFst x = filter ((/= x) . fst)
229+
230+nubByFst :: (Eq a) => [(a, b)] -> [(a, b)]
231+nubByFst = nubBy ((==) `on` fst)
232+
233+----------------------------------------------------------------------------
234+-- Functions for testing and benchmarking
235+----------------------------------------------------------------------------
236+
237+-- | Existential wrapper around 'Typeable' indexed by @f@ type parameter.
238+-- Useful for 'TypeRepMap' structure creation form list of 'TF's.
239+data TF f where
240+ TF :: Typeable a => f a -> TF f
241+
242+instance Show (TF f) where
243+ show (TF tf) = show $ calcFp tf
244+
245+{- | Creates 'TypeRepMap' from a list of 'TF's.
246+
247+>>> size $ fromList [TF $ Identity True, TF $ Identity 'a']
248+2
249+
250+-}
251+fromList :: forall f . [TF f] -> TypeRepMap f
252+fromList = fromListPairs . map (fp &&& an)
253+ where
254+ fp :: TF f -> Fingerprint
255+ fp (TF x) = calcFp x
256+
257+ an :: TF f -> Any
258+ an (TF x) = toAny x
259+
260+fromF :: Typeable a => f a -> Proxy a
261+fromF _ = Proxy
262+
263+calcFp :: Typeable a => f a -> Fingerprint
264+calcFp = typeRepFingerprint . typeRep . fromF
265+
266+fromListPairs :: [(Fingerprint, Any)] -> TypeRepMap f
267+fromListPairs kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans)
268+ where
269+ (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
270+ (fps, ans) = unzip $ fromSortedList $ sortWith fst $ nubByFst kvs
271+
272+----------------------------------------------------------------------------
273+-- Tree-like conversion
274+----------------------------------------------------------------------------
275+
276+fromSortedList :: forall a . [a] -> [a]
277+fromSortedList l = IM.elems $ fst $ go 0 0 mempty (IM.fromList $ zip [0..] l)
278+ where
279+ -- state monad could be used here, but it's another dependency
280+ go :: Int -> Int -> IntMap a -> IntMap a -> (IntMap a, Int)
281+ go i first result vector =
282+ if i >= IM.size vector
283+ then (result, first)
284+ else do
285+ let (newResult, newFirst) = go (2 * i + 1) first result vector
286+ let withCur = IM.insert i (fromJust $ IM.lookup newFirst vector) newResult
287+ go (2 * i + 2) (newFirst + 1) withCur vector
src/Data/TypeRep/TMap.hsnew file+89
@@ -0,0 +1,89 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE Rank2Types #-}
3+
4+-- | This module introduces 'TMap' data type — 'TR.TypeRepMap' parametrized by 'Identity'.
5+
6+module Data.TypeRep.TMap
7+ ( -- * Map type
8+ TMap
9+
10+ -- * Construction
11+ , empty
12+ , one
13+
14+ -- * Modification
15+ , insert
16+ , delete
17+ , hoist
18+ , unionWith
19+ , union
20+
21+ -- * Query
22+ , lookup
23+ , member
24+ , size
25+ ) where
26+
27+import Prelude hiding (lookup)
28+
29+import Data.Functor.Identity (Identity (..))
30+import Data.Typeable (Typeable)
31+import GHC.Exts (coerce)
32+
33+import qualified Data.TypeRep.Map as TR
34+
35+-- | 'TR.TypeRepMap' parametrized by 'Identity'.
36+type TMap = TR.TypeRepMap Identity
37+
38+-- | Similar to 'TR.empty' but for 'TMap'.
39+empty :: TMap
40+empty = TR.empty
41+{-# INLINE empty #-}
42+
43+-- | Similar to 'TR.one' but for 'TMap'.
44+one :: forall a . Typeable a => a -> TMap
45+one x = coerce (TR.one @a @Identity $ coerce x)
46+{-# INLINE one #-}
47+
48+-- | Similar to 'TR.insert' but for 'TMap'.
49+insert :: forall a . Typeable a => a -> TMap -> TMap
50+insert x = coerce (TR.insert @a @Identity $ coerce x)
51+{-# INLINE insert #-}
52+
53+-- | Similar to 'TR.delete' but for 'TMap'.
54+delete :: forall a . Typeable a => TMap -> TMap
55+delete = TR.delete @a @Identity
56+{-# INLINE delete #-}
57+
58+-- | Similar to 'TR.hoist' but for 'TMap'.
59+hoist :: (forall x. x -> g x) -> TMap -> TR.TypeRepMap g
60+hoist f = TR.hoist (f . coerce)
61+{-# INLINE hoist #-}
62+
63+-- | Similar to 'TR.unionWith' but for 'TMap'.
64+unionWith :: (forall x. x -> x -> x) -> TMap -> TMap -> TMap
65+unionWith f = TR.unionWith fId
66+ where
67+ fId :: forall y . Identity y -> Identity y -> Identity y
68+ fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
69+{-# INLINE unionWith #-}
70+
71+-- | Similar to 'TR.union' but for 'TMap'.
72+union :: TMap -> TMap -> TMap
73+union = TR.union
74+{-# INLINE union #-}
75+
76+-- | Similar to 'TR.lookup' but for 'TMap'.
77+lookup :: forall a. Typeable a => TMap -> Maybe a
78+lookup = coerce (TR.lookup @a @Identity)
79+{-# INLINE lookup #-}
80+
81+-- | Similar to 'TR.member' but for 'TMap'.
82+member :: forall a . Typeable a => TMap -> Bool
83+member = TR.member @a @Identity
84+{-# INLINE member #-}
85+
86+-- | Similar to 'TR.size' but for 'TMap'.
87+size :: TMap -> Int
88+size = TR.size
89+{-# INLINE size #-}
src/Data/TypeRepMap.hsdeleted−81
@@ -1,81 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−{- |
4−Copyright: (c) 2017-2020 Kowainik
5−SPDX-License-Identifier: MPL-2.0
6−Maintainer: Kowainik <xrom.xkov@gmail.com>
7−
8−A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
9−sort of parametrization may be familiar to users of @vinyl@ records.
10−
11−@'TypeRepMap' f@ is a more efficient replacement for @DMap
12−'Type.Reflection.TypeRep' f@ (where @DMap@ is from the @dependent-map@
13−package).
14−
15−Here is an example of using 'Prelude.Maybe' as an interpretation, with a
16−comparison to 'Data.TMap.TMap':
17−
18−@
19− 'Data.TMap.TMap' 'TypeRepMap' 'Prelude.Maybe'
20−-------------- -------------------
21− Int -> 5 Int -> Just 5
22− Bool -> True Bool -> Nothing
23− Char -> \'x\' Char -> Just \'x\'
24−@
25−
26−In fact, a 'Data.TMap.TMap' is defined as 'TypeRepMap'
27−'Data.Functor.Identity'.
28−
29−Since 'Type.Reflection.TypeRep' is poly-kinded, the interpretation can use
30−any kind for the keys. For instance, we can use the 'GHC.TypeLits.Symbol'
31−kind to use 'TypeRepMap' as an extensible record:
32−
33−@
34−newtype Field name = F (FType name)
35−
36−type family FType (name :: Symbol) :: Type
37−type instance FType "radius" = Double
38−type instance FType "border-color" = RGB
39−type instance FType "border-width" = Double
40−
41− 'TypeRepMap' Field
42−--------------------------------------
43− "radius" -> F 5.7
44− "border-color" -> F (rgb 148 0 211)
45− "border-width" -> F 0.5
46−@
47−--}
48−module Data.TypeRepMap
49− ( -- * Map type
50− TypeRepMap()
51−
52− -- * Construction
53− , empty
54− , one
55−
56− -- * Modification
57− , insert
58− , delete
59− , adjust
60− , alter
61− , hoist
62− , hoistA
63− , hoistWithKey
64− , unionWith
65− , union
66− , intersectionWith
67− , intersection
68−
69− -- * Query
70− , lookup
71− , member
72− , size
73− , keys
74− , keysWith
75− , toListWith
76−
77− -- * 'IsList'
78− , WrapTypeable (..)
79− ) where
80−
81−import Data.TypeRepMap.Internal
src/Data/TypeRepMap/Internal.hsdeleted−670
@@ -1,670 +0,0 @@
1−{-# OPTIONS_GHC -Wno-missing-export-lists #-}
2−
3−{-# LANGUAGE AllowAmbiguousTypes #-}
4−{-# LANGUAGE BangPatterns #-}
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE DataKinds #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE MagicHash #-}
10−{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE Rank2Types #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeInType #-}
14−{-# LANGUAGE ViewPatterns #-}
15−
16−#if __GLASGOW_HASKELL__ >= 806
17−{-# LANGUAGE QuantifiedConstraints #-}
18−#endif
19−
20−-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
21−
22−{- |
23−Copyright: (c) 2017-2020 Kowainik
24−SPDX-License-Identifier: MPL-2.0
25−Maintainer: Kowainik <xrom.xkov@gmail.com>
26−
27−Internal API for 'TypeRepMap' and operations on it. The functions here do
28−not have any stability guarantees and can change between minor versions.
29−
30−If you need to use this module for purposes other than tests,
31−create an issue.
32−-}
33−
34−#include "MachDeps.h"
35−
36−module Data.TypeRepMap.Internal where
37−
38−import Prelude hiding (lookup)
39−
40−import Control.DeepSeq
41−import Control.Monad.ST (ST, runST)
42−import Control.Monad.Zip (mzip)
43−import Data.Function (on)
44−import Data.Kind (Type)
45−import Data.Type.Equality ((:~:) (..), TestEquality (..))
46−import Data.List (intercalate, nubBy)
47−import Data.Maybe (fromMaybe)
48−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
49− thawArray, unsafeFreezeArray, writeArray)
50−import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
51−import Data.Semigroup (Semigroup (..), All(..))
52−import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
53−import GHC.Exts (IsList (..), inline, sortWith)
54−import GHC.Fingerprint (Fingerprint (..))
55−#if WORD_SIZE_IN_BITS >= 64
56−import GHC.Prim (eqWord#, ltWord#)
57−#else
58−import GHC.IntWord64 (eqWord64#, ltWord64#)
59−#define eqWord eqWord64
60−#define ltWord ltWord64
61−#endif
62−import GHC.Word (Word64 (..))
63−import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeable)
64−import Type.Reflection.Unsafe (typeRepFingerprint)
65−import Unsafe.Coerce (unsafeCoerce)
66−
67−import qualified GHC.Exts as GHC (fromList, toList)
68−
69−{- |
70−
71−'TypeRepMap' is a heterogeneous data structure similar in its essence to
72−'Data.Map.Map' with types as keys, where each value has the type of its key. In
73−addition to that, each value is wrapped in an interpretation @f@.
74−
75−Here is an example of using 'Prelude.Maybe' as an interpretation, with a
76−comparison to 'Data.Map.Map':
77−
78−@
79− 'Data.Map.Map' 'Prelude.String' ('Prelude.Maybe' 'Prelude.String') 'TypeRepMap' 'Prelude.Maybe'
80−--------------------------- ---------------------
81− \"Int\" -> Just \"5\" 'Prelude.Int' -> Just 5
82− \"Bool\" -> Just \"True\" 'Prelude.Bool' -> Just 'Prelude.True'
83− \"Char\" -> Nothing 'Prelude.Char' -> Nothing
84−@
85−
86−The runtime representation of 'TypeRepMap' is an array, not a tree. This makes
87−'lookup' significantly more efficient.
88−
89−-}
90−data TypeRepMap (f :: k -> Type) =
91− TypeRepMap
92− { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ first components of key fingerprints
93− , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ second components of key fingerprints
94− , trAnys :: {-# UNPACK #-} !(Array Any) -- ^ values stored in the map
95− , trKeys :: {-# UNPACK #-} !(Array Any) -- ^ typerep keys
96− }
97− -- ^ an unsafe constructor for 'TypeRepMap'
98−
99−instance NFData (TypeRepMap f) where
100− rnf x = rnf (keys x) `seq` ()
101−
102−-- | Shows only keys.
103−instance Show (TypeRepMap f) where
104− show TypeRepMap{..} = "TypeRepMap [" ++ showKeys ++ "]"
105− where
106− showKeys :: String
107− showKeys = intercalate ", " $ toList $ mapArray' (show . anyToTypeRep) trKeys
108−
109−-- | Uses 'union' to combine 'TypeRepMap's.
110−instance Semigroup (TypeRepMap f) where
111− (<>) :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
112− (<>) = union
113− {-# INLINE (<>) #-}
114−
115−instance Monoid (TypeRepMap f) where
116− mempty = TypeRepMap mempty mempty mempty mempty
117− mappend = (<>)
118− {-# INLINE mempty #-}
119− {-# INLINE mappend #-}
120−
121−#if __GLASGOW_HASKELL__ >= 806
122−instance (forall a. Typeable a => Eq (f a)) => Eq (TypeRepMap f) where
123− tm1 == tm2 = size tm1 == size tm2 && go 0
124− where
125− go :: Int -> Bool
126− go i
127− | i == size tm1 = True
128− | otherwise = case testEquality tr1i tr2i of
129− Nothing -> False
130− Just Refl -> repEq tr1i (fromAny tv1i) (fromAny tv2i) && go (i + 1)
131− where
132− tr1i :: TypeRep x
133− tr1i = anyToTypeRep $ indexArray (trKeys tm1) i
134−
135− tr2i :: TypeRep y
136− tr2i = anyToTypeRep $ indexArray (trKeys tm2) i
137−
138− tv1i, tv2i :: Any
139− tv1i = indexArray (trAnys tm1) i
140− tv2i = indexArray (trAnys tm2) i
141−
142− repEq :: TypeRep x -> f x -> f x -> Bool
143− repEq tr = withTypeable tr (==)
144−#endif
145−
146−-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
147−toFingerprints :: TypeRepMap f -> [Fingerprint]
148−toFingerprints TypeRepMap{..} =
149− zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
150−
151−{- |
152−
153−A 'TypeRepMap' with no values stored in it.
154−
155−prop> size empty == 0
156−prop> member @a empty == False
157−
158−-}
159−empty :: TypeRepMap f
160−empty = mempty
161−{-# INLINE empty #-}
162−
163−{- |
164−
165−Construct a 'TypeRepMap' with a single element.
166−
167−prop> size (one x) == 1
168−prop> member @a (one (x :: f a)) == True
169−
170−-}
171−one :: 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)
178−{-# INLINE one #-}
179−
180−{- |
181−
182−Insert a value into a 'TypeRepMap'.
183−TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
184−
185−prop> size (insert v tm) >= size tm
186−prop> member @a (insert (x :: f a) tm) == True
187−
188−-}
189−insert :: 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)}
195− 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
202−{-# INLINE insert #-}
203−
204−-- Extract the kind of a type. We use it to work around lack of syntax for
205−-- inferred type variables (which are not subject to type applications).
206−type KindOf (a :: k) = k
207−
208−type ArgKindOf (f :: k -> l) = k
209−
210−{- | Delete a value from a 'TypeRepMap'.
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−
215−prop> size (delete @a tm) <= size tm
216−prop> member @a (delete @a tm) == False
217−
218−>>> tm = delete @Bool $ insert (Just True) $ one (Just 'a')
219−>>> size tm
220−1
221−>>> member @Bool tm
222−False
223−>>> member @Char tm
224−True
225−-}
226−delete :: 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
234−{-# INLINE delete #-}
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−
240−{- |
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.
243−
244−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
245−>>> lookup @String $ adjust (fmap (++ "ww")) trmap
246−Just (Identity "aww")
247−-}
248−adjust :: 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)}
273− where
274− replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275− replaceAnyAt i v trAs = runST $ do
276− let n = sizeofArray trAs
277− mutArr <- thawArray trAs 0 n
278− writeArray mutArr i v
279− unsafeFreezeArray mutArr
280−{-# INLINE alter #-}
281−
282−{- | Map over the elements of a 'TypeRepMap'.
283−
284−>>> tm = insert (Identity True) $ one (Identity 'a')
285−>>> lookup @Bool tm
286−Just (Identity True)
287−>>> lookup @Char tm
288−Just (Identity 'a')
289−>>> tm2 = hoist ((:[]) . runIdentity) tm
290−>>> lookup @Bool tm2
291−Just [True]
292−>>> lookup @Char tm2
293−Just "a"
294−-}
295−hoist :: (forall x. f x -> g x) -> TypeRepMap f -> TypeRepMap g
296−hoist f (TypeRepMap as bs ans ks) = TypeRepMap as bs (mapArray' (toAny . f . fromAny) ans) ks
297−{-# INLINE hoist #-}
298−
299−hoistA :: (Applicative t) => (forall x. f x -> t (g x)) -> TypeRepMap f -> t (TypeRepMap g)
300−hoistA f (TypeRepMap as bs (toList -> ans) ks) = (\l -> TypeRepMap as bs (fromList $ map toAny l) ks)
301− <$> traverse (f . fromAny) ans
302−{-# INLINE hoistA #-}
303−
304−hoistWithKey :: forall f g. (forall x. Typeable x => f x -> g x) -> TypeRepMap f -> TypeRepMap g
305−hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
306− where
307− newAns = mapArray' mapAns (mzip ans ks)
308− mapAns (a, k) = toAny $ withTr (unsafeCoerce k) $ fromAny a
309−
310− withTr :: forall x. TypeRep x -> f x -> g x
311− withTr t = withTypeable t f
312−{-# INLINE hoistWithKey #-}
313−
314−-- | The union of two 'TypeRepMap's using a combining function for conflicting entries. @O(n + m)@
315−unionWith :: 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)
318− where
319− f' :: forall x. TypeRep x -> f x -> f x -> f x
320− f' tr = withTypeable tr f
321−
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
347−{-# INLINE unionWith #-}
348−
349−-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
350−-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
351−union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352−union = unionWith const
353−{-# INLINE union #-}
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−
397−{- | Check if a value of the given type is present in a 'TypeRepMap'.
398−
399−>>> member @Char $ one (Identity 'a')
400−True
401−>>> member @Bool $ one (Identity 'a')
402−False
403−-}
404−member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
405−member tm = case lookup @a tm of
406− Nothing -> False
407− Just _ -> True
408−{-# INLINE member #-}
409−
410−{- | Lookup a value of the given type in a 'TypeRepMap'.
411−
412−>>> x = lookup $ insert (Identity (11 :: Int)) empty
413−>>> x :: Maybe (Identity Int)
414−Just (Identity 11)
415−>>> x :: Maybe (Identity ())
416−Nothing
417−-}
418−lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
419−lookup tVect = fromAny . (trAnys tVect `indexArray`)
420− <$> cachedBinarySearch (typeFp @a)
421− (fingerprintAs tVect)
422− (fingerprintBs tVect)
423−{-# INLINE lookup #-}
424−
425−-- | Get the amount of elements in a 'TypeRepMap'.
426−size :: TypeRepMap f -> Int
427−size = sizeofPrimArray . fingerprintAs
428−{-# INLINE size #-}
429−
430−-- | Return the list of 'SomeTypeRep' from the keys.
431−keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
433−{-# INLINE keys #-}
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−
448−-- | Binary searched based on this article
449−-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450−-- with modification for our two-vector search case.
451−cachedBinarySearch :: Fingerprint -> PrimArray Word64 -> PrimArray Word64 -> Maybe Int
452−cachedBinarySearch (Fingerprint (W64# a) (W64# b)) fpAs fpBs = inline (go 0#)
453− where
454− go :: Int# -> Maybe Int
455− go i = case i <# len of
456− 0# -> Nothing
457− _ -> let !(W64# valA) = indexPrimArray fpAs (I# i) in case a `ltWord#` valA of
458− 0# -> case a `eqWord#` valA of
459− 0# -> go (2# *# i +# 2#)
460− _ -> let !(W64# valB) = indexPrimArray fpBs (I# i) in case b `eqWord#` valB of
461− 0# -> case b `ltWord#` valB of
462− 0# -> go (2# *# i +# 2#)
463− _ -> go (2# *# i +# 1#)
464− _ -> Just (I# i)
465− _ -> go (2# *# i +# 1#)
466−
467− len :: Int#
468− len = let !(I# l) = sizeofPrimArray fpAs in l
469−{-# INLINE cachedBinarySearch #-}
470−
471−----------------------------------------------------------------------------
472−-- Internal functions
473−----------------------------------------------------------------------------
474−
475−toAny :: f a -> Any
476−toAny = unsafeCoerce
477−
478−fromAny :: Any -> f a
479−fromAny = unsafeCoerce
480−
481−anyToTypeRep :: Any -> TypeRep f
482−anyToTypeRep = unsafeCoerce
483−
484−typeFp :: forall a . Typeable a => Fingerprint
485−typeFp = typeRepFingerprint $ typeRep @a
486−{-# INLINE typeFp #-}
487−
488−toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
489−toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
490−
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)
505−
506−nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
507−nubByFst = nubBy ((==) `on` fst3)
508−
509−fst3 :: (a, b, c) -> a
510−fst3 (a, _, _) = a
511−
512−----------------------------------------------------------------------------
513−-- Functions for testing and benchmarking
514−----------------------------------------------------------------------------
515−
516−-- | Existential wrapper around 'Typeable' indexed by @f@ type parameter.
517−-- Useful for 'TypeRepMap' structure creation form list of 'WrapTypeable's.
518−data WrapTypeable f where
519− WrapTypeable :: Typeable a => f a -> WrapTypeable f
520−
521−instance Show (WrapTypeable f) where
522− show (WrapTypeable (_ :: f a)) = show $ calcFp @a
523−
524−{- |
525−
526−prop> fromList . toList == 'id'
527−
528−Creates 'TypeRepMap' from a list of 'WrapTypeable's.
529−
530−>>> show $ fromList [WrapTypeable $ Identity True, WrapTypeable $ Identity 'a']
531−TypeRepMap [Bool, Char]
532−
533−
534−-}
535−instance IsList (TypeRepMap f) where
536− type Item (TypeRepMap f) = WrapTypeable f
537−
538− fromList :: [WrapTypeable f] -> TypeRepMap f
539− fromList = fromTriples . map (\x -> (fp x, an x, k x))
540− where
541− fp :: WrapTypeable f -> Fingerprint
542− fp (WrapTypeable (_ :: f a)) = calcFp @a
543−
544− an :: WrapTypeable f -> Any
545− an (WrapTypeable x) = toAny x
546−
547− k :: WrapTypeable f -> Any
548− k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549−
550− toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
552−
553−calcFp :: forall a . Typeable a => Fingerprint
554−calcFp = typeRepFingerprint $ typeRep @a
555−
556−fromTriples :: [(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)
561− where
562− (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
563− (fps, ans, ks) = unzip3 $ fromSortedList kvs
564−
565−----------------------------------------------------------------------------
566−-- Tree-like conversion
567−----------------------------------------------------------------------------
568−
569−fromSortedList :: forall a . [a] -> [a]
570−fromSortedList l = runST $ do
571− let n = length l
572− let arrOrigin = fromListN n l
573− arrResult <- thawArray arrOrigin 0 n
574− go n arrResult arrOrigin
575− toList <$> unsafeFreezeArray arrResult
576− where
577− -- state monad could be used here, but it's another dependency
578− go :: forall s . Int -> MutableArray s a -> Array a -> ST s ()
579− go len result origin = () <$ loop 0 0
580− where
581− loop :: Int -> Int -> ST s Int
582− loop i first =
583− if i >= len
584− then pure first
585− else do
586− newFirst <- loop (2 * i + 1) first
587− writeArray result i (indexArray origin newFirst)
588− loop (2 * i + 2) (newFirst + 1)
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−
614−----------------------------------------------------------------------------
615−-- Helper functions.
616−----------------------------------------------------------------------------
617−
618−-- | Check that invariant of the structure holds.
619−-- The structure maintains the following invariant.
620−-- For each element @A@ at index @i@:
621−--
622−-- 1. if there is an element @B@ at index @2*i+1@,
623−-- then @B < A@.
624−--
625−-- 2. if there is an element @C@ at index @2*i+2@,
626−-- then @A < C@.
627−--
628−invariantCheck :: TypeRepMap f -> Bool
629−invariantCheck TypeRepMap{..} = getAll (check 0)
630− where
631− lastMay [] = Nothing
632− lastMay [x] = Just x
633− lastMay (_:xs) = lastMay xs
634− sz = sizeofPrimArray fingerprintAs
635− check i | i >= sz = All True
636− | otherwise =
637− let left = i*2+1
638− right = i*2+2
639− -- maximum value in the left branch
640− leftMax =
641− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
642− $ lastMay
643− $ takeWhile (<sz)
644− $ iterate (\j -> j*2+2) left
645− -- minimum value in the right branch
646− rightMin =
647− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
648− $ lastMay
649− $ takeWhile (<sz)
650− $ iterate (\j -> j*2+1) right
651− in mconcat
652− [ All $
653− if left < sz
654− then
655− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs left of
656− LT -> False
657− EQ -> indexPrimArray fingerprintBs i >= indexPrimArray fingerprintBs left
658− GT -> True
659− else True
660− , All $
661− if right < sz
662− then
663− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs right of
664− LT -> True
665− EQ -> indexPrimArray fingerprintBs i <= indexPrimArray fingerprintBs right
666− GT -> False
667− else True
668− , All $ fromMaybe True $ (<=) <$> leftMax <*> rightMin
669− , check (i+1)
670− ]
stack.yaml+2−4
@@ -1,6 +1,4 @@
1−resolver: lts-15.8
1+resolver: nightly-2018-07-05
22
33extra-deps:
4− - dependent-map-0.2.4.0
5− - dependent-sum-0.5
6− - hspec-hedgehog-0.0.1.2
4+ - primitive-0.6.4.0
test/Test.hs+1−21
@@ -1,21 +1 @@
1−module Main
2− ( main
3− ) where
4−
5−import Test.Hspec (hspec)
6−
7−import Test.TypeRep.CMap (cMapSpec)
8−import Test.TypeRep.TypeRepMap (typeRepMapSpec)
9−import Test.TypeRep.TypeRepMapProperty (typeRepMapPropertySpec)
10−import Test.TypeRep.Vector (vectorSpec)
11−import Test.TypeRep.VectorOpt (optimalVectorSpec)
12−
13−
14−main :: IO ()
15−main = hspec $ do
16− typeRepMapSpec
17− cMapSpec
18− vectorSpec
19− optimalVectorSpec
20− -- property
21− typeRepMapPropertySpec
1+{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display #-}
test/Test/TypeRep/CMap.hs+7−9
@@ -1,18 +1,16 @@
1−module Test.TypeRep.CMap
2− ( cMapSpec
3− ) where
1+module Test.TypeRep.CMap where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.CMap (TypeRepMap, empty, insert, lookup, size)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.CMap
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−cMapSpec :: Spec
15−cMapSpec = describe "Containers Map TypeRep" $ do
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup = do
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (insert (Identity 'a') empty) `shouldBe` Just (Identity 'a')
@@ -37,7 +35,7 @@ mapOf10 = insert (Identity True)
3735 $ insert (Identity $ Just ())
3836 $ insert (Identity [()])
3937 $ insert (Identity ())
40− $ insert (Identity @String "aaa")
38+ $ insert (Identity "aaa")
4139 $ insert (Identity $ Just 'a')
4240 $ insert (Identity 'a')
4341 $ insert (Identity (11 :: Int)) empty
test/Test/TypeRep/CacheMap.hsnew file+48
@@ -0,0 +1,48 @@
1+module Test.TypeRep.CacheMap where
2+
3+import Prelude hiding (lookup)
4+
5+import Data.Functor.Identity (Identity (..))
6+import Test.Tasty.Hspec (Spec, describe, it, shouldBe)
7+
8+import Data.TypeRep.Map (TF (..), fromList)
9+import Data.TypeRep.TMap (TMap, empty, insert, lookup, one, size, union)
10+
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup = do
14+ describe "Lookup Test" $ do
15+ it "returns the inserted element" $
16+ lookup (fromList [TF $ Identity 'a']) `shouldBe` Just 'a'
17+ it "returns the second inserted value of the same type" $
18+ lookup (fromList [TF (Identity 'b'), TF (Identity 'a')]) `shouldBe` Just 'b'
19+
20+ describe "Size Test" $ do
21+ it "is empty" $
22+ size empty `shouldBe` 0
23+ it "is of size 1 when 1 element inserted" $
24+ size (one 'a') `shouldBe` 1
25+ it "doesn't increase size when element of the same type is added" $
26+ size (insert 'b' $ insert 'a' empty) `shouldBe` 1
27+ it "returns 10 when 10 different types are inserted" $
28+ size mapOf10 `shouldBe` 10
29+
30+ describe "Union test" $ do
31+ let m = fromList [TF $ Identity 'a', TF $ Identity True] `union` fromList [TF $ Identity 'b']
32+ it "lookup works on union as expected" $ do
33+ lookup m `shouldBe` Just 'a'
34+ lookup m `shouldBe` Just True
35+ lookup @Int m `shouldBe` Nothing
36+
37+
38+mapOf10 :: TMap
39+mapOf10 = insert True
40+ $ insert [True, False]
41+ $ insert (Just True)
42+ $ insert (Just ())
43+ $ insert [()]
44+ $ insert ()
45+ $ insert "aaa"
46+ $ insert (Just 'a')
47+ $ insert 'a'
48+ $ insert (11 :: Int) empty
test/Test/TypeRep/MapProperty.hsnew file+69
@@ -0,0 +1,69 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE KindSignatures #-}
3+{-# LANGUAGE PolyKinds #-}
4+
5+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
6+
7+module Test.TypeRep.MapProperty where
8+
9+import Prelude hiding (lookup)
10+
11+import Data.Proxy (Proxy (..))
12+import GHC.Stack (HasCallStack)
13+import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
14+import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
15+import Test.Tasty (TestName, TestTree)
16+import Test.Tasty.Hedgehog (testProperty)
17+
18+import Data.TypeRep.Map (TF (..), TypeRepMap, delete, fromList, insert, lookup, member)
19+
20+import qualified Hedgehog.Gen as Gen
21+import qualified Hedgehog.Range as Range
22+
23+type PropertyTest = [TestTree]
24+
25+prop :: HasCallStack => TestName -> PropertyT IO () -> PropertyTest
26+prop testName = pure . testProperty testName . property
27+
28+test_InsertLookup :: PropertyTest
29+test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
30+ m <- forAll genMap
31+ TF (proxy :: IntProxy n) <- forAll genTF
32+
33+ lookup @n @IntProxy (insert proxy m) === Just proxy
34+
35+test_InsertInsert :: PropertyTest
36+test_InsertInsert = prop "insert k b . insert k a == insert k b" $ do
37+ m <- forAll genMap
38+ TF a@(IntProxy (proxy :: Proxy n) i) <- forAll genTF
39+ let b = IntProxy proxy (i + 1)
40+ lookup @n @IntProxy (insert b $ insert a m) === Just b
41+
42+test_DeleteMember :: PropertyTest
43+test_DeleteMember = prop "member k . delete k == False" $ do
44+ m <- forAll genMap
45+ TF (proxy :: IntProxy n) <- forAll genTF
46+ shouldInsert <- forAll Gen.bool
47+
48+ if shouldInsert then
49+ member @n (delete @n $ insert proxy m) === False
50+ else
51+ member @n (delete @n m) === False
52+
53+----------------------------------------------------------------------------
54+-- Generators
55+----------------------------------------------------------------------------
56+
57+data IntProxy (n :: Nat) = IntProxy (Proxy n) Int
58+ deriving (Show, Eq)
59+
60+genMap :: MonadGen m => m (TypeRepMap IntProxy)
61+genMap = fromList <$> Gen.list (Range.linear 0 1000) genTF
62+
63+genTF :: MonadGen m => m (TF IntProxy)
64+genTF = do
65+ randNat :: Integer <- Gen.integral (Range.linear 0 10000)
66+ randInt <- Gen.int Range.constantBounded
67+ case someNatVal randNat of
68+ Just (SomeNat proxyNat) -> pure $ TF $ IntProxy proxyNat randInt
69+ Nothing -> error "Invalid test generator"
test/Test/TypeRep/TypeRepMap.hsdeleted−111
@@ -1,111 +0,0 @@
1−module Test.TypeRep.TypeRepMap
2− ( typeRepMapSpec
3− ) where
4−
5−import Prelude hiding (lookup)
6−
7−import 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)
14−
15−import Data.TMap (TMap, empty, insert, lookup, one, size, union, keys)
16−import Data.TypeRepMap.Internal (WrapTypeable (..))
17−
18−
19−-- Simple test for 'lookup', 'insert', 'size', 'keys', 'toList' functions.
20−typeRepMapSpec :: Spec
21−typeRepMapSpec = describe "TypeRepMap" $ do
22− describe "Lookup Test" $ do
23− it "returns the inserted element" $
24− lookup (fromList [WrapTypeable $ Identity 'a']) `shouldBe` Just 'a'
25− it "returns the second inserted value of the same type" $
26− lookup (fromList [WrapTypeable (Identity 'b'), WrapTypeable (Identity 'a')]) `shouldBe` Just 'b'
27−
28− describe "Size Test" $ do
29− it "is empty" $
30− size empty `shouldBe` 0
31− it "is of size 1 when 1 element inserted" $
32− size (one 'a') `shouldBe` 1
33− it "doesn't increase size when element of the same type is added" $
34− size (insert 'b' $ insert 'a' empty) `shouldBe` 1
35− it "returns 10 when 10 different types are inserted" $
36− size mapOf10 `shouldBe` 10
37−
38− describe "Union test" $ do
39− let m = fromList [WrapTypeable $ Identity 'a', WrapTypeable $ Identity True] `union`
40− fromList [WrapTypeable $ Identity 'b']
41− it "lookup works on union as expected" $ do
42− lookup m `shouldBe` Just 'a'
43− lookup m `shouldBe` Just True
44− lookup @Int m `shouldBe` Nothing
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
100−
101−mapOf10 :: TMap
102−mapOf10 = insert True
103− $ insert [True, False]
104− $ insert (Just True)
105− $ insert (Just ())
106− $ insert [()]
107− $ insert ()
108− $ insert @String "aaa"
109− $ insert (Just 'a')
110− $ insert 'a'
111− $ insert (11 :: Int) empty
test/Test/TypeRep/TypeRepMapProperty.hsdeleted−184
@@ -1,184 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE PolyKinds #-}
5−
6−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
7−
8−module Test.TypeRep.TypeRepMapProperty
9− ( typeRepMapPropertySpec
10− ) where
11−
12−import Prelude hiding (lookup)
13−
14−import Data.Proxy (Proxy (..))
15−import Data.Semigroup (Semigroup (..))
16−import GHC.Exts (fromList)
17−import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
18−import Hedgehog (MonadGen, assert, forAll, (===))
19−import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
20−import Test.Hspec.Hedgehog (hedgehog)
21−
22−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
23− lookup, member, union, generateOrderMapping, fromSortedList,
24− adjust, alter, intersection)
25−
26−import qualified Hedgehog.Gen as Gen
27−import qualified Hedgehog.Range as Range
28−
29−
30−typeRepMapPropertySpec :: Spec
31−typeRepMapPropertySpec = describe "TypeRepMap Property tests" $ do
32− describe "Map modification properties" $ do
33− insertLookupSpec
34− insertInsertSpec
35− deleteMemberSpec
36− insertInvariantSpec
37− deleteInvariantSpec
38− alterInsertSpec
39− alterDeleteSpec
40− alterAdjustSpec
41− alterModifySpec
42− intersectionSpec
43− describe "Internal helpers" $ do
44− generateOrderMappingInvariantSpec
45− describe "Instance Laws" $ do
46− semigroupAssocSpec
47− monoidIdentitySpec
48−
49−----------------------------------------------------------------------------
50−-- Map modification properties
51−----------------------------------------------------------------------------
52−
53−type Property = SpecWith (Arg Expectation)
54−
55−insertLookupSpec :: Property
56−insertLookupSpec = it "lookup k (insert k v m) == Just v" $ hedgehog $ do
57− m <- forAll genMap
58− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
59− lookup @n @IntProxy (insert proxy m) === Just proxy
60−
61−insertInsertSpec :: Property
62−insertInsertSpec = it "insert k b . insert k a == insert k b" $ hedgehog $ do
63− m <- forAll genMap
64− WrapTypeable a@(IntProxy (proxy :: Proxy n) i) <- forAll genTF
65− let b = IntProxy proxy (i + 1)
66− lookup @n @IntProxy (insert b $ insert a m) === Just b
67−
68−deleteMemberSpec :: Property
69−deleteMemberSpec = it "member k . delete k == False" $ hedgehog $ do
70− m <- forAll genMap
71− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
72− shouldInsert <- forAll Gen.bool
73−
74− if shouldInsert then
75− member @n (delete @n $ insert proxy m) === False
76− else
77− member @n (delete @n m) === False
78−
79−insertInvariantSpec :: Property
80−insertInvariantSpec = it "invariantCheck (insert k b) == True" $ hedgehog $ do
81− m <- forAll genMap
82− WrapTypeable a <- forAll genTF
83− assert $ invariantCheck (insert a m)
84−
85−deleteInvariantSpec :: Property
86−deleteInvariantSpec = it "invariantCheck (delete k b) == True" $ hedgehog $ do
87− m <- forAll genMap
88− WrapTypeable (_ :: IntProxy n) <- forAll genTF
89− assert $ invariantCheck (delete @n m)
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−
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−
137−----------------------------------------------------------------------------
138−-- Semigroup and Monoid laws
139−----------------------------------------------------------------------------
140−
141−semigroupAssocSpec :: Property
142−semigroupAssocSpec = it "x <> (y <> z) == (x <> y) <> z" $ hedgehog $ do
143− x <- forAll genMap
144− y <- forAll genMap
145− z <- forAll genMap
146− (x <> (y <> z)) === ((x <> y) <> z)
147−
148−monoidIdentitySpec :: Property
149−monoidIdentitySpec = it "x <> mempty == mempty <> x == x" $ hedgehog $ do
150− x <- forAll genMap
151− x <> mempty === x
152− mempty <> x === x
153−
154−----------------------------------------------------------------------------
155−-- Generators
156−----------------------------------------------------------------------------
157−
158−data IntProxy (n :: Nat) = IntProxy (Proxy n) Int
159− deriving stock (Show, Eq)
160−
161−genMap :: MonadGen m => m (TypeRepMap IntProxy)
162−genMap = fromList <$> Gen.list (Range.linear 0 1000) genTF
163−
164−genTF :: MonadGen m => m (WrapTypeable IntProxy)
165−genTF = do
166− randNat :: Integer <- Gen.integral (Range.linear 0 10000)
167− randInt <- Gen.int Range.constantBounded
168− case someNatVal randNat of
169− Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
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
test/Test/TypeRep/Vector.hs+6−8
@@ -1,18 +1,16 @@
1−module Test.TypeRep.Vector
2− ( vectorSpec
3− ) where
1+module Test.TypeRep.Vector where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.Vector (TF (..), fromList, lookup)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.Vector
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−vectorSpec :: Spec
15−vectorSpec = describe "Vector TypeRep" $
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup =
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (fromList [TF (Identity 'a')]) `shouldBe` Just (Identity 'a')
test/Test/TypeRep/VectorOpt.hs+6−8
@@ -1,18 +1,16 @@
1−module Test.TypeRep.VectorOpt
2− ( optimalVectorSpec
3− ) where
1+module Test.TypeRep.VectorOpt where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−optimalVectorSpec :: Spec
15−optimalVectorSpec = describe "Optimal Vector TypeRep" $
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup =
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (fromList [TF $ Identity 'a']) `shouldBe` Just (Identity 'a')
typerep-extra-impls/Data/TypeRep/CMap.hs+3−16
@@ -1,12 +1,5 @@
1−{-# LANGUAGE PolyKinds #-}
2−
3−{- |
4−Copyright: (c) 2017-2020 Kowainik
5−SPDX-License-Identifier: MPL-2.0
6−Maintainer: Kowainik <xrom.xkov@gmail.com>
7−
8−TypeRepMap implementation based on @containers@ 'Map'.
9−-}
1+{-# LANGUAGE KindSignatures #-}
2+{-# LANGUAGE PolyKinds #-}
103
114module Data.TypeRep.CMap
125 ( TypeRepMap (..)
@@ -19,8 +12,6 @@ module Data.TypeRep.CMap
1912
2013import Prelude hiding (lookup)
2114
22−import Control.DeepSeq
23−import Data.Kind (Type)
2415import Data.Proxy (Proxy (..))
2516import Data.Typeable (TypeRep, Typeable, typeRep)
2617import GHC.Base (Any)
@@ -28,15 +19,11 @@ import Unsafe.Coerce (unsafeCoerce)
2819
2920import qualified Data.Map.Lazy as LMap
3021
31−
3222-- | Map-like data structure with types served as the keys.
33−newtype TypeRepMap (f :: k -> Type) = TypeRepMap
23+newtype TypeRepMap (f :: k -> *) = TypeRepMap
3424 { unMap :: LMap.Map TypeRep Any
3525 }
3626
37−instance NFData (TypeRepMap f) where
38− rnf x = rnf (keys x) `seq` ()
39−
4027-- | Empty structure.
4128empty :: TypeRepMap f
4229empty = TypeRepMap mempty
typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−20
@@ -1,16 +1,9 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE TypeFamilies #-}
6−
7−{- |
8−Copyright: (c) 2017-2020 Kowainik
9−SPDX-License-Identifier: MPL-2.0
10−Maintainer: Kowainik <xrom.xkov@gmail.com>
11−
12−TypeRepMap implementation based on the optimal Vector.
13−-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE MagicHash #-}
5+{-# LANGUAGE PolyKinds #-}
6+{-# LANGUAGE TypeFamilies #-}
147
158module Data.TypeRep.OptimalVector
169 ( -- * Map type
@@ -30,7 +23,6 @@ module Data.TypeRep.OptimalVector
3023import Prelude hiding (lookup)
3124
3225import Control.Arrow ((&&&))
33−import Control.DeepSeq
3426import Data.Kind (Type)
3527import Data.Proxy (Proxy (..))
3628import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
@@ -43,16 +35,12 @@ import Unsafe.Coerce (unsafeCoerce)
4335import qualified Data.Vector as V
4436import qualified Data.Vector.Unboxed as Unboxed
4537
46−
4738data TypeRepMap (f :: k -> Type) = TypeRepMap
4839 { fingerprintAs :: Unboxed.Vector Word64
4940 , fingerprintBs :: Unboxed.Vector Word64
5041 , anys :: V.Vector Any
5142 }
5243
53−instance NFData (TypeRepMap f) where
54− rnf x = x `seq` ()
55−
5644fromAny :: Any -> f a
5745fromAny = unsafeCoerce
5846
@@ -61,7 +49,7 @@ empty :: TypeRepMap f
6149empty = TypeRepMap mempty mempty mempty
6250
6351-- | Inserts the value with its type as a key.
64−insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
52+insert :: forall a f . Typeable a => a -> TypeRepMap f -> TypeRepMap f
6553insert = undefined
6654
6755-- | Looks up the value at the type.
@@ -111,7 +99,7 @@ binarySearch (Fingerprint a b) fpAs fpBs =
11199data TF f where
112100 TF :: Typeable a => f a -> TF f
113101
114−fromF :: f a -> Proxy a
102+fromF :: Typeable a => f a -> Proxy a
115103fromF _ = Proxy
116104
117105fromList :: forall f . [TF f] -> TypeRepMap f
typerep-extra-impls/Data/TypeRep/Vector.hs+2−11
@@ -7,14 +7,6 @@
77{-# LANGUAGE PolyKinds #-}
88{-# LANGUAGE TypeFamilies #-}
99
10−{- |
11−Copyright: (c) 2017-2020 Kowainik
12−SPDX-License-Identifier: MPL-2.0
13−Maintainer: Kowainik <xrom.xkov@gmail.com>
14−
15−TypeRepMap implementation based on Vector.
16−-}
17−
1810module Data.TypeRep.Vector
1911 ( TypeRepVector (..)
2012 , TF (..)
@@ -41,7 +33,6 @@ import qualified Data.Vector.Generic as G
4133import qualified Data.Vector.Generic.Mutable as M
4234import qualified Data.Vector.Unboxed as Unboxed
4335
44−
4536data instance Unboxed.MVector s Fingerprint = MFingerprintVector (Unboxed.MVector s Word64) (Unboxed.MVector s Word64)
4637data instance Unboxed.Vector Fingerprint = FingerprintVector (Unboxed.Vector Word64) (Unboxed.Vector Word64)
4738
@@ -144,7 +135,7 @@ empty :: TypeRepVector f
144135empty = TypeRepVect mempty mempty
145136
146137-- | Inserts the value with its type as a key.
147−insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
138+insert :: forall a f . Typeable a => a -> TypeRepVector f -> TypeRepVector f
148139insert = undefined
149140
150141-- | Looks up the value at the type.
@@ -164,7 +155,7 @@ size = Unboxed.length . fingerprints
164155data TF f where
165156 TF :: Typeable a => f a -> TF f
166157
167−fromF :: f a -> Proxy a
158+fromF :: Typeable a => f a -> Proxy a
168159fromF _ = Proxy
169160
170161fromList :: forall f . [TF f] -> TypeRepVector f
typerep-map.cabal+60−90
@@ -1,133 +1,103 @@
1−cabal-version: 2.4
21name: typerep-map
3−version: 0.3.3.0
4−synopsis: Efficient implementation of a dependent map with types as keys
5−description:
6− A dependent map from type representations to values of these types.
7− .
8− Here is an illustration of such a map:
9− .
10− > TMap
11− > ---------------
12− > Int -> 5
13− > Bool -> True
14− > Char -> 'x'
15− .
16− In addition to @TMap@, we provide @TypeRepMap@ parametrized by a
17− @vinyl@-style interpretation. This data structure is equivalent to @DMap
18− TypeRep@, but with significantly more efficient lookups.
19−
2+version: 1.0
3+synopsis: Efficient implementation of data type with types as keys
4+description: See README.md for more details.
205homepage: https://github.com/kowainik/typerep-map
216bug-reports: https://github.com/kowainik/typerep-map/issues
22−license: MPL-2.0
7+license: MIT
238license-file: LICENSE
24−author: Veronika Romashkina, Vladislav Zavialov, Dmitrii Kovanikov
25−maintainer: Kowainik <xrom.xkov@gmail.com>
26−copyright: 2017-2020 Kowainik
9+author: Kowainik, Vladislav Zavialov
10+maintainer: xrom.xkov@gmail.com
11+copyright: 2017-2018 Kowainik
2712category: Data, Data Structures, Types
2813build-type: Simple
29−extra-doc-files: README.md
30− , CHANGELOG.md
31−tested-with: GHC == 8.2.2
32− , GHC == 8.4.4
33− , GHC == 8.6.5
34− , GHC == 8.8.3
35− , GHC == 8.10.1
14+extra-source-files: README.md
15+cabal-version: 2.0
16+tested-with: GHC == 8.0.2
17+ , GHC == 8.2.2
18+ , GHC == 8.4.3
3619
3720source-repository head
3821 type: git
3922 location: https://github.com/kowainik/typerep-map.git
4023
41−common common-options
42− build-depends: base >= 4.10 && < 4.15
43−
44− default-language: Haskell2010
45− default-extensions: BangPatterns
46− DerivingStrategies
47− OverloadedStrings
24+library
25+ hs-source-dirs: src
26+ exposed-modules: Data.TypeRep.Map
27+ Data.TypeRep.TMap
28+ ghc-options: -Wall
29+ build-depends: base
30+ , containers
31+ , ghc-prim
32+ , primitive
33+ default-extensions: OverloadedStrings
4834 RecordWildCards
4935 ScopedTypeVariables
5036 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)
64− ghc-options: -Wmissing-deriving-strategies
65− -Werror=missing-deriving-strategies
66− if impl(ghc >= 8.10)
67− ghc-options: -Wunused-packages
68−
69−library
70− import: common-options
71− hs-source-dirs: src
72− exposed-modules: Data.TMap
73− Data.TypeRepMap
74− Data.TypeRepMap.Internal
75−
76− build-depends: ghc-prim >= 0.5.1.1 && < 0.7
77− , primitive ^>= 0.7.0
78− , deepseq ^>= 1.4
37+ default-language: Haskell2010
7938
8039library typerep-extra-impls
81− import: common-options
8240 hs-source-dirs: typerep-extra-impls
8341 exposed-modules: Data.TypeRep.CMap
8442 Data.TypeRep.OptimalVector
8543 Data.TypeRep.Vector
44+ ghc-options: -Wall
45+ build-depends: base
46+ , containers
47+ , vector
48+ default-extensions: OverloadedStrings
49+ RecordWildCards
50+ ScopedTypeVariables
51+ TypeApplications
8652
87− build-depends: containers >= 0.5.10.2 && < 0.7
88− , vector ^>= 0.12.0.1
89− , deepseq ^>= 1.4
53+ default-language: Haskell2010
9054
9155test-suite typerep-map-test
92− import: common-options
9356 type: exitcode-stdio-1.0
9457 hs-source-dirs: test
95−
9658 main-is: Test.hs
9759 other-modules: Test.TypeRep.CMap
98− , Test.TypeRep.TypeRepMap
99− , Test.TypeRep.TypeRepMapProperty
60+ , Test.TypeRep.CacheMap
61+ , Test.TypeRep.MapProperty
10062 , Test.TypeRep.Vector
10163 , Test.TypeRep.VectorOpt
102−
103− build-depends: ghc-typelits-knownnat >= 0.4.2 && < 0.8
104− , hedgehog ^>= 1.0
105− , hspec ^>= 2.7.1
106− , hspec-hedgehog ^>= 0.0.1
64+ build-depends: base
65+ , ghc-typelits-knownnat
66+ , hedgehog
10767 , typerep-map
10868 , typerep-extra-impls
69+ , tasty
70+ , tasty-discover >= 4.1.1
71+ , tasty-hedgehog
72+ , tasty-hspec
73+ build-tool-depends: tasty-discover:tasty-discover
74+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
75+ default-extensions: ScopedTypeVariables
76+ TypeApplications
10977
110− ghc-options: -threaded -rtsopts -with-rtsopts=-N
78+ default-language: Haskell2010
11179
11280benchmark typerep-map-benchmark
113− import: common-options
11481 type: exitcode-stdio-1.0
82+ default-language: Haskell2010
83+ ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
11584 hs-source-dirs: benchmark
116−
11785 main-is: Main.hs
11886 other-modules: CMap
11987 , CacheMap
120− , DMap
121− , Spec
12288 , Vector
12389 , OptimalVector
124−
125− build-depends: criterion >= 1.4.1.0 && < 1.6
126− , deepseq ^>= 1.4.3.0
127− , dependent-map >= 0.2.4.0 && < 0.5
128− , dependent-sum >= 0.5 && < 0.8
129− , ghc-typelits-knownnat >= 0.4.2 && < 0.8
90+ build-depends: base
91+ , criterion
92+ , deepseq
93+ , dependent-map
94+ , dependent-sum
95+ , ghc-typelits-knownnat
13096 , typerep-map
13197 , typerep-extra-impls
132−
133− ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
98+ default-extensions: OverloadedStrings
99+ RecordWildCards
100+ ScopedTypeVariables
101+ TypeApplications
102+ if impl(ghc >= 8.2.2)
103+ other-modules: DMap