on main help sign in
33 files changed+1728−717
since splitsnapshots
unifiedsplit
.github/CODEOWNERS+1 .github/workflows/ci.yml+47 .gitignore+1 .travis.yml+8−66 CHANGELOG.md+30−8 CONTRIBUTING.md−53 LICENSE+373−21 README.md+17−9 appveyor.yml+35 benchmark/CMap.hs+53−33 benchmark/CacheMap.hs+55−29 benchmark/DMap.hs+56−53 benchmark/Main.hs+41−20 benchmark/OptimalVector.hs+34−36 benchmark/Spec.hs+64 benchmark/Vector.hs+23−22 shell.nix−10 src/Data/TMap.hs+54−4 src/Data/TypeRepMap.hs+50−40 src/Data/TypeRepMap/Internal.hs+327−65 stack.yaml+4−2 test/Test.hs+21−1 test/Test/TypeRep/CMap.hs+9−7 test/Test/TypeRep/CacheMap.hs−50 test/Test/TypeRep/MapProperty.hs−110 test/Test/TypeRep/TypeRepMap.hs+111 test/Test/TypeRep/TypeRepMapProperty.hs+184 test/Test/TypeRep/Vector.hs+8−6 test/Test/TypeRep/VectorOpt.hs+8−6 typerep-extra-impls/Data/TypeRep/CMap.hs+16−3 typerep-extra-impls/Data/TypeRep/OptimalVector.hs+20−8 typerep-extra-impls/Data/TypeRep/Vector.hs+11−2 typerep-map.cabal+67−53
.github/CODEOWNERSnew file+1
@@ -0,0 +1 @@
1+* @chshersh @vrom911
.github/workflows/ci.ymlnew file+47
@@ -0,0 +1,47 @@
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,6 +24,7 @@ cabal.project.local
2424
2525# Stack
2626.stack-work/
27+stack.yaml.lock
2728
2829### IDE/support
2930# Vim
.travis.yml+8−66
@@ -6,81 +6,23 @@ git:
66
77cache:
88 directories:
9− - "$HOME/.cabal"
10− - "$HOME/.ghc"
119 - "$HOME/.stack"
12− - "$HOME/build/kowainik/typerep-map/.stack-work"
1310
1411matrix:
15−
1612 include:
17−
18− - ghc: 8.2.2
19− env: GHCVER='8.2.2' CABALVER='head'
20− os: linux
21− addons:
22− apt:
23− sources:
24− - hvr-ghc
25− packages:
26− - ghc-8.2.2
27− - cabal-install-head
28−
29− - ghc: 8.4.3
30− env: GHCVER='8.4.3' CABALVER='head'
31− os: linux
32− addons:
33− apt:
34− sources:
35− - hvr-ghc
36− packages:
37− - ghc-8.4.3
38− - cabal-install-head
39−
40− - ghc: 8.6.1
41− env: GHCVER='8.6.1' CABALVER='head'
42− os: linux
43− addons:
44− apt:
45− sources:
46− - hvr-ghc
47− packages:
48− - ghc-8.6.1
49− - cabal-install-head
50−
51− - ghc: 8.4.3
52− env: STACK_YAML="$HOME/build/kowainik/typerep-map/stack.yaml"
13+ - env: GHC='8.8.3'
5314 os: linux
54− addons:
55− apt:
56− packages:
57− - libgmp-dev
15+ - env: GHC='8.8.3'
16+ os: osx
5817
5918install:
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
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
7623
7724script:
78− - |
79− if [ -z "$STACK_YAML" ]; then
80− cabal new-test $ALLOWNEWER
81− else
82− stack build --test --bench --no-terminal
83− fi
25+ - stack test --no-terminal
8426
8527notifications:
8628 email: false
CHANGELOG.md+30−8
@@ -1,10 +1,33 @@
1−Change log
2−==========
1+# Changelog
32
43`typerep-map` uses [PVP Versioning][1].
5−The change log is available [on GitHub][2].
4+The changelog is available [on GitHub][2].
65
7−# 0.3.0
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
831
932* [#46](https://github.com/kowainik/typerep-map/issues/46):
1033 Make `Show` instance for `TypeRepMap` show keys.
@@ -15,8 +38,7 @@ The change log is available [on GitHub][2].
1538 Rewrite `fromSortedList` to use `Array` and `MutableArray`
1639 instead of `IntMap`.
1740
18−
19−# 0.2.0
41+## 0.2.0
2042
2143* [#43](https://github.com/kowainik/typerep-map/issues/43):
2244 Implement `IsList` instance for `TypeRepMap`.
@@ -26,9 +48,9 @@ The change log is available [on GitHub][2].
2648 Add `map` function for `TMap`.
2749* Drop support for `ghc-8.0.2`.
2850
29−# 0.1.0
51+## 0.1.0
3052
3153* Initially created.
3254
3355[1]: https://pvp.haskell.org
34−[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
56+[2]: https://github.com/kowainik/typerep-map/releases
CONTRIBUTING.mddeleted−53
@@ -1,53 +0,0 @@
1−# Contributing to the Kowainik repositories
2−
3−## :wave: Greetings Traveler!
4−
5−We are delighted you're reading this, and we appreciate the effort you're
6−taking to make our projects awesome! :sparkles:
7−
8−## How to contribute
9−
10−### :bug: Report bugs or feature request :bulb:
11−
12−If you discover a bug or have any proposals on how to make this project better
13−don't hesitate to create an issue [here](../../issues/new) in a free format.
14−
15−### Create a PR
16−
17−We love to receive pull requests from everyone! It's usually a good idea
18−to tell about your intention to work on something under the corresponding
19−issue, so everyone is aware that you're on it. If there's no such issue — simply
20−create a new one!
21−
22−To get started with the Pull Request implementation you should first
23−[fork](../../fork), then clone the repo:
24−
25− git clone git@github.com:your-username/project-name.git
26−
27−Make your changes and consider the following checklist to go through
28−before submitting your pull request.
29−
30−### :white_check_mark: Check list
31−- [ ] New/fixed features work as expected (Bonus points for the new tests).
32−- [ ] There are no warnings during compilation.
33−- [ ] `hlint .` output is: _No Hints_ (see [`hlint`][hlint] tool docs).
34−- [ ] The code is formatted with the [`stylish-haskell`][stylish-tool] tool
35− using [stylish-haskell.yaml][stylish] file in the repository.
36−- [ ] The code style of the files you changed is preserved (for more specific
37− details on our style guide check [this document][style-guide]).
38−- [ ] Commit messages are in the proper format.
39− Start the first line of the commit with the issue number in square parentheses.
40−
41− **_Example:_** `[#42] Upgrade upper bounds of 'base'`
42−
43−After all above is done commit and push to your fork.
44−Now you are ready to [submit a pull request](../../compare).
45−
46−
47−----------
48−Thanks for spending your time on reading this contributing guide! :sparkling_heart:
49−
50−[stylish]: .stylish-haskell.yaml
51−[stylish-tool]: http://hackage.haskell.org/package/stylish-haskell
52−[hlint]: http://hackage.haskell.org/package/hlint
53−[style-guide]: https://github.com/kowainik/org/blob/master/style-guide.md#haskell-style-guide
LICENSE+373−21
@@ -1,21 +1,373 @@
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.
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.
README.md+17−9
@@ -1,13 +1,21 @@
11# typerep-map
22
3−![electricity](https://user-images.githubusercontent.com/8126674/44323413-788dd700-a484-11e8-842e-f224cfaa4206.png)
4−[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg)](https://hackage.haskell.org/package/typerep-map)
5−[![Build status](https://secure.travis-ci.org/kowainik/typerep-map.svg)](https://travis-ci.org/kowainik/typerep-map)
6−[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
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)
9+[![Stackage LTS](http://stackage.org/package/typerep-map/badge/lts)](http://stackage.org/lts/package/typerep-map)
10+[![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+
713
814`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.
915
10−For the more details on the implementation see [this blog post](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step.html).
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)
1119
1220## Usage example
1321
@@ -47,10 +55,10 @@ False
4755Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
4856on structure with size `10^4`:
4957
50−| | ghc-8.2.2 | ghc-8.4.3 |
51−|----------------|-----------|-----------|
52−| `DMap TypeRep` | 517.5 ns | 779.9 ns |
53−| `typerep-map` | 205.3 ns | 187.2 ns |
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 |
5462
5563 ghc-8.2.2 | ghc-8.4.3
5664:---------:|:-----------:
appveyor.ymlnew file+35
@@ -0,0 +1,35 @@
1+clone_folder: "c:\\WORK"
2+clone_depth: 5
3+
4+# Do not build feature branch with open Pull Requests
5+skip_branch_with_pr: true
6+
7+platform:
8+ - x86
9+ - x86_64
10+
11+cache:
12+ - "C:\\SR"
13+ - dist-newstyle
14+
15+environment:
16+ global:
17+ CABOPTS: --store-dir=C:\\SR
18+
19+ matrix:
20+ - GHCVER: 8.8.3
21+
22+install:
23+ - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2
24+ - choco install -y cabal --version 3.0.0.0
25+ - choco install -y ghc --version 8.8.3
26+ - refreshenv
27+
28+before_build:
29+ - cabal --version
30+ - ghc --version
31+ - cabal %CABOPTS% update
32+
33+build_script:
34+ - cabal %CABOPTS% build --enable-tests --write-ghc-environment-files=always
35+ - cabal %CABOPTS% test --enable-tests
benchmark/CMap.hs+53−33
@@ -1,57 +1,77 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
13{-# 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−
119module CMap
12− ( benchMap
13− , prepareBenchMap
14− ) where
15−
16−import Criterion.Main (Benchmark, bench, bgroup, nf)
10+ ( spec
11+ ) where
1712
1813import Prelude hiding (lookup)
1914
20−import Control.DeepSeq (rnf)
21−import Control.Exception
15+import Criterion.Main (bench, env, nf, whnf)
16+import Data.Kind (Type)
2217import Data.Maybe (fromJust)
2318import Data.Proxy (Proxy (..))
2419import Data.Typeable (Typeable)
25−import GHC.TypeLits
20+import GHC.TypeLits (type (+), KnownNat, Nat)
2621
27−import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, keys, lookup)
22+import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
2823
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− ]
24+import Spec (BenchSpec (..))
3525
36−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
37− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
38− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
39− )
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+ )
4050tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4151 where
4252 lp :: forall (a::Nat). Typeable a => Proxy a
4353 lp = fromJust $ lookup tmap
4454
45−-- TypeRepMap of 10000 elements
46−bigMap :: TypeRepMap (Proxy :: Nat -> *)
47−bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
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
4869
49−buildBigMap :: forall a . (KnownNat a) => Int -> Proxy (a :: Nat) -> TypeRepMap (Proxy :: Nat -> *) -> TypeRepMap (Proxy :: Nat -> *)
70+buildBigMap
71+ :: forall a . (KnownNat a)
72+ => Int
73+ -> Proxy (a :: Nat)
74+ -> TypeRepMap (Proxy :: Nat -> Type)
75+ -> TypeRepMap (Proxy :: Nat -> Type)
5076buildBigMap 1 x = insert x
5177buildBigMap 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+55−29
@@ -1,53 +1,79 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
13{-# 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−
119module CacheMap
12− ( benchCacheMap
13− ) where
14−
15−import Criterion.Main (Benchmark, bench, bgroup, nf)
10+ ( spec
11+ ) where
1612
1713import Prelude hiding (lookup)
1814
15+import Criterion.Main (bench, env, nf, whnf)
16+import Data.Kind (Type)
1917import Data.Maybe (fromJust)
2018import Data.Proxy (Proxy (..))
2119import Data.Typeable (Typeable)
2220import GHC.Exts (fromList)
23−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
2422
25−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), lookup)
23+import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
2624
27−benchCacheMap :: Benchmark
28−benchCacheMap = bgroup "vector optimal cache"
29− [ bench "lookup" $ nf tenLookups bigMap
30− -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
31− -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
32− ]
25+import Spec (BenchSpec (..))
3326
34−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
35− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
36− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
37− )
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+ )
3851tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
3952 where
40− lp :: forall (a::Nat). Typeable a => Proxy a
53+ lp :: forall (a :: Nat) . Typeable a => Proxy a
4154 lp = fromJust $ lookup tmap
4255
43−-- TypeRepMap of 10000 elements
44−bigMap :: TypeRepMap (Proxy :: Nat -> *)
45−bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
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+
4671
47−buildBigMap :: forall a . (KnownNat a)
48− => Int
49− -> Proxy (a :: Nat)
50− -> [WrapTypeable (Proxy :: Nat -> *)]
51− -> [WrapTypeable (Proxy :: Nat -> *)]
72+buildBigMap
73+ :: forall a . (KnownNat a)
74+ => Int
75+ -> Proxy (a :: Nat)
76+ -> [WrapTypeable (Proxy :: Nat -> Type)]
77+ -> [WrapTypeable (Proxy :: Nat -> Type)]
5278buildBigMap 1 x = (WrapTypeable x :)
5379buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hs+56−53
@@ -1,86 +1,89 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
13{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE ExplicitNamespaces #-}
34{-# 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 #-}
129
1310module DMap
14− ( benchDMap
15− , prepareBenchDMap
16− ) where
17−
18−import Criterion.Main (Benchmark, bench, bgroup, nf)
11+ ( spec
12+ ) where
1913
2014import Prelude hiding (lookup)
2115
22−import Control.DeepSeq (rnf)
23−import Control.Exception
16+import Control.DeepSeq (NFData (..))
17+import Criterion.Main (bench, env, nf, whnf)
18+import Data.Kind (Type)
2419import Data.Maybe (fromJust)
2520import Data.Proxy (Proxy (..))
26−import Data.Type.Equality ((:~:) (..))
27−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
2822import Type.Reflection (TypeRep, Typeable, typeRep)
2923import Type.Reflection.Unsafe (typeRepFingerprint)
30−import Unsafe.Coerce (unsafeCoerce)
3124
3225import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
33−import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
34−import Data.Some (Some (This))
26+import Data.Some (Some (Some))
27+
28+import Spec (BenchSpec (..))
29+
3530
3631type TypeRepMap = DMap TypeRep
3732
38−benchDMap :: Benchmark
39−benchDMap = bgroup "dependent map"
40− [ bench "lookup" $ nf tenLookups bigMap
41− -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
42− -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
43− ]
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+ }
4446
45−tenLookups :: TypeRepMap (Proxy :: Nat -> *)
46− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
47− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
48− )
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+ )
4952tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5053 where
5154 lp :: forall (a :: Nat) . Typeable a => Proxy a
5255 lp = fromJust $ lookup (typeRep @a) tmap
5356
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))
68+
5469-- TypeRepMap of 10000 elements
55−bigMap :: TypeRepMap (Proxy :: Nat -> *)
56−bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
70+mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
71+mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
5772
58−buildBigMap :: forall a . (KnownNat a)
59− => Int
60− -> Proxy (a :: Nat)
61− -> TypeRepMap (Proxy :: Nat -> *)
62− -> TypeRepMap (Proxy :: Nat -> *)
73+buildBigMap
74+ :: forall a . (KnownNat a)
75+ => Int
76+ -> Proxy (a :: Nat)
77+ -> TypeRepMap (Proxy :: Nat -> Type)
78+ -> TypeRepMap (Proxy :: Nat -> Type)
6379buildBigMap 1 x = insert (typeRep @a) x
6480buildBigMap n x = insert (typeRep @a) x
6581 . buildBigMap (n - 1) (Proxy @(a + 1))
6682
67−rknf :: TypeRepMap f -> ()
68−rknf = rnf . map (\(This t) -> typeRepFingerprint t) . keys
69−
70−prepareBenchDMap :: IO ()
71−prepareBenchDMap = evaluate (rknf bigMap)
72−
73−instance GEq TypeRep where
74− geq :: TypeRep a -> TypeRep b -> Maybe (a :~: b)
75− geq (typeRepFingerprint -> a) (typeRepFingerprint -> b) =
76− if a == b
77− then Just $ unsafeCoerce Refl
78− else Nothing
83+-- | Wrapper that provides NFData instance to the 'DMap' structure.
84+newtype DMapNF f = DMapNF (TypeRepMap f)
7985
80−instance GCompare TypeRep where
81− gcompare :: TypeRep a -> TypeRep b -> GOrdering a b
82− gcompare (typeRepFingerprint -> a) (typeRepFingerprint -> b) =
83− case compare a b of
84− EQ -> unsafeCoerce GEQ
85− LT -> GLT
86− GT -> GGT
86+instance NFData (DMapNF f) where
87+ rnf :: DMapNF f -> ()
88+ rnf (DMapNF x) =
89+ rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
benchmark/Main.hs+41−20
@@ -1,31 +1,52 @@
11{-# LANGUAGE CPP #-}
22
3−module Main where
3+module Main
4+ ( main
5+ ) where
46
5−import Criterion.Main (defaultMain)
7+import Criterion.Main (bgroup, defaultMain)
68
7−import CacheMap (benchCacheMap)
8−import CMap (benchMap, prepareBenchMap)
9+import Spec (BenchSpec (..))
10+
11+import qualified CacheMap
12+import qualified CMap
913#if ( __GLASGOW_HASKELL__ >= 802 )
10−import DMap (benchDMap, prepareBenchDMap)
14+import qualified DMap
1115#endif
12−import OptimalVector (benchVectorOpt, prepareBenchVectorOpt)
13−--import Vector (benchVector, prepareBenchVector)
16+import qualified OptimalVector as OptVec
17+
1418
1519main :: IO ()
1620main = do
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
21+ let specs = [("CMap", CMap.spec)
22+ ,("CacheMap", CacheMap.spec)
2823#if ( __GLASGOW_HASKELL__ >= 802 )
29− , benchDMap
24+ , ("DMap", DMap.spec)
3025#endif
31− ]
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+ ]
benchmark/OptimalVector.hs+34−36
@@ -1,62 +1,60 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
13{-# 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−
1210module OptimalVector
13− ( benchVectorOpt
14− , prepareBenchVectorOpt
11+ ( spec
1512 ) where
1613
17−import Criterion.Main (Benchmark, bench, bgroup, nf)
18−
1914import Prelude hiding (lookup)
2015
21−import Control.DeepSeq (rnf)
22−import Control.Exception
16+import Criterion.Main (bench, env, nf)
17+import Data.Kind (Type)
2318import Data.Maybe (fromJust)
2419import Data.Proxy (Proxy (..))
2520import Data.Typeable (Typeable)
26−import GHC.TypeLits
21+import GHC.TypeLits (type (+), KnownNat, Nat)
2722
2823import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
2924
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− )
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+ )
4144tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4245 where
4346 lp :: forall (a::Nat). Typeable a => Proxy a
4447 lp = fromJust $ lookup tmap
4548
4649-- TypeRepMap of 10000 elements
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 -> *)]
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)]
5559buildBigMap 1 x = (TF x :)
5660buildBigMap 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.hsnew file+64
@@ -0,0 +1,64 @@
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+23−22
@@ -1,31 +1,30 @@
1+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2+
13{-# 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−
1210module Vector
13− ( benchVector
14− , prepareBenchVector
15− ) where
16−
17−import Criterion.Main (Benchmark, bench, bgroup, nf)
11+ ( benchVector
12+ , prepareBenchVector
13+ ) where
1814
1915import Prelude hiding (lookup)
2016
2117import Control.DeepSeq (rnf)
22−import Control.Exception
18+import Control.Exception (evaluate)
19+import Criterion.Main (Benchmark, bench, bgroup, nf)
20+import Data.Kind (Type)
2321import Data.Maybe (fromJust)
2422import Data.Proxy (Proxy (..))
2523import Data.Typeable (Typeable)
26−import GHC.TypeLits
24+import GHC.TypeLits (type (+), KnownNat, Nat)
25+
26+import Data.TypeRep.Vector (TF (..), TypeRepVector, fingerprints, fromList, lookup)
2727
28−import Data.TypeRep.Vector
2928
3029benchVector :: Benchmark
3130benchVector = bgroup "vector"
@@ -34,24 +33,26 @@ benchVector = bgroup "vector"
3433 -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
3534 ]
3635
37−tenLookups :: TypeRepVector (Proxy :: Nat -> *)
38− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40− )
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+ )
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 -> *)
47+bigMap :: TypeRepVector (Proxy :: Nat -> Type)
4848bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
4949
50−buildBigMap :: forall a . (KnownNat a)
51− => Int
52− -> Proxy (a :: Nat)
53− -> [TF (Proxy :: Nat -> *)]
54− -> [TF (Proxy :: Nat -> *)]
50+buildBigMap
51+ :: forall a . (KnownNat a)
52+ => Int
53+ -> Proxy (a :: Nat)
54+ -> [TF (Proxy :: Nat -> Type)]
55+ -> [TF (Proxy :: Nat -> Type)]
5556buildBigMap 1 x = (TF x :)
5657buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
5758
shell.nixdeleted−10
@@ -1,10 +0,0 @@
1−# { stdenv, pkgs, haskell }:
2−with import <nixpkgs> { };
3−
4−stdenv.mkDerivation rec {
5− name = "typerep-map";
6− buildInputs = [
7− haskell.compiler.ghc843
8− pkgs.cabal-install
9− ];
10−}
src/Data/TMap.hs+54−4
@@ -2,6 +2,10 @@
22{-# LANGUAGE Rank2Types #-}
33
44{- |
5+Copyright: (c) 2017-2020 Kowainik
6+SPDX-License-Identifier: MPL-2.0
7+Maintainer: Kowainik <xrom.xkov@gmail.com>
8+
59'TMap' is a heterogeneous data structure similar in its essence to
610'Data.Map.Map' with types as keys, where each value has the type of its key.
711
@@ -33,14 +37,19 @@ module Data.TMap
3337 , delete
3438 , unionWith
3539 , union
40+ , intersectionWith
41+ , intersection
3642 , map
3743 , adjust
44+ , alter
3845
3946 -- * Query
4047 , lookup
4148 , member
4249 , size
4350 , keys
51+ , keysWith
52+ , toListWith
4453 ) where
4554
4655import Prelude hiding (lookup, map)
@@ -48,7 +57,7 @@ import Prelude hiding (lookup, map)
4857import Data.Functor.Identity (Identity (..))
4958import Data.Typeable (Typeable)
5059import GHC.Exts (coerce)
51−import Type.Reflection (SomeTypeRep)
60+import Type.Reflection (SomeTypeRep, TypeRep)
5261
5362import qualified Data.TypeRepMap as F
5463
@@ -83,6 +92,7 @@ one x = coerce (F.one @a @Identity $ coerce x)
8392{- |
8493
8594Insert a value into a 'TMap'.
95+TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
8696
8797prop> size (insert v tm) >= size tm
8898prop> member @a (insert (x :: a) tm) == True
@@ -94,6 +104,9 @@ insert x = coerce (F.insert @a @Identity $ coerce x)
94104
95105{- | Delete a value from a 'TMap'.
96106
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+
97110prop> size (delete @a tm) <= size tm
98111prop> member @a (delete @a tm) == False
99112
@@ -110,10 +123,10 @@ delete = F.delete @a @Identity
110123{-# INLINE delete #-}
111124
112125-- | The union of two 'TMap's using a combining function.
113−unionWith :: (forall x. x -> x -> x) -> TMap -> TMap -> TMap
126+unionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
114127unionWith f = F.unionWith fId
115128 where
116− fId :: forall y . Identity y -> Identity y -> Identity y
129+ fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
117130 fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
118131{-# INLINE unionWith #-}
119132
@@ -123,6 +136,24 @@ union :: TMap -> TMap -> TMap
123136union = F.union
124137{-# INLINE union #-}
125138
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+
126157{- | Lookup a value of the given type in a 'TMap'.
127158
128159>>> x = lookup $ insert (11 :: Int) empty
@@ -156,6 +187,16 @@ keys :: TMap -> [SomeTypeRep]
156187keys = F.keys
157188{-# INLINE keys #-}
158189
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+
159200-- | Map a function over the values.
160201map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
161202map f = F.hoistWithKey (liftToIdentity f)
@@ -166,5 +207,14 @@ adjust :: Typeable a => (a -> a) -> TMap -> TMap
166207adjust f = F.adjust (liftToIdentity f)
167208{-# INLINE adjust #-}
168209
169−liftToIdentity :: forall a. Typeable a => (a -> a) -> Identity a -> Identity a
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
170220liftToIdentity = coerce
src/Data/TypeRepMap.hs+50−40
@@ -1,45 +1,50 @@
11{-# LANGUAGE NoImplicitPrelude #-}
22
3−-- | A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
4−-- sort of parametrization may be familiar to users of @vinyl@ records.
5−--
6−-- @'TypeRepMap' f@ is a more efficient replacement for @DMap
7−-- 'Type.Reflection.TypeRep' f@ (where @DMap@ is from the @dependent-map@
8−-- package).
9−--
10−-- Here is an example of using 'Prelude.Maybe' as an interpretation, with a
11−-- comparison to 'Data.TMap.TMap':
12−--
13−-- @
14−-- 'Data.TMap.TMap' 'TypeRepMap' 'Prelude.Maybe'
15−-- -------------- -------------------
16−-- Int -> 5 Int -> Just 5
17−-- Bool -> True Bool -> Nothing
18−-- Char -> \'x\' Char -> Just \'x\'
19−-- @
20−--
21−-- In fact, a 'Data.TMap.TMap' is defined as 'TypeRepMap'
22−-- 'Data.Functor.Identity'.
23−--
24−-- Since 'Type.Reflection.TypeRep' is poly-kinded, the interpretation can use
25−-- any kind for the keys. For instance, we can use the 'GHC.TypeLits.Symbol'
26−-- kind to use 'TypeRepMap' as an extensible record:
27−--
28−-- @
29−-- newtype Field name = F (FType name)
30−--
31−-- type family FType (name :: Symbol) :: Type
32−-- type instance FType "radius" = Double
33−-- type instance FType "border-color" = RGB
34−-- type instance FType "border-width" = Double
35−--
36−-- 'TypeRepMap' Field
37−-- --------------------------------------
38−-- "radius" -> F 5.7
39−-- "border-color" -> F (rgb 148 0 211)
40−-- "border-width" -> F 0.5
41−-- @
42−--
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+--}
4348module Data.TypeRepMap
4449 ( -- * Map type
4550 TypeRepMap()
@@ -52,17 +57,22 @@ module Data.TypeRepMap
5257 , insert
5358 , delete
5459 , adjust
60+ , alter
5561 , hoist
5662 , hoistA
5763 , hoistWithKey
5864 , unionWith
5965 , union
66+ , intersectionWith
67+ , intersection
6068
6169 -- * Query
6270 , lookup
6371 , member
6472 , size
6573 , keys
74+ , keysWith
75+ , toListWith
6676
6777 -- * 'IsList'
6878 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+327−65
@@ -1,47 +1,69 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE InstanceSigs #-}
6−{-# LANGUAGE KindSignatures #-}
7−{-# LANGUAGE MagicHash #-}
8−{-# LANGUAGE PolyKinds #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE TypeInType #-}
12−{-# LANGUAGE ViewPatterns #-}
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
1319
1420-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
1521
16−-- | Internal API for 'TypeRepMap' and operations on it. The functions here do
17−-- not have any stability guarantees and can change between minor versions.
18−--
19−-- If you need to use this module for purposes other than tests,
20−-- create an issue.
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+
2236module Data.TypeRepMap.Internal where
2337
2438import Prelude hiding (lookup)
2539
40+import Control.DeepSeq
2641import Control.Monad.ST (ST, runST)
2742import Control.Monad.Zip (mzip)
2843import Data.Function (on)
2944import Data.Kind (Type)
45+import Data.Type.Equality ((:~:) (..), TestEquality (..))
3046import Data.List (intercalate, nubBy)
31−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', readArray, sizeofArray,
47+import Data.Maybe (fromMaybe)
48+import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
3249 thawArray, unsafeFreezeArray, writeArray)
33−import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
34−import Data.Semigroup (Semigroup (..))
50+import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
51+import Data.Semigroup (Semigroup (..), All(..))
3552import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
3653import GHC.Exts (IsList (..), inline, sortWith)
3754import GHC.Fingerprint (Fingerprint (..))
55+#if WORD_SIZE_IN_BITS >= 64
3856import GHC.Prim (eqWord#, ltWord#)
57+#else
58+import GHC.IntWord64 (eqWord64#, ltWord64#)
59+#define eqWord eqWord64
60+#define ltWord ltWord64
61+#endif
3962import GHC.Word (Word64 (..))
4063import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeable)
4164import Type.Reflection.Unsafe (typeRepFingerprint)
4265import Unsafe.Coerce (unsafeCoerce)
4366
44−import qualified Data.Map.Strict as Map
4567import qualified GHC.Exts as GHC (fromList, toList)
4668
4769{- |
@@ -74,6 +96,9 @@ data TypeRepMap (f :: k -> Type) =
7496 }
7597 -- ^ an unsafe constructor for 'TypeRepMap'
7698
99+instance NFData (TypeRepMap f) where
100+ rnf x = rnf (keys x) `seq` ()
101+
77102-- | Shows only keys.
78103instance Show (TypeRepMap f) where
79104 show TypeRepMap{..} = "TypeRepMap [" ++ showKeys ++ "]"
@@ -93,6 +118,31 @@ instance Monoid (TypeRepMap f) where
93118 {-# INLINE mempty #-}
94119 {-# INLINE mappend #-}
95120
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+
96146-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
97147toFingerprints :: TypeRepMap f -> [Fingerprint]
98148toFingerprints TypeRepMap{..} =
@@ -119,33 +169,49 @@ prop> member @a (one (x :: f a)) == True
119169
120170-}
121171one :: forall a f . Typeable a => f a -> TypeRepMap f
122−one x = insert x empty
172+one x = TypeRepMap (primArrayFromListN 1 [fa])
173+ (primArrayFromListN 1 [fb])
174+ (pure @Array v)
175+ (pure @Array k)
176+ where
177+ (Fingerprint fa fb, v, k) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
123178{-# INLINE one #-}
124179
125180{- |
126181
127182Insert a value into a 'TypeRepMap'.
183+TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
128184
129185prop> size (insert v tm) >= size tm
130186prop> member @a (insert (x :: f a) tm) == True
131187
132188-}
133189insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
134−insert x = fromTriples . addX . toTriples
190+insert x m
191+ | size m == 0 = one x
192+ | otherwise = case cachedBinarySearch (typeFp @a) (fingerprintAs m) (fingerprintBs m) of
193+ Nothing -> union m $ one x
194+ Just i -> m {trAnys = changeAnyArr i (trAnys m)}
135195 where
136− tripleX :: (Fingerprint, Any, Any)
137− tripleX@(fpX, _, _) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
138−
139− addX :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
140− addX l = tripleX : deleteByFst fpX l
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
141202{-# INLINE insert #-}
142203
143204-- Extract the kind of a type. We use it to work around lack of syntax for
144205-- inferred type variables (which are not subject to type applications).
145206type KindOf (a :: k) = k
146207
208+type ArgKindOf (f :: k -> l) = k
209+
147210{- | Delete a value from a 'TypeRepMap'.
148211
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+
149215prop> size (delete @a tm) <= size tm
150216prop> member @a (delete @a tm) == False
151217
@@ -158,30 +224,60 @@ False
158224True
159225-}
160226delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
161−delete = fromTriples . deleteByFst (typeFp @a) . toTriples
227+delete m
228+ -- Lookups are fast, so check if we even have the element first.
229+ | not (member @a m) = m
230+ -- We know we have the element, If the map has exactly one element, we can return the empty map
231+ | size m == 1 = empty
232+ -- Otherwise, filter out the element in linear time.
233+ | otherwise = fromSortedTriples . deleteFirst ((== typeFp @a) . fst3) . toSortedTriples $ m
162234{-# INLINE delete #-}
163235
236+deleteFirst :: (a -> Bool) -> [a] -> [a]
237+deleteFirst _ [] = []
238+deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239+
164240{- |
165−Update a value at a specific key with the result of the provided function. When
166−the key is not a member of the map, the original map is returned.
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.
167243
168244>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
169245>>> lookup @String $ adjust (fmap (++ "ww")) trmap
170246Just (Identity "aww")
171247-}
172248adjust :: forall a f . Typeable a => (f a -> f a) -> TypeRepMap f -> TypeRepMap f
173−adjust fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
174− Nothing -> tr
175− Just i -> tr {trAnys = changeAnyArr i (trAnys tr)}
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)}
176273 where
177− changeAnyArr :: Int -> Array Any -> Array Any
178− changeAnyArr i trAs = runST $ do
274+ replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275+ replaceAnyAt i v trAs = runST $ do
179276 let n = sizeofArray trAs
180277 mutArr <- thawArray trAs 0 n
181− a <- toAny . fun . fromAny <$> readArray mutArr i
182− writeArray mutArr i a
278+ writeArray mutArr i v
183279 unsafeFreezeArray mutArr
184−{-# INLINE adjust #-}
280+{-# INLINE alter #-}
185281
186282{- | Map over the elements of a 'TypeRepMap'.
187283
@@ -215,30 +311,89 @@ hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
215311 withTr t = withTypeable t f
216312{-# INLINE hoistWithKey #-}
217313
218−-- | The union of two 'TypeRepMap's using a combining function.
219−unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
220−unionWith f m1 m2 = fromTriples
221− $ toTripleList
222− $ Map.unionWith combine
223− (fromTripleList $ toTriples m1)
224− (fromTripleList $ toTriples m2)
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)
225318 where
226− combine :: (Any, Any) -> (Any, Any) -> (Any, Any)
227− combine (av, ak) (bv, _) = (toAny $ f (fromAny av) (fromAny bv), ak)
228−
229− fromTripleList :: Ord a => [(a, b, c)] -> Map.Map a (b, c)
230− fromTripleList = Map.fromList . map (\(a, b, c) -> (a, (b, c)))
231−
232− toTripleList :: Map.Map a (b, c) -> [(a, b, c)]
233− toTripleList = map (\(a, (b, c)) -> (a, b, c)) . Map.toList
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
234347{-# INLINE unionWith #-}
235348
236−-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
349+-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
237350-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
238351union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
239352union = unionWith const
240353{-# INLINE union #-}
241354
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+
242397{- | Check if a value of the given type is present in a 'TypeRepMap'.
243398
244399>>> member @Char $ one (Identity 'a')
@@ -274,9 +429,22 @@ size = sizeofPrimArray . fingerprintAs
274429
275430-- | Return the list of 'SomeTypeRep' from the keys.
276431keys :: TypeRepMap f -> [SomeTypeRep]
277−keys TypeRepMap{..} = SomeTypeRep . anyToTypeRep <$> toList trKeys
432+keys = keysWith SomeTypeRep
278433{-# INLINE keys #-}
279434
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+
280448-- | Binary searched based on this article
281449-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
282450-- with modification for our two-vector search case.
@@ -320,8 +488,20 @@ typeFp = typeRepFingerprint $ typeRep @a
320488toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
321489toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
322490
323−deleteByFst :: Eq a => a -> [(a, b, c)] -> [(a, b, c)]
324−deleteByFst x = filter ((/= x) . fst3)
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)
325505
326506nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
327507nubByFst = nubBy ((==) `on` fst3)
@@ -368,19 +548,19 @@ instance IsList (TypeRepMap f) where
368548 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
369549
370550 toList :: TypeRepMap f -> [WrapTypeable f]
371− toList = map toWrapTypeable . toTriples
372− where
373− toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
374− toWrapTypeable (_, an, k) = withTypeable (unsafeCoerce k) $ fromAny an
551+ toList = toListWith WrapTypeable
375552
376553calcFp :: forall a . Typeable a => Fingerprint
377554calcFp = typeRepFingerprint $ typeRep @a
378555
379556fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
380−fromTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
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)
381561 where
382562 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
383− (fps, ans, ks) = unzip3 $ fromSortedList $ sortWith fst3 $ nubByFst kvs
563+ (fps, ans, ks) = unzip3 $ fromSortedList kvs
384564
385565----------------------------------------------------------------------------
386566-- Tree-like conversion
@@ -406,3 +586,85 @@ fromSortedList l = runST $ do
406586 newFirst <- loop (2 * i + 1) first
407587 writeArray result i (indexArray origin newFirst)
408588 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+4−2
@@ -1,4 +1,6 @@
1−resolver: nightly-2018-07-05
1+resolver: lts-15.8
22
33extra-deps:
4− - primitive-0.6.4.0
4+ - dependent-map-0.2.4.0
5+ - dependent-sum-0.5
6+ - hspec-hedgehog-0.0.1.2
test/Test.hs+21−1
@@ -1 +1,21 @@
1−{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display #-}
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
test/Test/TypeRep/CMap.hs+9−7
@@ -1,16 +1,18 @@
1−module Test.TypeRep.CMap where
1+module Test.TypeRep.CMap
2+ ( cMapSpec
3+ ) where
24
35import Prelude hiding (lookup)
46
57import Data.Functor.Identity (Identity (..))
8+import Test.Hspec (Spec, describe, it, shouldBe)
69
7−import Test.Tasty.Hspec
10+import Data.TypeRep.CMap (TypeRepMap, empty, insert, lookup, size)
811
9−import Data.TypeRep.CMap
1012
11−-- Simple test for 'lookup', 'insert' and 'size' functions.
12−spec_insertLookup :: Spec
13−spec_insertLookup = do
13+-- | Simple test for 'lookup', 'insert' and 'size' functions.
14+cMapSpec :: Spec
15+cMapSpec = describe "Containers Map TypeRep" $ do
1416 describe "Lookup Test" $ do
1517 it "returns the inserted element" $
1618 lookup (insert (Identity 'a') empty) `shouldBe` Just (Identity 'a')
@@ -35,7 +37,7 @@ mapOf10 = insert (Identity True)
3537 $ insert (Identity $ Just ())
3638 $ insert (Identity [()])
3739 $ insert (Identity ())
38− $ insert (Identity "aaa")
40+ $ insert (Identity @String "aaa")
3941 $ insert (Identity $ Just 'a')
4042 $ insert (Identity 'a')
4143 $ insert (Identity (11 :: Int)) empty
test/Test/TypeRep/CacheMap.hsdeleted−50
@@ -1,50 +0,0 @@
1−module Test.TypeRep.CacheMap where
2−
3−import Prelude hiding (lookup)
4−
5−import Data.Functor.Identity (Identity (..))
6−import GHC.Exts (fromList)
7−import Test.Tasty.Hspec (Spec, describe, it, shouldBe)
8−
9−import Data.TMap (TMap, empty, insert, lookup, one, size, union)
10−import Data.TypeRepMap.Internal (WrapTypeable (..))
11−
12−-- Simple test for 'lookup', 'insert' and 'size' functions.
13−spec_insertLookup :: Spec
14−spec_insertLookup = do
15− describe "Lookup Test" $ do
16− it "returns the inserted element" $
17− lookup (fromList [WrapTypeable $ Identity 'a']) `shouldBe` Just 'a'
18− it "returns the second inserted value of the same type" $
19− lookup (fromList [WrapTypeable (Identity 'b'), WrapTypeable (Identity 'a')]) `shouldBe` Just 'b'
20−
21− describe "Size Test" $ do
22− it "is empty" $
23− size empty `shouldBe` 0
24− it "is of size 1 when 1 element inserted" $
25− size (one 'a') `shouldBe` 1
26− it "doesn't increase size when element of the same type is added" $
27− size (insert 'b' $ insert 'a' empty) `shouldBe` 1
28− it "returns 10 when 10 different types are inserted" $
29− size mapOf10 `shouldBe` 10
30−
31− describe "Union test" $ do
32− let m = fromList [WrapTypeable $ Identity 'a', WrapTypeable $ Identity True] `union`
33− fromList [WrapTypeable $ Identity 'b']
34− it "lookup works on union as expected" $ do
35− lookup m `shouldBe` Just 'a'
36− lookup m `shouldBe` Just True
37− lookup @Int m `shouldBe` Nothing
38−
39−
40−mapOf10 :: TMap
41−mapOf10 = insert True
42− $ insert [True, False]
43− $ insert (Just True)
44− $ insert (Just ())
45− $ insert [()]
46− $ insert ()
47− $ insert "aaa"
48− $ insert (Just 'a')
49− $ insert 'a'
50− $ insert (11 :: Int) empty
test/Test/TypeRep/MapProperty.hsdeleted−110
@@ -1,110 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE KindSignatures #-}
4−{-# LANGUAGE PolyKinds #-}
5−
6−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
7−
8−module Test.TypeRep.MapProperty where
9−
10−import Prelude hiding (lookup)
11−
12−import Data.Proxy (Proxy (..))
13−import Data.Semigroup (Semigroup (..))
14−import GHC.Exts (fromList)
15−import GHC.Stack (HasCallStack)
16−import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
17−import Hedgehog (MonadGen, PropertyT, forAll, property, (===))
18−import Test.Tasty (TestName, TestTree)
19−import Test.Tasty.Hedgehog (testProperty)
20−
21−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, lookup, member)
22−
23−import qualified Hedgehog.Gen as Gen
24−import qualified Hedgehog.Range as Range
25−
26−----------------------------------------------------------------------------
27−-- Common utils
28−----------------------------------------------------------------------------
29−
30−type PropertyTest = [TestTree]
31−
32−prop :: HasCallStack => TestName -> PropertyT IO () -> PropertyTest
33−prop testName = pure . testProperty testName . property
34−
35−----------------------------------------------------------------------------
36−-- Map modification properties
37−----------------------------------------------------------------------------
38−
39−test_InsertLookup :: PropertyTest
40−test_InsertLookup = prop "lookup k (insert k v m) == Just v" $ do
41− m <- forAll genMap
42− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
43−
44− lookup @n @IntProxy (insert proxy m) === Just proxy
45−
46−test_InsertInsert :: PropertyTest
47−test_InsertInsert = prop "insert k b . insert k a == insert k b" $ do
48− m <- forAll genMap
49− WrapTypeable a@(IntProxy (proxy :: Proxy n) i) <- forAll genTF
50− let b = IntProxy proxy (i + 1)
51− lookup @n @IntProxy (insert b $ insert a m) === Just b
52−
53−test_DeleteMember :: PropertyTest
54−test_DeleteMember = prop "member k . delete k == False" $ do
55− m <- forAll genMap
56− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
57− shouldInsert <- forAll Gen.bool
58−
59− if shouldInsert then
60− member @n (delete @n $ insert proxy m) === False
61− else
62− member @n (delete @n m) === False
63−
64−----------------------------------------------------------------------------
65−-- Semigroup and Monoid laws
66−----------------------------------------------------------------------------
67−
68−-- This newtype is used to compare 'TypeRepMap's using only 'Fingerprint's. It's
69−-- not a good idea to write such `Eq` instance for `TypeRepMap` itself because
70−-- it doesn't compare values so it's not true equality. But this should be
71−-- enough for tests.
72−newtype FpMap f = FpMap (TypeRepMap f)
73− deriving (Show, Semigroup, Monoid)
74−
75−instance Eq (FpMap f) where
76− FpMap (TypeRepMap as1 bs1 _ _) == FpMap (TypeRepMap as2 bs2 _ _) =
77− as1 == as2 && bs1 == bs2
78−
79−test_SemigroupAssoc :: PropertyTest
80−test_SemigroupAssoc = prop "x <> (y <> z) == (x <> y) <> z" $ do
81− x <- FpMap <$> forAll genMap
82− y <- FpMap <$> forAll genMap
83− z <- FpMap <$> forAll genMap
84−
85− (x <> (y <> z)) === ((x <> y) <> z)
86−
87−test_MonoidIdentity :: PropertyTest
88−test_MonoidIdentity = prop "x <> mempty == mempty <> x == x" $ do
89− x <- FpMap <$> forAll genMap
90−
91− x <> mempty === x
92− mempty <> x === x
93−
94−----------------------------------------------------------------------------
95−-- Generators
96−----------------------------------------------------------------------------
97−
98−data IntProxy (n :: Nat) = IntProxy (Proxy n) Int
99− deriving (Show, Eq)
100−
101−genMap :: MonadGen m => m (TypeRepMap IntProxy)
102−genMap = fromList <$> Gen.list (Range.linear 0 1000) genTF
103−
104−genTF :: MonadGen m => m (WrapTypeable IntProxy)
105−genTF = do
106− randNat :: Integer <- Gen.integral (Range.linear 0 10000)
107− randInt <- Gen.int Range.constantBounded
108− case someNatVal randNat of
109− Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
110− Nothing -> error "Invalid test generator"
test/Test/TypeRep/TypeRepMap.hsnew file+111
@@ -0,0 +1,111 @@
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.hsnew file+184
@@ -0,0 +1,184 @@
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+8−6
@@ -1,16 +1,18 @@
1−module Test.TypeRep.Vector where
1+module Test.TypeRep.Vector
2+ ( vectorSpec
3+ ) where
24
35import Prelude hiding (lookup)
46
57import Data.Functor.Identity (Identity (..))
8+import Test.Hspec (Spec, describe, it, shouldBe)
69
7−import Test.Tasty.Hspec
10+import Data.TypeRep.Vector (TF (..), fromList, lookup)
811
9−import Data.TypeRep.Vector
1012
11−-- Simple test for 'lookup', 'insert' and 'size' functions.
12−spec_insertLookup :: Spec
13−spec_insertLookup =
13+-- | Simple test for 'lookup', 'insert' and 'size' functions.
14+vectorSpec :: Spec
15+vectorSpec = describe "Vector TypeRep" $
1416 describe "Lookup Test" $ do
1517 it "returns the inserted element" $
1618 lookup (fromList [TF (Identity 'a')]) `shouldBe` Just (Identity 'a')
test/Test/TypeRep/VectorOpt.hs+8−6
@@ -1,16 +1,18 @@
1−module Test.TypeRep.VectorOpt where
1+module Test.TypeRep.VectorOpt
2+ ( optimalVectorSpec
3+ ) where
24
35import Prelude hiding (lookup)
46
57import Data.Functor.Identity (Identity (..))
6−
7−import Test.Tasty.Hspec
8+import Test.Hspec (Spec, describe, it, shouldBe)
89
910import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
1011
11−-- Simple test for 'lookup', 'insert' and 'size' functions.
12−spec_insertLookup :: Spec
13−spec_insertLookup =
12+
13+-- | Simple test for 'lookup', 'insert' and 'size' functions.
14+optimalVectorSpec :: Spec
15+optimalVectorSpec = describe "Optimal Vector TypeRep" $
1416 describe "Lookup Test" $ do
1517 it "returns the inserted element" $
1618 lookup (fromList [TF $ Identity 'a']) `shouldBe` Just (Identity 'a')
typerep-extra-impls/Data/TypeRep/CMap.hs+16−3
@@ -1,5 +1,12 @@
1−{-# LANGUAGE KindSignatures #-}
2−{-# LANGUAGE PolyKinds #-}
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+-}
310
411module Data.TypeRep.CMap
512 ( TypeRepMap (..)
@@ -12,6 +19,8 @@ module Data.TypeRep.CMap
1219
1320import Prelude hiding (lookup)
1421
22+import Control.DeepSeq
23+import Data.Kind (Type)
1524import Data.Proxy (Proxy (..))
1625import Data.Typeable (TypeRep, Typeable, typeRep)
1726import GHC.Base (Any)
@@ -19,11 +28,15 @@ import Unsafe.Coerce (unsafeCoerce)
1928
2029import qualified Data.Map.Lazy as LMap
2130
31+
2232-- | Map-like data structure with types served as the keys.
23−newtype TypeRepMap (f :: k -> *) = TypeRepMap
33+newtype TypeRepMap (f :: k -> Type) = TypeRepMap
2434 { unMap :: LMap.Map TypeRep Any
2535 }
2636
37+instance NFData (TypeRepMap f) where
38+ rnf x = rnf (keys x) `seq` ()
39+
2740-- | Empty structure.
2841empty :: TypeRepMap f
2942empty = TypeRepMap mempty
typerep-extra-impls/Data/TypeRep/OptimalVector.hs+20−8
@@ -1,9 +1,16 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE KindSignatures #-}
4−{-# LANGUAGE MagicHash #-}
5−{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE TypeFamilies #-}
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+-}
714
815module Data.TypeRep.OptimalVector
916 ( -- * Map type
@@ -23,6 +30,7 @@ module Data.TypeRep.OptimalVector
2330import Prelude hiding (lookup)
2431
2532import Control.Arrow ((&&&))
33+import Control.DeepSeq
2634import Data.Kind (Type)
2735import Data.Proxy (Proxy (..))
2836import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
@@ -35,12 +43,16 @@ import Unsafe.Coerce (unsafeCoerce)
3543import qualified Data.Vector as V
3644import qualified Data.Vector.Unboxed as Unboxed
3745
46+
3847data TypeRepMap (f :: k -> Type) = TypeRepMap
3948 { fingerprintAs :: Unboxed.Vector Word64
4049 , fingerprintBs :: Unboxed.Vector Word64
4150 , anys :: V.Vector Any
4251 }
4352
53+instance NFData (TypeRepMap f) where
54+ rnf x = x `seq` ()
55+
4456fromAny :: Any -> f a
4557fromAny = unsafeCoerce
4658
@@ -49,7 +61,7 @@ empty :: TypeRepMap f
4961empty = TypeRepMap mempty mempty mempty
5062
5163-- | Inserts the value with its type as a key.
52−insert :: forall a f . Typeable a => a -> TypeRepMap f -> TypeRepMap f
64+insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
5365insert = undefined
5466
5567-- | Looks up the value at the type.
@@ -99,7 +111,7 @@ binarySearch (Fingerprint a b) fpAs fpBs =
99111data TF f where
100112 TF :: Typeable a => f a -> TF f
101113
102−fromF :: Typeable a => f a -> Proxy a
114+fromF :: f a -> Proxy a
103115fromF _ = Proxy
104116
105117fromList :: forall f . [TF f] -> TypeRepMap f
typerep-extra-impls/Data/TypeRep/Vector.hs+11−2
@@ -7,6 +7,14 @@
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+
1018module Data.TypeRep.Vector
1119 ( TypeRepVector (..)
1220 , TF (..)
@@ -33,6 +41,7 @@ import qualified Data.Vector.Generic as G
3341import qualified Data.Vector.Generic.Mutable as M
3442import qualified Data.Vector.Unboxed as Unboxed
3543
44+
3645data instance Unboxed.MVector s Fingerprint = MFingerprintVector (Unboxed.MVector s Word64) (Unboxed.MVector s Word64)
3746data instance Unboxed.Vector Fingerprint = FingerprintVector (Unboxed.Vector Word64) (Unboxed.Vector Word64)
3847
@@ -135,7 +144,7 @@ empty :: TypeRepVector f
135144empty = TypeRepVect mempty mempty
136145
137146-- | Inserts the value with its type as a key.
138−insert :: forall a f . Typeable a => a -> TypeRepVector f -> TypeRepVector f
147+insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
139148insert = undefined
140149
141150-- | Looks up the value at the type.
@@ -155,7 +164,7 @@ size = Unboxed.length . fingerprints
155164data TF f where
156165 TF :: Typeable a => f a -> TF f
157166
158−fromF :: Typeable a => f a -> Proxy a
167+fromF :: f a -> Proxy a
159168fromF _ = Proxy
160169
161170fromList :: forall f . [TF f] -> TypeRepVector f
typerep-map.cabal+67−53
@@ -1,5 +1,6 @@
1+cabal-version: 2.4
12name: typerep-map
2−version: 0.3.0
3+version: 0.3.3.0
34synopsis: Efficient implementation of a dependent map with types as keys
45description:
56 A dependent map from type representations to values of these types.
@@ -18,102 +19,115 @@ description:
1819
1920homepage: https://github.com/kowainik/typerep-map
2021bug-reports: https://github.com/kowainik/typerep-map/issues
21−license: MIT
22+license: MPL-2.0
2223license-file: LICENSE
23−author: Kowainik, Vladislav Zavialov
24−maintainer: xrom.xkov@gmail.com
25−copyright: 2017-2018 Kowainik
24+author: Veronika Romashkina, Vladislav Zavialov, Dmitrii Kovanikov
25+maintainer: Kowainik <xrom.xkov@gmail.com>
26+copyright: 2017-2020 Kowainik
2627category: Data, Data Structures, Types
2728build-type: Simple
2829extra-doc-files: README.md
2930 , CHANGELOG.md
30−cabal-version: 2.0
3131tested-with: GHC == 8.2.2
32− , GHC == 8.4.3
32+ , GHC == 8.4.4
33+ , GHC == 8.6.5
34+ , GHC == 8.8.3
35+ , GHC == 8.10.1
3336
3437source-repository head
3538 type: git
3639 location: https://github.com/kowainik/typerep-map.git
3740
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
48+ RecordWildCards
49+ ScopedTypeVariables
50+ TypeApplications
51+ ghc-options: -Wall
52+ -Wcompat
53+ -Widentities
54+ -Wincomplete-uni-patterns
55+ -Wincomplete-record-updates
56+ if impl(ghc >= 8.0)
57+ ghc-options: -Wredundant-constraints
58+ if impl(ghc >= 8.2)
59+ ghc-options: -fhide-source-paths
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+
3869library
70+ import: common-options
3971 hs-source-dirs: src
4072 exposed-modules: Data.TMap
4173 Data.TypeRepMap
4274 Data.TypeRepMap.Internal
43− ghc-options: -Wall
44− build-depends: base >= 4.10 && < 5
45− , containers >= 0.5.10.2 && < 0.7
46− , ghc-prim ^>= 0.5.1.1
47− , primitive ^>= 0.6.4
48− default-extensions: OverloadedStrings
49− RecordWildCards
50− ScopedTypeVariables
51− TypeApplications
52− default-language: Haskell2010
75+
76+ build-depends: ghc-prim >= 0.5.1.1 && < 0.7
77+ , primitive ^>= 0.7.0
78+ , deepseq ^>= 1.4
5379
5480library typerep-extra-impls
81+ import: common-options
5582 hs-source-dirs: typerep-extra-impls
5683 exposed-modules: Data.TypeRep.CMap
5784 Data.TypeRep.OptimalVector
5885 Data.TypeRep.Vector
59− ghc-options: -Wall
60− build-depends: base >= 4.10 && < 5
61− , containers >= 0.5.10.2 && < 0.7
62− , vector ^>= 0.12.0.1
63− default-extensions: OverloadedStrings
64− RecordWildCards
65− ScopedTypeVariables
66− TypeApplications
6786
68− default-language: Haskell2010
87+ build-depends: containers >= 0.5.10.2 && < 0.7
88+ , vector ^>= 0.12.0.1
89+ , deepseq ^>= 1.4
6990
7091test-suite typerep-map-test
92+ import: common-options
7193 type: exitcode-stdio-1.0
7294 hs-source-dirs: test
95+
7396 main-is: Test.hs
7497 other-modules: Test.TypeRep.CMap
75− , Test.TypeRep.CacheMap
76− , Test.TypeRep.MapProperty
98+ , Test.TypeRep.TypeRepMap
99+ , Test.TypeRep.TypeRepMapProperty
77100 , Test.TypeRep.Vector
78101 , Test.TypeRep.VectorOpt
79− build-depends: base >= 4.10 && < 5
80− , ghc-typelits-knownnat >= 0.4.2 && < 0.7
81− , hedgehog >= 0.5.3 && < 0.7
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
82107 , typerep-map
83108 , typerep-extra-impls
84− , tasty >= 1.0.1.1 && < 1.2
85− , tasty-discover >= 4.1.1 && < 4.3
86− , tasty-hedgehog >= 0.1.0.2 && < 0.3
87− , tasty-hspec ^>= 1.1.5
88− , QuickCheck >= 2.11.3 && < 2.13
89− build-tool-depends: tasty-discover:tasty-discover
90− ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
91− default-extensions: ScopedTypeVariables
92− TypeApplications
93109
94− default-language: Haskell2010
110+ ghc-options: -threaded -rtsopts -with-rtsopts=-N
95111
96112benchmark typerep-map-benchmark
113+ import: common-options
97114 type: exitcode-stdio-1.0
98− default-language: Haskell2010
99− ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
100115 hs-source-dirs: benchmark
116+
101117 main-is: Main.hs
102118 other-modules: CMap
103119 , CacheMap
120+ , DMap
121+ , Spec
104122 , Vector
105123 , OptimalVector
106− build-depends: base >= 4.10 && < 5
107− , criterion >= 1.4.1.0 && < 1.6
124+
125+ build-depends: criterion >= 1.4.1.0 && < 1.6
108126 , deepseq ^>= 1.4.3.0
109127 , dependent-map >= 0.2.4.0 && < 0.5
110− , dependent-sum ^>= 0.4
111− , ghc-typelits-knownnat >= 0.4.2 && < 0.7
128+ , dependent-sum >= 0.5 && < 0.8
129+ , ghc-typelits-knownnat >= 0.4.2 && < 0.8
112130 , typerep-map
113131 , typerep-extra-impls
114− default-extensions: OverloadedStrings
115− RecordWildCards
116− ScopedTypeVariables
117− TypeApplications
118− if impl(ghc >= 8.2.2)
119− other-modules: DMap
132+
133+ ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0