on 0abe416 help sign in
33 files changed+663−1868
since splitsnapshots
unifiedsplit
.github/CODEOWNERS−1 .github/workflows/ci.yml−47 .gitignore−1 .stylish-haskell.yaml−59 .travis.yml+55−8 CHANGELOG.md+6−40 LICENSE+21−373 README.md+7−18 appveyor.yml−35 benchmark/CMap.hs+33−53 benchmark/CacheMap.hs+29−55 benchmark/DMap.hs+53−56 benchmark/Main.hs+20−41 benchmark/OptimalVector.hs+36−34 benchmark/Spec.hs−64 benchmark/Vector.hs+22−23 shell.nix+10 src/Data/TMap.hs+6−69 src/Data/TypeRepMap.hs+40−52 src/Data/TypeRepMap/Internal.hs+76−378 stack.yaml+2−4 test/Test.hs+1−21 test/Test/TypeRep/CMap.hs+7−9 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+6−8 test/Test/TypeRep/VectorOpt.hs+6−8 typerep-extra-impls/Data/TypeRep/CMap.hs+3−16 typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−20 typerep-extra-impls/Data/TypeRep/Vector.hs+2−11 typerep-map.cabal+54−69
.github/CODEOWNERSdeleted−1
@@ -1 +0,0 @@
1−* @chshersh @vrom911
.github/workflows/ci.ymldeleted−47
@@ -1,47 +0,0 @@
1−name: CI
2−
3−# Trigger the workflow on push or pull request, but only for the main branch
4−on:
5− push:
6− branches:
7− - main
8− pull_request:
9−
10−jobs:
11− build:
12− name: ghc ${{ matrix.ghc }}
13− runs-on: ubuntu-16.04
14− strategy:
15− matrix:
16− cabal: ["3.0"]
17− ghc:
18− - "8.2.2"
19− - "8.4.4"
20− - "8.6.5"
21− - "8.8.3"
22− - "8.10.1"
23−
24− steps:
25− - uses: actions/checkout@v2
26− if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/main'
27−
28− - uses: actions/setup-haskell@v1
29− name: Setup Haskell
30− with:
31− ghc-version: ${{ matrix.ghc }}
32− cabal-version: ${{ matrix.cabal }}
33−
34− - uses: actions/cache@v1
35− name: Cache ~/.cabal/store
36− with:
37− path: ~/.cabal/store
38− key: ${{ runner.os }}-${{ matrix.ghc }}-cabal
39−
40− - name: Build
41− run: |
42− cabal update
43− cabal build --enable-tests --enable-benchmarks all
44−
45− - name: Test
46− run: |
47− cabal test --enable-tests --test-show-details=direct all
.gitignore−1
@@ -24,7 +24,6 @@ cabal.project.local
2424
2525# Stack
2626.stack-work/
27−stack.yaml.lock
2827
2928### IDE/support
3029# Vim
.stylish-haskell.yamldeleted−59
@@ -1,59 +0,0 @@
1−steps:
2− - simple_align:
3− cases: true
4− top_level_patterns: true
5− records: true
6−
7− # Import cleanup
8− - imports:
9− align: none
10− list_align: after_alias
11− pad_module_names: false
12− long_list_align: inline
13− empty_list_align: inherit
14− list_padding: 4
15− separate_lists: true
16− space_surround: false
17−
18− - language_pragmas:
19− style: vertical
20− remove_redundant: true
21−
22− # Remove trailing whitespace
23− - trailing_whitespace: {}
24−
25−columns: 100
26−
27−newline: native
28−
29−language_extensions:
30− - BangPatterns
31− - ConstraintKinds
32− - DataKinds
33− - DefaultSignatures
34− - DeriveAnyClass
35− - DeriveDataTypeable
36− - DeriveGeneric
37− - DerivingStrategies
38− - FlexibleContexts
39− - FlexibleInstances
40− - FunctionalDependencies
41− - GADTs
42− - GeneralizedNewtypeDeriving
43− - InstanceSigs
44− - KindSignatures
45− - LambdaCase
46− - MultiParamTypeClasses
47− - MultiWayIf
48− - NamedFieldPuns
49− - NoImplicitPrelude
50− - OverloadedStrings
51− - QuasiQuotes
52− - RecordWildCards
53− - ScopedTypeVariables
54− - StandaloneDeriving
55− - TemplateHaskell
56− - TupleSections
57− - TypeApplications
58− - TypeFamilies
59− - ViewPatterns
.travis.yml+55−8
@@ -6,23 +6,70 @@ git:
66
77cache:
88 directories:
9+ - "$HOME/.cabal"
10+ - "$HOME/.ghc"
911 - "$HOME/.stack"
12+ - "$HOME/build/kowainik/typerep-map/.stack-work"
1013
1114matrix:
15+
1216 include:
13− - env: GHC='8.8.3'
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.4.3
41+ env: STACK_YAML="$HOME/build/kowainik/typerep-map/stack.yaml"
1442 os: linux
15− - env: GHC='8.8.3'
16− os: osx
43+ addons:
44+ apt:
45+ packages:
46+ - libgmp-dev
1747
1848install:
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
49+ - |
50+ if [ -z "$STACK_YAML" ]; then
51+ export PATH="/opt/ghc/$GHCVER/bin:/opt/cabal/$CABALVER/bin:$PATH"
52+ echo $PATH
53+ cabal new-update
54+ cabal new-build --enable-tests --enable-benchmarks
55+ else
56+ echo "$TRAVIS_BUILD_DIR"
57+ mkdir -p ~/.local/bin
58+ export PATH="$HOME/.local/bin:$PATH"
59+ travis_retry curl -L 'https://www.stackage.org/stack/linux-x86_64' | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
60+ stack --version
61+ stack setup --no-terminal --install-cabal 2.0.1.0
62+ stack ghc -- --version
63+ stack build --only-dependencies --no-terminal
64+ fi
2365
2466script:
25− - stack test --no-terminal
67+ - |
68+ if [ -z "$STACK_YAML" ]; then
69+ cabal new-test $ALLOWNEWER
70+ else
71+ stack build --test --bench --no-terminal
72+ fi
2673
2774notifications:
2875 email: false
CHANGELOG.md+6−40
@@ -1,44 +1,10 @@
1−# Changelog
1+Change log
2+==========
23
34`typerep-map` uses [PVP Versioning][1].
4−The changelog is available [on GitHub][2].
5+The change log is available [on GitHub][2].
56
6−## 0.3.3.0 — Apr 18, 2020
7−
8−* [#83](https://github.com/kowainik/typerep-map/issues/83):
9− Support GHC-8.10.
10−* [#80](https://github.com/kowainik/typerep-map/issues/80):
11− Build on 32-bit platforms.
12−* [#78](https://github.com/kowainik/typerep-map/issues/78):
13− Support GHC-8.8.3.
14−* [#63](https://github.com/kowainik/typerep-map/pull/63):
15− Add invariant check.
16−
17−## 0.3.2 — Mar 27, 2019
18−
19−* [#47](https://github.com/kowainik/typerep-map/issues/47):
20− Add `Eq` instance for `TypeRepMap` using `-XQuantifiedConstraints`.
21−* [#70](https://github.com/kowainik/typerep-map/issues/70):
22− Bump up to `dependent-sum-0.5`.
23−
24−## 0.3.1
25−
26−* [#64](https://github.com/kowainik/typerep-map/issues/64):
27− Fix segfault in `toList`.
28−* Support GHC 8.4.4 and 8.6.3.
29−
30−## 0.3.0
31−
32−* [#46](https://github.com/kowainik/typerep-map/issues/46):
33− Make `Show` instance for `TypeRepMap` show keys.
34− Add `keys` function.
35−* [#48](https://github.com/kowainik/typerep-map/issues/48):
36− Add `adjust` function for `TypeRepMap` and `TMap`.
37−* [#30](https://github.com/kowainik/typerep-map/issues/30):
38− Rewrite `fromSortedList` to use `Array` and `MutableArray`
39− instead of `IntMap`.
40−
41−## 0.2.0
7+# 0.2.0
428
439* [#43](https://github.com/kowainik/typerep-map/issues/43):
4410 Implement `IsList` instance for `TypeRepMap`.
@@ -48,9 +14,9 @@ The changelog is available [on GitHub][2].
4814 Add `map` function for `TMap`.
4915* Drop support for `ghc-8.0.2`.
5016
51−## 0.1.0
17+# 0.1.0
5218
5319* Initially created.
5420
5521[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
22+[2]: https://github.com/kowainik/typerep-map/blob/master/CHANGELOG.md
LICENSE+21−373
@@ -1,373 +1,21 @@
1−Mozilla Public License Version 2.0
2−==================================
3−
4−1. Definitions
5−--------------
6−
7−1.1. "Contributor"
8− means each individual or legal entity that creates, contributes to
9− the creation of, or owns Covered Software.
10−
11−1.2. "Contributor Version"
12− means the combination of the Contributions of others (if any) used
13− by a Contributor and that particular Contributor's Contribution.
14−
15−1.3. "Contribution"
16− means Covered Software of a particular Contributor.
17−
18−1.4. "Covered Software"
19− means Source Code Form to which the initial Contributor has attached
20− the notice in Exhibit A, the Executable Form of such Source Code
21− Form, and Modifications of such Source Code Form, in each case
22− including portions thereof.
23−
24−1.5. "Incompatible With Secondary Licenses"
25− means
26−
27− (a) that the initial Contributor has attached the notice described
28− in Exhibit B to the Covered Software; or
29−
30− (b) that the Covered Software was made available under the terms of
31− version 1.1 or earlier of the License, but not also under the
32− terms of a Secondary License.
33−
34−1.6. "Executable Form"
35− means any form of the work other than Source Code Form.
36−
37−1.7. "Larger Work"
38− means a work that combines Covered Software with other material, in
39− a separate file or files, that is not Covered Software.
40−
41−1.8. "License"
42− means this document.
43−
44−1.9. "Licensable"
45− means having the right to grant, to the maximum extent possible,
46− whether at the time of the initial grant or subsequently, any and
47− all of the rights conveyed by this License.
48−
49−1.10. "Modifications"
50− means any of the following:
51−
52− (a) any file in Source Code Form that results from an addition to,
53− deletion from, or modification of the contents of Covered
54− Software; or
55−
56− (b) any new file in Source Code Form that contains any Covered
57− Software.
58−
59−1.11. "Patent Claims" of a Contributor
60− means any patent claim(s), including without limitation, method,
61− process, and apparatus claims, in any patent Licensable by such
62− Contributor that would be infringed, but for the grant of the
63− License, by the making, using, selling, offering for sale, having
64− made, import, or transfer of either its Contributions or its
65− Contributor Version.
66−
67−1.12. "Secondary License"
68− means either the GNU General Public License, Version 2.0, the GNU
69− Lesser General Public License, Version 2.1, the GNU Affero General
70− Public License, Version 3.0, or any later versions of those
71− licenses.
72−
73−1.13. "Source Code Form"
74− means the form of the work preferred for making modifications.
75−
76−1.14. "You" (or "Your")
77− means an individual or a legal entity exercising rights under this
78− License. For legal entities, "You" includes any entity that
79− controls, is controlled by, or is under common control with You. For
80− purposes of this definition, "control" means (a) the power, direct
81− or indirect, to cause the direction or management of such entity,
82− whether by contract or otherwise, or (b) ownership of more than
83− fifty percent (50%) of the outstanding shares or beneficial
84− ownership of such entity.
85−
86−2. License Grants and Conditions
87−--------------------------------
88−
89−2.1. Grants
90−
91−Each Contributor hereby grants You a world-wide, royalty-free,
92−non-exclusive license:
93−
94−(a) under intellectual property rights (other than patent or trademark)
95− Licensable by such Contributor to use, reproduce, make available,
96− modify, display, perform, distribute, and otherwise exploit its
97− Contributions, either on an unmodified basis, with Modifications, or
98− as part of a Larger Work; and
99−
100−(b) under Patent Claims of such Contributor to make, use, sell, offer
101− for sale, have made, import, and otherwise transfer either its
102− Contributions or its Contributor Version.
103−
104−2.2. Effective Date
105−
106−The licenses granted in Section 2.1 with respect to any Contribution
107−become effective for each Contribution on the date the Contributor first
108−distributes such Contribution.
109−
110−2.3. Limitations on Grant Scope
111−
112−The licenses granted in this Section 2 are the only rights granted under
113−this License. No additional rights or licenses will be implied from the
114−distribution or licensing of Covered Software under this License.
115−Notwithstanding Section 2.1(b) above, no patent license is granted by a
116−Contributor:
117−
118−(a) for any code that a Contributor has removed from Covered Software;
119− or
120−
121−(b) for infringements caused by: (i) Your and any other third party's
122− modifications of Covered Software, or (ii) the combination of its
123− Contributions with other software (except as part of its Contributor
124− Version); or
125−
126−(c) under Patent Claims infringed by Covered Software in the absence of
127− its Contributions.
128−
129−This License does not grant any rights in the trademarks, service marks,
130−or logos of any Contributor (except as may be necessary to comply with
131−the notice requirements in Section 3.4).
132−
133−2.4. Subsequent Licenses
134−
135−No Contributor makes additional grants as a result of Your choice to
136−distribute the Covered Software under a subsequent version of this
137−License (see Section 10.2) or under the terms of a Secondary License (if
138−permitted under the terms of Section 3.3).
139−
140−2.5. Representation
141−
142−Each Contributor represents that the Contributor believes its
143−Contributions are its original creation(s) or it has sufficient rights
144−to grant the rights to its Contributions conveyed by this License.
145−
146−2.6. Fair Use
147−
148−This License is not intended to limit any rights You have under
149−applicable copyright doctrines of fair use, fair dealing, or other
150−equivalents.
151−
152−2.7. Conditions
153−
154−Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155−in Section 2.1.
156−
157−3. Responsibilities
158−-------------------
159−
160−3.1. Distribution of Source Form
161−
162−All distribution of Covered Software in Source Code Form, including any
163−Modifications that You create or to which You contribute, must be under
164−the terms of this License. You must inform recipients that the Source
165−Code Form of the Covered Software is governed by the terms of this
166−License, and how they can obtain a copy of this License. You may not
167−attempt to alter or restrict the recipients' rights in the Source Code
168−Form.
169−
170−3.2. Distribution of Executable Form
171−
172−If You distribute Covered Software in Executable Form then:
173−
174−(a) such Covered Software must also be made available in Source Code
175− Form, as described in Section 3.1, and You must inform recipients of
176− the Executable Form how they can obtain a copy of such Source Code
177− Form by reasonable means in a timely manner, at a charge no more
178− than the cost of distribution to the recipient; and
179−
180−(b) You may distribute such Executable Form under the terms of this
181− License, or sublicense it under different terms, provided that the
182− license for the Executable Form does not attempt to limit or alter
183− the recipients' rights in the Source Code Form under this License.
184−
185−3.3. Distribution of a Larger Work
186−
187−You may create and distribute a Larger Work under terms of Your choice,
188−provided that You also comply with the requirements of this License for
189−the Covered Software. If the Larger Work is a combination of Covered
190−Software with a work governed by one or more Secondary Licenses, and the
191−Covered Software is not Incompatible With Secondary Licenses, this
192−License permits You to additionally distribute such Covered Software
193−under the terms of such Secondary License(s), so that the recipient of
194−the Larger Work may, at their option, further distribute the Covered
195−Software under the terms of either this License or such Secondary
196−License(s).
197−
198−3.4. Notices
199−
200−You may not remove or alter the substance of any license notices
201−(including copyright notices, patent notices, disclaimers of warranty,
202−or limitations of liability) contained within the Source Code Form of
203−the Covered Software, except that You may alter any license notices to
204−the extent required to remedy known factual inaccuracies.
205−
206−3.5. Application of Additional Terms
207−
208−You may choose to offer, and to charge a fee for, warranty, support,
209−indemnity or liability obligations to one or more recipients of Covered
210−Software. However, You may do so only on Your own behalf, and not on
211−behalf of any Contributor. You must make it absolutely clear that any
212−such warranty, support, indemnity, or liability obligation is offered by
213−You alone, and You hereby agree to indemnify every Contributor for any
214−liability incurred by such Contributor as a result of warranty, support,
215−indemnity or liability terms You offer. You may include additional
216−disclaimers of warranty and limitations of liability specific to any
217−jurisdiction.
218−
219−4. Inability to Comply Due to Statute or Regulation
220−---------------------------------------------------
221−
222−If it is impossible for You to comply with any of the terms of this
223−License with respect to some or all of the Covered Software due to
224−statute, judicial order, or regulation then You must: (a) comply with
225−the terms of this License to the maximum extent possible; and (b)
226−describe the limitations and the code they affect. Such description must
227−be placed in a text file included with all distributions of the Covered
228−Software under this License. Except to the extent prohibited by statute
229−or regulation, such description must be sufficiently detailed for a
230−recipient of ordinary skill to be able to understand it.
231−
232−5. Termination
233−--------------
234−
235−5.1. The rights granted under this License will terminate automatically
236−if You fail to comply with any of its terms. However, if You become
237−compliant, then the rights granted under this License from a particular
238−Contributor are reinstated (a) provisionally, unless and until such
239−Contributor explicitly and finally terminates Your grants, and (b) on an
240−ongoing basis, if such Contributor fails to notify You of the
241−non-compliance by some reasonable means prior to 60 days after You have
242−come back into compliance. Moreover, Your grants from a particular
243−Contributor are reinstated on an ongoing basis if such Contributor
244−notifies You of the non-compliance by some reasonable means, this is the
245−first time You have received notice of non-compliance with this License
246−from such Contributor, and You become compliant prior to 30 days after
247−Your receipt of the notice.
248−
249−5.2. If You initiate litigation against any entity by asserting a patent
250−infringement claim (excluding declaratory judgment actions,
251−counter-claims, and cross-claims) alleging that a Contributor Version
252−directly or indirectly infringes any patent, then the rights granted to
253−You by any and all Contributors for the Covered Software under Section
254−2.1 of this License shall terminate.
255−
256−5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257−end user license agreements (excluding distributors and resellers) which
258−have been validly granted by You or Your distributors under this License
259−prior to termination shall survive termination.
260−
261−************************************************************************
262−* *
263−* 6. Disclaimer of Warranty *
264−* ------------------------- *
265−* *
266−* Covered Software is provided under this License on an "as is" *
267−* basis, without warranty of any kind, either expressed, implied, or *
268−* statutory, including, without limitation, warranties that the *
269−* Covered Software is free of defects, merchantable, fit for a *
270−* particular purpose or non-infringing. The entire risk as to the *
271−* quality and performance of the Covered Software is with You. *
272−* Should any Covered Software prove defective in any respect, You *
273−* (not any Contributor) assume the cost of any necessary servicing, *
274−* repair, or correction. This disclaimer of warranty constitutes an *
275−* essential part of this License. No use of any Covered Software is *
276−* authorized under this License except under this disclaimer. *
277−* *
278−************************************************************************
279−
280−************************************************************************
281−* *
282−* 7. Limitation of Liability *
283−* -------------------------- *
284−* *
285−* Under no circumstances and under no legal theory, whether tort *
286−* (including negligence), contract, or otherwise, shall any *
287−* Contributor, or anyone who distributes Covered Software as *
288−* permitted above, be liable to You for any direct, indirect, *
289−* special, incidental, or consequential damages of any character *
290−* including, without limitation, damages for lost profits, loss of *
291−* goodwill, work stoppage, computer failure or malfunction, or any *
292−* and all other commercial damages or losses, even if such party *
293−* shall have been informed of the possibility of such damages. This *
294−* limitation of liability shall not apply to liability for death or *
295−* personal injury resulting from such party's negligence to the *
296−* extent applicable law prohibits such limitation. Some *
297−* jurisdictions do not allow the exclusion or limitation of *
298−* incidental or consequential damages, so this exclusion and *
299−* limitation may not apply to You. *
300−* *
301−************************************************************************
302−
303−8. Litigation
304−-------------
305−
306−Any litigation relating to this License may be brought only in the
307−courts of a jurisdiction where the defendant maintains its principal
308−place of business and such litigation shall be governed by laws of that
309−jurisdiction, without reference to its conflict-of-law provisions.
310−Nothing in this Section shall prevent a party's ability to bring
311−cross-claims or counter-claims.
312−
313−9. Miscellaneous
314−----------------
315−
316−This License represents the complete agreement concerning the subject
317−matter hereof. If any provision of this License is held to be
318−unenforceable, such provision shall be reformed only to the extent
319−necessary to make it enforceable. Any law or regulation which provides
320−that the language of a contract shall be construed against the drafter
321−shall not be used to construe this License against a Contributor.
322−
323−10. Versions of the License
324−---------------------------
325−
326−10.1. New Versions
327−
328−Mozilla Foundation is the license steward. Except as provided in Section
329−10.3, no one other than the license steward has the right to modify or
330−publish new versions of this License. Each version will be given a
331−distinguishing version number.
332−
333−10.2. Effect of New Versions
334−
335−You may distribute the Covered Software under the terms of the version
336−of the License under which You originally received the Covered Software,
337−or under the terms of any subsequent version published by the license
338−steward.
339−
340−10.3. Modified Versions
341−
342−If you create software not governed by this License, and you want to
343−create a new license for such software, you may create and use a
344−modified version of this License if you rename the license and remove
345−any references to the name of the license steward (except to note that
346−such modified license differs from this License).
347−
348−10.4. Distributing Source Code Form that is Incompatible With Secondary
349−Licenses
350−
351−If You choose to distribute Source Code Form that is Incompatible With
352−Secondary Licenses under the terms of this version of the License, the
353−notice described in Exhibit B of this License must be attached.
354−
355−Exhibit A - Source Code Form License Notice
356−-------------------------------------------
357−
358− This Source Code Form is subject to the terms of the Mozilla Public
359− License, v. 2.0. If a copy of the MPL was not distributed with this
360− file, You can obtain one at http://mozilla.org/MPL/2.0/.
361−
362−If it is not possible or desirable to put the notice in a particular
363−file, then You may include the notice in a location (such as a LICENSE
364−file in a relevant directory) where a recipient would be likely to look
365−for such a notice.
366−
367−You may add additional accurate notices of copyright ownership.
368−
369−Exhibit B - "Incompatible With Secondary Licenses" Notice
370−---------------------------------------------------------
371−
372− This Source Code Form is "Incompatible With Secondary Licenses", as
373− defined by the Mozilla Public License, v. 2.0.
1+MIT License
2+
3+Copyright (c) 2017 Veronika Romashkina
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
README.md+7−18
@@ -1,22 +1,11 @@
11# typerep-map
22
3−![logo](https://user-images.githubusercontent.com/8126674/44323413-788dd700-a484-11e8-842e-f224cfaa4206.png)
4−
5−[![GitHub CI](https://github.com/kowainik/typerep-map/workflows/CI/badge.svg)](https://github.com/kowainik/typerep-map/actions)
6−[![Build status](https://img.shields.io/travis/kowainik/typerep-map.svg?logo=travis)](https://travis-ci.org/kowainik/typerep-map)
7−[![Windows build status](https://ci.appveyor.com/api/projects/status/github/kowainik/typerep-map?branch=main&svg=true)](https://ci.appveyor.com/project/kowainik/typerep-map)
8−[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg?logo=haskell)](https://hackage.haskell.org/package/typerep-map)
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−
3+[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg)](https://hackage.haskell.org/package/typerep-map)
4+[![Build status](https://secure.travis-ci.org/kowainik/typerep-map.svg)](https://travis-ci.org/kowainik/typerep-map)
5+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
136
147`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.
158
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)
19−
209## Usage example
2110
2211```haskell
@@ -55,10 +44,10 @@ False
5544Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
5645on structure with size `10^4`:
5746
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 |
47+| | ghc-8.2.2 | ghc-8.4.3 |
48+|----------------|-----------|-----------|
49+| `DMap TypeRep` | 517.5 ns | 779.9 ns |
50+| `typerep-map` | 205.3 ns | 187.2 ns |
6251
6352 ghc-8.2.2 | ghc-8.4.3
6453:---------:|:-----------:
appveyor.ymldeleted−35
@@ -1,35 +0,0 @@
1−clone_folder: "c:\\WORK"
2−clone_depth: 5
3−
4−# Do not build feature branch with open Pull Requests
5−skip_branch_with_pr: true
6−
7−platform:
8− - x86
9− - x86_64
10−
11−cache:
12− - "C:\\SR"
13− - dist-newstyle
14−
15−environment:
16− global:
17− CABOPTS: --store-dir=C:\\SR
18−
19− matrix:
20− - GHCVER: 8.8.3
21−
22−install:
23− - choco source add -n mistuke -s https://www.myget.org/F/mistuke/api/v2
24− - choco install -y cabal --version 3.0.0.0
25− - choco install -y ghc --version 8.8.3
26− - refreshenv
27−
28−before_build:
29− - cabal --version
30− - ghc --version
31− - cabal %CABOPTS% update
32−
33−build_script:
34− - cabal %CABOPTS% build --enable-tests --write-ghc-environment-files=always
35− - cabal %CABOPTS% test --enable-tests
benchmark/CMap.hs+33−53
@@ -1,77 +1,57 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE TypeFamilies #-}
66{-# LANGUAGE TypeOperators #-}
77{-# LANGUAGE UndecidableInstances #-}
88
9+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10+
911module CMap
10− ( spec
11− ) where
12+ ( benchMap
13+ , prepareBenchMap
14+ ) where
15+
16+import Criterion.Main (Benchmark, bench, bgroup, nf)
1217
1318import Prelude hiding (lookup)
1419
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
20+import Control.DeepSeq (rnf)
21+import Control.Exception
1722import Data.Maybe (fromJust)
1823import Data.Proxy (Proxy (..))
1924import Data.Typeable (Typeable)
20−import GHC.TypeLits (type (+), KnownNat, Nat)
25+import GHC.TypeLits
2126
22−import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
27+import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, keys, lookup)
2328
24−import Spec (BenchSpec (..))
29+benchMap :: Benchmark
30+benchMap = bgroup "map"
31+ [ bench "lookup" $ nf tenLookups bigMap
32+ --, bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
33+ --, bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
34+ ]
2535
26−
27−spec :: BenchSpec
28−spec = BenchSpec
29− { benchLookup = Just $ \name ->
30− env (mkMap 10000) $ \ ~bigMap ->
31− bench name $ nf tenLookups bigMap
32− , benchInsertSmall = Just $ \name ->
33− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
34− , benchInsertBig = Just $ \name ->
35− env (mkMap 10000) $ \ ~bigMap ->
36− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
37− , benchUpdateSmall = Just $ \name ->
38− env (mkMap 10) $ \ ~smallMap ->
39− bench name $ whnf (inserts smallMap 10) (Proxy @ 0)
40− , benchUpdateBig = Just $ \name ->
41− env (mkMap 10000) $ \ ~bigMap ->
42− bench name $ whnf (inserts bigMap 10) (Proxy @ 0)
43− }
44−
45−tenLookups
46− :: TypeRepMap (Proxy :: Nat -> Type)
47− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
48− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
49− )
36+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
37+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
38+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
39+ )
5040tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5141 where
5242 lp :: forall (a::Nat). Typeable a => Proxy a
5343 lp = fromJust $ lookup tmap
5444
55−inserts
56− :: forall a . (KnownNat a)
57− => TypeRepMap (Proxy :: Nat -> Type)
58− -> Int
59− -> Proxy (a :: Nat)
60− -> TypeRepMap (Proxy :: Nat -> Type)
61−inserts !c 0 _ = c
62−inserts !c n x = inserts
63− (insert x c)
64− (n-1)
65− (Proxy :: Proxy (a+1))
66−
67−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
68−mkMap n = pure $ buildBigMap n (Proxy :: Proxy 0) empty
45+-- TypeRepMap of 10000 elements
46+bigMap :: TypeRepMap (Proxy :: Nat -> *)
47+bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
6948
70−buildBigMap
71− :: forall a . (KnownNat a)
72− => Int
73− -> Proxy (a :: Nat)
74− -> TypeRepMap (Proxy :: Nat -> Type)
75− -> TypeRepMap (Proxy :: Nat -> Type)
49+buildBigMap :: forall a . (KnownNat a) => Int -> Proxy (a :: Nat) -> TypeRepMap (Proxy :: Nat -> *) -> TypeRepMap (Proxy :: Nat -> *)
7650buildBigMap 1 x = insert x
7751buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
52+
53+rknf :: TypeRepMap f -> ()
54+rknf = rnf . keys
55+
56+prepareBenchMap :: IO ()
57+prepareBenchMap = evaluate (rknf bigMap)
benchmark/CacheMap.hs+29−55
@@ -1,79 +1,53 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE TypeFamilies #-}
66{-# LANGUAGE TypeOperators #-}
77{-# LANGUAGE UndecidableInstances #-}
88
9+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
10+
911module CacheMap
10− ( spec
11− ) where
12+ ( benchCacheMap
13+ ) where
14+
15+import Criterion.Main (Benchmark, bench, bgroup, nf)
1216
1317import Prelude hiding (lookup)
1418
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
1719import Data.Maybe (fromJust)
1820import Data.Proxy (Proxy (..))
1921import Data.Typeable (Typeable)
2022import GHC.Exts (fromList)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
23+import GHC.TypeLits
2224
23−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
25+import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), lookup)
2426
25−import Spec (BenchSpec (..))
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+ ]
2633
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− )
34+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
35+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
36+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
37+ )
5138tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5239 where
53− lp :: forall (a :: Nat) . Typeable a => Proxy a
40+ lp :: forall (a::Nat). Typeable a => Proxy a
5441 lp = fromJust $ lookup tmap
5542
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−
43+-- TypeRepMap of 10000 elements
44+bigMap :: TypeRepMap (Proxy :: Nat -> *)
45+bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
7146
72−buildBigMap
73− :: forall a . (KnownNat a)
74− => Int
75− -> Proxy (a :: Nat)
76− -> [WrapTypeable (Proxy :: Nat -> Type)]
77− -> [WrapTypeable (Proxy :: Nat -> Type)]
47+buildBigMap :: forall a . (KnownNat a)
48+ => Int
49+ -> Proxy (a :: Nat)
50+ -> [WrapTypeable (Proxy :: Nat -> *)]
51+ -> [WrapTypeable (Proxy :: Nat -> *)]
7852buildBigMap 1 x = (WrapTypeable x :)
7953buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hs+53−56
@@ -1,89 +1,86 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
43{-# LANGUAGE InstanceSigs #-}
4+{-# LANGUAGE KindSignatures #-}
55{-# LANGUAGE PolyKinds #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
9+{-# LANGUAGE ViewPatterns #-}
10+
11+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver -fno-warn-orphans #-}
912
1013module DMap
11− ( spec
12− ) where
14+ ( benchDMap
15+ , prepareBenchDMap
16+ ) where
17+
18+import Criterion.Main (Benchmark, bench, bgroup, nf)
1319
1420import Prelude hiding (lookup)
1521
16−import Control.DeepSeq (NFData (..))
17−import Criterion.Main (bench, env, nf, whnf)
18−import Data.Kind (Type)
22+import Control.DeepSeq (rnf)
23+import Control.Exception
1924import Data.Maybe (fromJust)
2025import Data.Proxy (Proxy (..))
21−import GHC.TypeLits (type (+), KnownNat, Nat)
26+import Data.Type.Equality ((:~:) (..))
27+import GHC.TypeLits
2228import Type.Reflection (TypeRep, Typeable, typeRep)
2329import Type.Reflection.Unsafe (typeRepFingerprint)
30+import Unsafe.Coerce (unsafeCoerce)
2431
2532import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
26−import Data.Some (Some (Some))
27−
28−import Spec (BenchSpec (..))
29−
33+import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
34+import Data.Some (Some (This))
3035
3136type TypeRepMap = DMap TypeRep
3237
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− }
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+ ]
4644
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− )
45+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
46+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
47+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
48+ )
5249tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
5350 where
5451 lp :: forall (a :: Nat) . Typeable a => Proxy a
5552 lp = fromJust $ lookup (typeRep @a) tmap
5653
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−
6954-- TypeRepMap of 10000 elements
70−mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
71−mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
55+bigMap :: TypeRepMap (Proxy :: Nat -> *)
56+bigMap = buildBigMap 10000 (Proxy :: Proxy 0) empty
7257
73−buildBigMap
74− :: forall a . (KnownNat a)
75− => Int
76− -> Proxy (a :: Nat)
77− -> TypeRepMap (Proxy :: Nat -> Type)
78− -> TypeRepMap (Proxy :: Nat -> Type)
58+buildBigMap :: forall a . (KnownNat a)
59+ => Int
60+ -> Proxy (a :: Nat)
61+ -> TypeRepMap (Proxy :: Nat -> *)
62+ -> TypeRepMap (Proxy :: Nat -> *)
7963buildBigMap 1 x = insert (typeRep @a) x
8064buildBigMap n x = insert (typeRep @a) x
8165 . buildBigMap (n - 1) (Proxy @(a + 1))
8266
83−-- | Wrapper that provides NFData instance to the 'DMap' structure.
84−newtype DMapNF f = DMapNF (TypeRepMap f)
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
8579
86−instance NFData (DMapNF f) where
87− rnf :: DMapNF f -> ()
88− rnf (DMapNF x) =
89− rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
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
benchmark/Main.hs+20−41
@@ -1,52 +1,31 @@
11{-# LANGUAGE CPP #-}
22
3−module Main
4− ( main
5− ) where
3+module Main where
64
7−import Criterion.Main (bgroup, defaultMain)
5+import Criterion.Main (defaultMain)
86
9−import Spec (BenchSpec (..))
10−
11−import qualified CacheMap
12−import qualified CMap
7+import CacheMap (benchCacheMap)
8+import CMap (benchMap, prepareBenchMap)
139#if ( __GLASGOW_HASKELL__ >= 802 )
14−import qualified DMap
10+import DMap (benchDMap, prepareBenchDMap)
1511#endif
16−import qualified OptimalVector as OptVec
17−
12+import OptimalVector (benchVectorOpt, prepareBenchVectorOpt)
13+--import Vector (benchVector, prepareBenchVector)
1814
1915main :: IO ()
2016main = do
21− let specs = [("CMap", CMap.spec)
22− ,("CacheMap", CacheMap.spec)
17+ prepareBenchMap
18+ --prepareBenchVector
19+ prepareBenchVectorOpt
20+#if ( __GLASGOW_HASKELL__ >= 802 )
21+ prepareBenchDMap
22+#endif
23+ defaultMain
24+ [ benchMap
25+ -- , benchVector
26+ , benchCacheMap
27+ , benchVectorOpt
2328#if ( __GLASGOW_HASKELL__ >= 802 )
24− , ("DMap", DMap.spec)
29+ , benchDMap
2530#endif
26− , ("OptVec", OptVec.spec)
27− ]
28− {- This code creates a benchmark group. Given a getter
29− (that is test description) it gets a benchmark generation
30− function from each module spec. Benchmark generation
31− function takes a label and generate benchmarks. It's
32− possible to introduce parameters passing in the same way.
33− --}
34− let mkGroup getBenchmark =
35− [ mkBenchmark label
36− | (label, spec) <- specs
37− -- Here we use pure to force pattern matching in List
38− -- then in case of pattern match failure `mzero` will
39− -- be called, so benchmark will be ignored.
40− , Just mkBenchmark <- pure $ getBenchmark spec
41− ]
42− defaultMain
43− [ bgroup "lookup" $ mkGroup benchLookup
44− , bgroup "insert"
45− [ bgroup "10 elements to empty" $ mkGroup benchInsertSmall
46− , bgroup "1 element to big map" $ mkGroup benchInsertBig
47− ]
48− , bgroup "update"
49− [ bgroup "10 elements to empty" $ mkGroup benchUpdateSmall
50− , bgroup "1 element to big map" $ mkGroup benchUpdateBig
51− ]
52− ]
31+ ]
benchmark/OptimalVector.hs+36−34
@@ -1,60 +1,62 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
99
10+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11+
1012module OptimalVector
11− ( spec
13+ ( benchVectorOpt
14+ , prepareBenchVectorOpt
1215 ) where
1316
17+import Criterion.Main (Benchmark, bench, bgroup, nf)
18+
1419import Prelude hiding (lookup)
1520
16−import Criterion.Main (bench, env, nf)
17−import Data.Kind (Type)
21+import Control.DeepSeq (rnf)
22+import Control.Exception
1823import Data.Maybe (fromJust)
1924import Data.Proxy (Proxy (..))
2025import Data.Typeable (Typeable)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
26+import GHC.TypeLits
2227
2328import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
2429
25−import Spec (BenchSpec (..))
26−
27−
28−spec :: BenchSpec
29−spec = BenchSpec
30− { benchLookup = Just $ \name ->
31− env mkBigMap $ \ ~bigMap ->
32− bench name $ nf tenLookups bigMap
33− , benchInsertSmall = Nothing -- Not implemented
34− , benchInsertBig = Nothing -- Not implemented
35− , benchUpdateSmall = Nothing -- Not implemented
36− , benchUpdateBig = Nothing -- Not implemented
37− }
38−
39−tenLookups
40− :: TypeRepMap (Proxy :: Nat -> Type)
41− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
42− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
43− )
30+benchVectorOpt :: Benchmark
31+benchVectorOpt = bgroup "vector optimal"
32+ [ bench "lookup" $ nf tenLookups bigMap
33+ -- , bench "insert new" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 9999999999)
34+ -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
35+ ]
36+
37+tenLookups :: TypeRepMap (Proxy :: Nat -> *)
38+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40+ )
4441tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4542 where
4643 lp :: forall (a::Nat). Typeable a => Proxy a
4744 lp = fromJust $ lookup tmap
4845
4946-- TypeRepMap of 10000 elements
50−mkBigMap :: IO (TypeRepMap (Proxy :: Nat -> Type))
51−mkBigMap = pure $ fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
52−
53−buildBigMap
54− :: forall a . (KnownNat a)
55− => Int
56− -> Proxy (a :: Nat)
57− -> [TF (Proxy :: Nat -> Type)]
58− -> [TF (Proxy :: Nat -> Type)]
47+bigMap :: TypeRepMap (Proxy :: Nat -> *)
48+bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
49+
50+buildBigMap :: forall a . (KnownNat a)
51+ => Int
52+ -> Proxy (a :: Nat)
53+ -> [TF (Proxy :: Nat -> *)]
54+ -> [TF (Proxy :: Nat -> *)]
5955buildBigMap 1 x = (TF x :)
6056buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
57+
58+rknf :: TypeRepMap f -> ()
59+rknf tVect = rnf (fingerprintAs tVect, fingerprintBs tVect)
60+
61+prepareBenchVectorOpt :: IO ()
62+prepareBenchVectorOpt = evaluate (rknf bigMap)
benchmark/Spec.hsdeleted−64
@@ -1,64 +0,0 @@
1−{- | Specification of the benchmarks.
2−
3−This module keeps a list of all bencharks, this way
4−we can group benchmark by the interesting function, not
5−by the implementation.
6−-}
7−module Spec
8− ( BenchSpec(..)
9− ) where
10−
11−import Criterion (Benchmark)
12−
13−
14−{- | List of benchmarks that each module should provide.
15−If implementation can express the benchmark then it
16−can return @Nothing@ in that benchmark.
17−
18−Map should contain elements from @1@ to @size of map@
19−inserted in ascending order (later that requirement may
20−change).
21−-}
22−data BenchSpec = BenchSpec
23− { {- | Basic lookup we look 10 values inside 10k map.
24−
25− Implementation may look like:
26−
27− @
28− tenLookups :: TypeRepMap (Proxy :: Nat -> *)
29− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
30− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
31− )
32− tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
33− @
34− -}
35− benchLookup :: Maybe (String -> Benchmark)
36− {- ^ Insert 10 elements into an empty map.
37−
38− Implementation may look like:
39−
40− @
41− inserts
42− :: forall a . (KnownNat a)
43− => TypeRepMap (Proxy :: Nat -> *)
44− -> Int
45− -> Proxy (a :: Nat)
46− -> TypeRepMap (Proxy :: Nat -> *)
47− inserts !c 0 _ = c
48− inserts !c n x = inserts (insert x c) (n-1) (Proxy :: Proxy (a+1))
49− @
50− -}
51− , benchInsertSmall :: Maybe (String -> Benchmark)
52− {- ^ Insert 10 elements into a big map. Implementation is like
53− a small map, but should insert values into 10k elements map.
54− -}
55− , benchInsertBig :: Maybe (String -> Benchmark)
56− {- ^ Insert 10 elements into map of 10 elements, where each key
57− was already inserted in the map
58− -}
59− , benchUpdateSmall :: Maybe (String -> Benchmark)
60− {- ^ Insert 10 elements into map of 10k elements, where each key
61− was already inserted in the map
62− -}
63− , benchUpdateBig :: Maybe (String -> Benchmark)
64− }
benchmark/Vector.hs+22−23
@@ -1,30 +1,31 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
31{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE ExplicitNamespaces #-}
3+{-# LANGUAGE KindSignatures #-}
44{-# LANGUAGE PolyKinds #-}
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE TypeFamilies #-}
77{-# LANGUAGE TypeOperators #-}
88{-# LANGUAGE UndecidableInstances #-}
99
10+{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
11+
1012module Vector
11− ( benchVector
12− , prepareBenchVector
13− ) where
13+ ( benchVector
14+ , prepareBenchVector
15+ ) where
16+
17+import Criterion.Main (Benchmark, bench, bgroup, nf)
1418
1519import Prelude hiding (lookup)
1620
1721import Control.DeepSeq (rnf)
18−import Control.Exception (evaluate)
19−import Criterion.Main (Benchmark, bench, bgroup, nf)
20−import Data.Kind (Type)
22+import Control.Exception
2123import Data.Maybe (fromJust)
2224import Data.Proxy (Proxy (..))
2325import Data.Typeable (Typeable)
24−import GHC.TypeLits (type (+), KnownNat, Nat)
25−
26−import Data.TypeRep.Vector (TF (..), TypeRepVector, fingerprints, fromList, lookup)
26+import GHC.TypeLits
2727
28+import Data.TypeRep.Vector
2829
2930benchVector :: Benchmark
3031benchVector = bgroup "vector"
@@ -33,26 +34,24 @@ benchVector = bgroup "vector"
3334 -- , bench "update old" $ whnf (\x -> rknf $ insert x bigMap) (Proxy :: Proxy 1)
3435 ]
3536
36−tenLookups
37− :: TypeRepVector (Proxy :: Nat -> Type)
38− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40− )
37+tenLookups :: TypeRepVector (Proxy :: Nat -> *)
38+ -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
39+ , Proxy 50, Proxy 60, Proxy 70, Proxy 80
40+ )
4141tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
4242 where
4343 lp :: forall (a::Nat). Typeable a => Proxy a
4444 lp = fromJust $ lookup tmap
4545
4646-- TypeRepMap of 10000 elements
47−bigMap :: TypeRepVector (Proxy :: Nat -> Type)
47+bigMap :: TypeRepVector (Proxy :: Nat -> *)
4848bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
4949
50−buildBigMap
51− :: forall a . (KnownNat a)
52− => Int
53− -> Proxy (a :: Nat)
54− -> [TF (Proxy :: Nat -> Type)]
55− -> [TF (Proxy :: Nat -> Type)]
50+buildBigMap :: forall a . (KnownNat a)
51+ => Int
52+ -> Proxy (a :: Nat)
53+ -> [TF (Proxy :: Nat -> *)]
54+ -> [TF (Proxy :: Nat -> *)]
5655buildBigMap 1 x = (TF x :)
5756buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
5857
shell.nixnew file+10
@@ -0,0 +1,10 @@
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+6−69
@@ -2,10 +2,6 @@
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−
95'TMap' is a heterogeneous data structure similar in its essence to
106'Data.Map.Map' with types as keys, where each value has the type of its key.
117
@@ -37,19 +33,12 @@ module Data.TMap
3733 , delete
3834 , unionWith
3935 , union
40− , intersectionWith
41− , intersection
4236 , map
43− , adjust
44− , alter
4537
4638 -- * Query
4739 , lookup
4840 , member
4941 , size
50− , keys
51− , keysWith
52− , toListWith
5342 ) where
5443
5544import Prelude hiding (lookup, map)
@@ -57,7 +46,6 @@ import Prelude hiding (lookup, map)
5746import Data.Functor.Identity (Identity (..))
5847import Data.Typeable (Typeable)
5948import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
6149
6250import qualified Data.TypeRepMap as F
6351
@@ -92,7 +80,6 @@ one x = coerce (F.one @a @Identity $ coerce x)
9280{- |
9381
9482Insert a value into a 'TMap'.
95−TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
9683
9784prop> size (insert v tm) >= size tm
9885prop> member @a (insert (x :: a) tm) == True
@@ -104,9 +91,6 @@ insert x = coerce (F.insert @a @Identity $ coerce x)
10491
10592{- | Delete a value from a 'TMap'.
10693
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−
11094prop> size (delete @a tm) <= size tm
11195prop> member @a (delete @a tm) == False
11296
@@ -123,10 +107,10 @@ delete = F.delete @a @Identity
123107{-# INLINE delete #-}
124108
125109-- | The union of two 'TMap's using a combining function.
126−unionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
110+unionWith :: (forall x. x -> x -> x) -> TMap -> TMap -> TMap
127111unionWith f = F.unionWith fId
128112 where
129− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
113+ fId :: forall y . Identity y -> Identity y -> Identity y
130114 fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
131115{-# INLINE unionWith #-}
132116
@@ -136,24 +120,6 @@ union :: TMap -> TMap -> TMap
136120union = F.union
137121{-# INLINE union #-}
138122
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−
157123{- | Lookup a value of the given type in a 'TMap'.
158124
159125>>> x = lookup $ insert (11 :: Int) empty
@@ -182,39 +148,10 @@ size :: TMap -> Int
182148size = F.size
183149{-# INLINE size #-}
184150
185−-- | Returns the list of 'SomeTypeRep's from keys.
186−keys :: TMap -> [SomeTypeRep]
187−keys = F.keys
188−{-# INLINE keys #-}
189−
190−-- | Return the list of keys by wrapping them with a user-provided function.
191−keysWith :: (forall a. TypeRep a -> r) -> TMap -> [r]
192−keysWith = F.keysWith
193−{-# INLINE keysWith #-}
194−
195−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
196−toListWith :: (forall a. Typeable a => a -> r) -> TMap -> [r]
197−toListWith = F.toListWith
198−{-# INLINE toListWith #-}
199−
200151-- | Map a function over the values.
201152map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202−map f = F.hoistWithKey (liftToIdentity f)
203−{-# INLINE map #-}
204−
205−-- | Update a value with the result of the provided function.
206−adjust :: Typeable a => (a -> a) -> TMap -> TMap
207−adjust f = F.adjust (liftToIdentity f)
208−{-# INLINE adjust #-}
209−
210−-- | Updates a value at a specific key, whether or not it exists.
211−-- This can be used to insert, delete, or update a value of a given type in the map.
212−alter :: Typeable a => (Maybe a -> Maybe a) -> TMap -> TMap
213−alter f = F.alter (liftF f)
153+map f = F.hoistWithKey (fIdentity f)
214154 where
215− liftF :: forall a. (Maybe a -> Maybe a) -> Maybe (Identity a) -> Maybe (Identity a)
216− liftF = coerce
217−{-# INLINE alter #-}
218−
219−liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
220−liftToIdentity = coerce
155+ fIdentity :: forall a. Typeable a => (a -> a) -> Identity a -> Identity a
156+ fIdentity = coerce
157+{-# INLINE map #-}
src/Data/TypeRepMap.hs+40−52
@@ -1,50 +1,45 @@
11{-# LANGUAGE NoImplicitPrelude #-}
22
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−--}
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+--
4843module Data.TypeRepMap
4944 ( -- * Map type
5045 TypeRepMap()
@@ -56,23 +51,16 @@ module Data.TypeRepMap
5651 -- * Modification
5752 , insert
5853 , delete
59− , adjust
60− , alter
6154 , hoist
6255 , hoistA
6356 , hoistWithKey
6457 , unionWith
6558 , union
66− , intersectionWith
67− , intersection
6859
6960 -- * Query
7061 , lookup
7162 , member
7263 , size
73− , keys
74− , keysWith
75− , toListWith
7664
7765 -- * 'IsList'
7866 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+76−378
@@ -1,69 +1,48 @@
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
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 #-}
1913
2014-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
2115
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−
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+--
3622module Data.TypeRepMap.Internal where
3723
3824import Prelude hiding (lookup)
3925
40−import Control.DeepSeq
41−import Control.Monad.ST (ST, runST)
4226import Control.Monad.Zip (mzip)
4327import Data.Function (on)
28+import Data.IntMap.Strict (IntMap)
4429import Data.Kind (Type)
45−import Data.Type.Equality ((:~:) (..), TestEquality (..))
46−import Data.List (intercalate, nubBy)
47−import Data.Maybe (fromMaybe)
48−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
49− thawArray, unsafeFreezeArray, writeArray)
50−import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
51−import Data.Semigroup (Semigroup (..), All(..))
30+import Data.List (nubBy)
31+import Data.Maybe (fromJust)
32+import Data.Primitive.Array (Array, indexArray, mapArray')
33+import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
34+import Data.Semigroup (Semigroup (..))
5235import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
5336import GHC.Exts (IsList (..), inline, sortWith)
5437import GHC.Fingerprint (Fingerprint (..))
55−#if WORD_SIZE_IN_BITS >= 64
5638import GHC.Prim (eqWord#, ltWord#)
57−#else
58−import GHC.IntWord64 (eqWord64#, ltWord64#)
59−#define eqWord eqWord64
60−#define ltWord ltWord64
61−#endif
6239import GHC.Word (Word64 (..))
63−import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeable)
40+import Type.Reflection (TypeRep, Typeable, typeRep, withTypeable)
6441import Type.Reflection.Unsafe (typeRepFingerprint)
6542import Unsafe.Coerce (unsafeCoerce)
6643
44+import qualified Data.IntMap.Strict as IM
45+import qualified Data.Map.Strict as Map
6746import qualified GHC.Exts as GHC (fromList, toList)
6847
6948{- |
@@ -91,20 +70,14 @@ data TypeRepMap (f :: k -> Type) =
9170 TypeRepMap
9271 { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ first components of key fingerprints
9372 , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ second components of key fingerprints
94− , trAnys :: {-# UNPACK #-} !(Array Any) -- ^ values stored in the map
95− , trKeys :: {-# UNPACK #-} !(Array Any) -- ^ typerep keys
73+ , anys :: {-# UNPACK #-} !(Array Any) -- ^ values stored in the map
74+ , keys :: {-# UNPACK #-} !(Array Any) -- ^ typerep keys
9675 }
9776 -- ^ an unsafe constructor for 'TypeRepMap'
9877
99−instance NFData (TypeRepMap f) where
100− rnf x = rnf (keys x) `seq` ()
101−
102−-- | Shows only keys.
78+-- | Shows only 'Fingerprint's.
10379instance Show (TypeRepMap f) where
104− show TypeRepMap{..} = "TypeRepMap [" ++ showKeys ++ "]"
105− where
106− showKeys :: String
107− showKeys = intercalate ", " $ toList $ mapArray' (show . anyToTypeRep) trKeys
80+ show = show . toFingerprints
10881
10982-- | Uses 'union' to combine 'TypeRepMap's.
11083instance Semigroup (TypeRepMap f) where
@@ -118,31 +91,6 @@ instance Monoid (TypeRepMap f) where
11891 {-# INLINE mempty #-}
11992 {-# INLINE mappend #-}
12093
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−
14694-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
14795toFingerprints :: TypeRepMap f -> [Fingerprint]
14896toFingerprints TypeRepMap{..} =
@@ -169,49 +117,33 @@ prop> member @a (one (x :: f a)) == True
169117
170118-}
171119one :: forall a f . Typeable a => f a -> TypeRepMap f
172−one x = TypeRepMap (primArrayFromListN 1 [fa])
173− (primArrayFromListN 1 [fb])
174− (pure @Array v)
175− (pure @Array k)
176− where
177− (Fingerprint fa fb, v, k) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
120+one x = insert x empty
178121{-# INLINE one #-}
179122
180123{- |
181124
182125Insert a value into a 'TypeRepMap'.
183−TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
184126
185127prop> size (insert v tm) >= size tm
186128prop> member @a (insert (x :: f a) tm) == True
187129
188130-}
189131insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
190−insert x m
191− | size m == 0 = one x
192− | otherwise = case cachedBinarySearch (typeFp @a) (fingerprintAs m) (fingerprintBs m) of
193− Nothing -> union m $ one x
194− Just i -> m {trAnys = changeAnyArr i (trAnys m)}
132+insert x = fromTriples . addX . toTriples
195133 where
196− changeAnyArr :: Int -> Array Any -> Array Any
197− changeAnyArr i trAs = runST $ do
198− let n = sizeofArray trAs
199− mutArr <- thawArray trAs 0 n
200− writeArray mutArr i $ toAny x
201− unsafeFreezeArray mutArr
134+ tripleX :: (Fingerprint, Any, Any)
135+ tripleX@(fpX, _, _) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
136+
137+ addX :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
138+ addX l = tripleX : deleteByFst fpX l
202139{-# INLINE insert #-}
203140
204141-- Extract the kind of a type. We use it to work around lack of syntax for
205142-- inferred type variables (which are not subject to type applications).
206143type KindOf (a :: k) = k
207144
208−type ArgKindOf (f :: k -> l) = k
209−
210145{- | Delete a value from a 'TypeRepMap'.
211146
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−
215147prop> size (delete @a tm) <= size tm
216148prop> member @a (delete @a tm) == False
217149
@@ -224,61 +156,9 @@ False
224156True
225157-}
226158delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
227−delete m
228− -- Lookups are fast, so check if we even have the element first.
229− | not (member @a m) = m
230− -- We know we have the element, If the map has exactly one element, we can return the empty map
231− | size m == 1 = empty
232− -- Otherwise, filter out the element in linear time.
233− | otherwise = fromSortedTriples . deleteFirst ((== typeFp @a) . fst3) . toSortedTriples $ m
159+delete = fromTriples . deleteByFst (typeFp @a) . toTriples
234160{-# INLINE delete #-}
235161
236−deleteFirst :: (a -> Bool) -> [a] -> [a]
237−deleteFirst _ [] = []
238−deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239−
240−{- |
241−Update a value at a specific key with the result of the provided function.
242−When the key is not a member of the map, the original map is returned.
243−
244−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
245−>>> lookup @String $ adjust (fmap (++ "ww")) trmap
246−Just (Identity "aww")
247−-}
248−adjust :: forall a f . Typeable a => (f a -> f a) -> TypeRepMap f -> TypeRepMap f
249−adjust fun = alter (fmap fun)
250−{-# INLINE adjust #-}
251−
252−{- |
253−Updates a value at a specific key, whether or not it exists.
254−This can be used to insert, delete, or update a value of a given type in the map.
255−
256−>>> func = (\case Nothing -> Just (Identity "new"); Just (Identity s) -> Just (Identity (reverse s)))
257−>>> lookup @String $ alter func empty
258−Just (Identity "new")
259−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "helllo"]
260−>>> lookup @String $ alter func trmap
261−>>> Just (Identity "olleh")
262−-}
263−alter :: forall a f . Typeable a => (Maybe (f a) -> Maybe (f a)) -> TypeRepMap f -> TypeRepMap f
264−alter fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
265− Nothing ->
266− case (fun Nothing) of
267− Nothing -> tr
268− Just v -> insert v tr
269− Just i ->
270− case fun (Just . fromAny $ indexArray (trAnys tr) i) of
271− Nothing -> delete @a tr
272− Just v -> tr{trAnys = replaceAnyAt i (toAny v) (trAnys tr)}
273− where
274− replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275− replaceAnyAt i v trAs = runST $ do
276− let n = sizeofArray trAs
277− mutArr <- thawArray trAs 0 n
278− writeArray mutArr i v
279− unsafeFreezeArray mutArr
280−{-# INLINE alter #-}
281−
282162{- | Map over the elements of a 'TypeRepMap'.
283163
284164>>> tm = insert (Identity True) $ one (Identity 'a')
@@ -311,89 +191,30 @@ hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
311191 withTr t = withTypeable t f
312192{-# INLINE hoistWithKey #-}
313193
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)
194+-- | The union of two 'TypeRepMap's using a combining function.
195+unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
196+unionWith f m1 m2 = fromTriples
197+ $ toTripleList
198+ $ Map.unionWith combine
199+ (fromTripleList $ toTriples m1)
200+ (fromTripleList $ toTriples m2)
318201 where
319− f' :: forall x. TypeRep x -> f x -> f x -> f x
320− f' tr = withTypeable tr f
321−
322− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
323− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
324−
325− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
326− -- Using 'toSortedTriples' allows us to assume the triples are sorted by fingerprint,
327− -- Given O(n) performance from 'toSortedTriples', and given that we can merge-sort in
328− -- O(n + m) time, then can '.fromSortedTriples' back into cachedBinarySearch order in O(n + m)
329− -- that gives a total of O(n + m).
330− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
331− -- We've addressed all elements from both maps
332− mergeMaps as [] = as
333− mergeMaps [] bs = bs
334− -- Merge
335− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
336− case compare af bf of
337− -- Fingerprints are equal, union the elements using our function
338− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
339− -- fingerprints
340− EQ -> combine a b : mergeMaps as bs
341− -- First fingerprint must not be in the second map or we would have seen it by now
342− -- Add it to the result as-is
343− LT -> a : mergeMaps as (b : bs)
344− -- Second fingerprint must not be in the first map or we would have seen it by now
345− -- Add it to the result as-is
346− GT -> b : mergeMaps (a:as) bs
202+ combine :: (Any, Any) -> (Any, Any) -> (Any, Any)
203+ combine (av, ak) (bv, _) = (toAny $ f (fromAny av) (fromAny bv), ak)
204+
205+ fromTripleList :: Ord a => [(a, b, c)] -> Map.Map a (b, c)
206+ fromTripleList = Map.fromList . map (\(a, b, c) -> (a, (b, c)))
207+
208+ toTripleList :: Map.Map a (b, c) -> [(a, b, c)]
209+ toTripleList = map (\(a, (b, c)) -> (a, b, c)) . Map.toList
347210{-# INLINE unionWith #-}
348211
349−-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
212+-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
350213-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
351214union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352215union = unionWith const
353216{-# INLINE union #-}
354217
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−
397218{- | Check if a value of the given type is present in a 'TypeRepMap'.
398219
399220>>> member @Char $ one (Identity 'a')
@@ -416,7 +237,7 @@ Just (Identity 11)
416237Nothing
417238-}
418239lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
419−lookup tVect = fromAny . (trAnys tVect `indexArray`)
240+lookup tVect = fromAny . (anys tVect `indexArray`)
420241 <$> cachedBinarySearch (typeFp @a)
421242 (fingerprintAs tVect)
422243 (fingerprintBs tVect)
@@ -427,24 +248,6 @@ size :: TypeRepMap f -> Int
427248size = sizeofPrimArray . fingerprintAs
428249{-# INLINE size #-}
429250
430−-- | Return the list of 'SomeTypeRep' from the keys.
431−keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
433−{-# INLINE keys #-}
434−
435−-- | Return the list of keys by wrapping them with a user-provided function.
436−keysWith :: (forall (a :: ArgKindOf f). TypeRep a -> r) -> TypeRepMap f -> [r]
437−keysWith f TypeRepMap{..} = f . anyToTypeRep <$> toList trKeys
438−{-# INLINE keysWith #-}
439−
440−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
441−toListWith :: forall f r. (forall (a :: ArgKindOf f). Typeable a => f a -> r) -> TypeRepMap f -> [r]
442−toListWith f = map toF . toTriples
443− where
444− withTypeRep :: TypeRep a -> f a -> r
445− withTypeRep tr an = withTypeable tr $ f an
446− toF (_, an, k) = withTypeRep (unsafeCoerce k) (fromAny an)
447−
448251-- | Binary searched based on this article
449252-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450253-- with modification for our two-vector search case.
@@ -478,30 +281,15 @@ toAny = unsafeCoerce
478281fromAny :: Any -> f a
479282fromAny = unsafeCoerce
480283
481−anyToTypeRep :: Any -> TypeRep f
482−anyToTypeRep = unsafeCoerce
483−
484284typeFp :: forall a . Typeable a => Fingerprint
485285typeFp = typeRepFingerprint $ typeRep @a
486286{-# INLINE typeFp #-}
487287
488288toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
489−toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
289+toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ anys tm) (GHC.toList $ keys tm)
490290
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)
291+deleteByFst :: Eq a => a -> [(a, b, c)] -> [(a, b, c)]
292+deleteByFst x = filter ((/= x) . fst3)
505293
506294nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
507295nubByFst = nubBy ((==) `on` fst3)
@@ -527,8 +315,8 @@ prop> fromList . toList == 'id'
527315
528316Creates 'TypeRepMap' from a list of 'WrapTypeable's.
529317
530−>>> show $ fromList [WrapTypeable $ Identity True, WrapTypeable $ Identity 'a']
531−TypeRepMap [Bool, Char]
318+>>> size $ fromList [WrapTypeable $ Identity True, WrapTypeable $ Identity 'a']
319+2
532320
533321
534322-}
@@ -548,123 +336,33 @@ instance IsList (TypeRepMap f) where
548336 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549337
550338 toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
339+ toList = map toWrapTypeable . toTriples
340+ where
341+ toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
342+ toWrapTypeable (_, an, k) = withTypeable (unsafeCoerce k) $ fromAny an
552343
553344calcFp :: forall a . Typeable a => Fingerprint
554345calcFp = typeRepFingerprint $ typeRep @a
555346
556347fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
557−fromTriples = fromSortedTriples . sortWith fst3 . nubByFst
558−
559−fromSortedTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
560−fromSortedTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
348+fromTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
561349 where
562350 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
563− (fps, ans, ks) = unzip3 $ fromSortedList kvs
351+ (fps, ans, ks) = unzip3 $ fromSortedList $ sortWith fst3 $ nubByFst kvs
564352
565353----------------------------------------------------------------------------
566354-- Tree-like conversion
567355----------------------------------------------------------------------------
568356
569357fromSortedList :: forall a . [a] -> [a]
570−fromSortedList l = runST $ do
571− let n = length l
572− let arrOrigin = fromListN n l
573− arrResult <- thawArray arrOrigin 0 n
574− go n arrResult arrOrigin
575− toList <$> unsafeFreezeArray arrResult
358+fromSortedList l = IM.elems $ fst $ go 0 0 mempty (IM.fromList $ zip [0..] l)
576359 where
577360 -- state monad could be used here, but it's another dependency
578− go :: forall s . Int -> MutableArray s a -> Array a -> ST s ()
579− go len result origin = () <$ loop 0 0
580− where
581− loop :: Int -> Int -> ST s Int
582− loop i first =
583− if i >= len
584− then pure first
585− else do
586− newFirst <- loop (2 * i + 1) first
587− writeArray result i (indexArray origin newFirst)
588− loop (2 * i + 2) (newFirst + 1)
589−
590−-- Returns a list of indexes which represents the "sorted" order of an array generated by
591−-- fromSortedList of the provided length.
592−-- I.e. fmap (fromSortedList [1, 2, 3, 4, 5, 6] !!) (generateOrderMapping 6) == [1, 2, 3, 4, 5, 6]
593−--
594−-- >>> generateOrderMapping 6
595−-- [3,1,4,0,5,2]
596−--
597−-- >>> generateOrderMapping 8
598−-- [7,3,1,4,0,5,2,6]
599−generateOrderMapping :: Int -> [Int]
600−generateOrderMapping len = runST $ do
601− orderMappingArr <- newPrimArray len
602− _ <- loop orderMappingArr 0 0
603− primArrayToList <$> unsafeFreezePrimArray orderMappingArr
604− where
605− loop :: MutablePrimArray s Int -> Int -> Int -> ST s Int
606− loop result i first =
607− if i >= len
608− then pure first
609− else do
610− newFirst <- loop result (2 * i + 1) first
611− writePrimArray result newFirst i
612− loop result (2 * i + 2) (newFirst + 1)
613−
614−----------------------------------------------------------------------------
615−-- Helper functions.
616−----------------------------------------------------------------------------
617−
618−-- | Check that invariant of the structure holds.
619−-- The structure maintains the following invariant.
620−-- For each element @A@ at index @i@:
621−--
622−-- 1. if there is an element @B@ at index @2*i+1@,
623−-- then @B < A@.
624−--
625−-- 2. if there is an element @C@ at index @2*i+2@,
626−-- then @A < C@.
627−--
628−invariantCheck :: TypeRepMap f -> Bool
629−invariantCheck TypeRepMap{..} = getAll (check 0)
630− where
631− lastMay [] = Nothing
632− lastMay [x] = Just x
633− lastMay (_:xs) = lastMay xs
634− sz = sizeofPrimArray fingerprintAs
635− check i | i >= sz = All True
636− | otherwise =
637− let left = i*2+1
638− right = i*2+2
639− -- maximum value in the left branch
640− leftMax =
641− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
642− $ lastMay
643− $ takeWhile (<sz)
644− $ iterate (\j -> j*2+2) left
645− -- minimum value in the right branch
646− rightMin =
647− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
648− $ lastMay
649− $ takeWhile (<sz)
650− $ iterate (\j -> j*2+1) right
651− in mconcat
652− [ All $
653− if left < sz
654− then
655− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs left of
656− LT -> False
657− EQ -> indexPrimArray fingerprintBs i >= indexPrimArray fingerprintBs left
658− GT -> True
659− else True
660− , All $
661− if right < sz
662− then
663− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs right of
664− LT -> True
665− EQ -> indexPrimArray fingerprintBs i <= indexPrimArray fingerprintBs right
666− GT -> False
667− else True
668− , All $ fromMaybe True $ (<=) <$> leftMax <*> rightMin
669− , check (i+1)
670− ]
361+ go :: Int -> Int -> IntMap a -> IntMap a -> (IntMap a, Int)
362+ go i first result vector =
363+ if i >= IM.size vector
364+ then (result, first)
365+ else do
366+ let (newResult, newFirst) = go (2 * i + 1) first result vector
367+ let withCur = IM.insert i (fromJust $ IM.lookup newFirst vector) newResult
368+ go (2 * i + 2) (newFirst + 1) withCur vector
stack.yaml+2−4
@@ -1,6 +1,4 @@
1−resolver: lts-15.8
1+resolver: nightly-2018-07-05
22
33extra-deps:
4− - dependent-map-0.2.4.0
5− - dependent-sum-0.5
6− - hspec-hedgehog-0.0.1.2
4+ - primitive-0.6.4.0
test/Test.hs+1−21
@@ -1,21 +1 @@
1−module Main
2− ( main
3− ) where
4−
5−import Test.Hspec (hspec)
6−
7−import Test.TypeRep.CMap (cMapSpec)
8−import Test.TypeRep.TypeRepMap (typeRepMapSpec)
9−import Test.TypeRep.TypeRepMapProperty (typeRepMapPropertySpec)
10−import Test.TypeRep.Vector (vectorSpec)
11−import Test.TypeRep.VectorOpt (optimalVectorSpec)
12−
13−
14−main :: IO ()
15−main = hspec $ do
16− typeRepMapSpec
17− cMapSpec
18− vectorSpec
19− optimalVectorSpec
20− -- property
21− typeRepMapPropertySpec
1+{-# OPTIONS_GHC -F -pgmF tasty-discover -optF --tree-display #-}
test/Test/TypeRep/CMap.hs+7−9
@@ -1,18 +1,16 @@
1−module Test.TypeRep.CMap
2− ( cMapSpec
3− ) where
1+module Test.TypeRep.CMap where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.CMap (TypeRepMap, empty, insert, lookup, size)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.CMap
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−cMapSpec :: Spec
15−cMapSpec = describe "Containers Map TypeRep" $ do
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup = do
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (insert (Identity 'a') empty) `shouldBe` Just (Identity 'a')
@@ -37,7 +35,7 @@ mapOf10 = insert (Identity True)
3735 $ insert (Identity $ Just ())
3836 $ insert (Identity [()])
3937 $ insert (Identity ())
40− $ insert (Identity @String "aaa")
38+ $ insert (Identity "aaa")
4139 $ insert (Identity $ Just 'a')
4240 $ insert (Identity 'a')
4341 $ insert (Identity (11 :: Int)) empty
test/Test/TypeRep/CacheMap.hsnew file+50
@@ -0,0 +1,50 @@
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.hsnew file+110
@@ -0,0 +1,110 @@
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.hsdeleted−111
@@ -1,111 +0,0 @@
1−module Test.TypeRep.TypeRepMap
2− ( typeRepMapSpec
3− ) where
4−
5−import Prelude hiding (lookup)
6−
7−import Data.Functor.Identity (Identity (..))
8−import Data.List (sortBy)
9−import Data.Ord (comparing)
10−import Data.Typeable (cast)
11−import GHC.Exts (fromList, toList)
12−import Type.Reflection (SomeTypeRep(..), typeRep)
13−import Test.Hspec (Spec, describe, it, shouldBe, shouldMatchList, shouldSatisfy, expectationFailure)
14−
15−import Data.TMap (TMap, empty, insert, lookup, one, size, union, keys)
16−import Data.TypeRepMap.Internal (WrapTypeable (..))
17−
18−
19−-- Simple test for 'lookup', 'insert', 'size', 'keys', 'toList' functions.
20−typeRepMapSpec :: Spec
21−typeRepMapSpec = describe "TypeRepMap" $ do
22− describe "Lookup Test" $ do
23− it "returns the inserted element" $
24− lookup (fromList [WrapTypeable $ Identity 'a']) `shouldBe` Just 'a'
25− it "returns the second inserted value of the same type" $
26− lookup (fromList [WrapTypeable (Identity 'b'), WrapTypeable (Identity 'a')]) `shouldBe` Just 'b'
27−
28− describe "Size Test" $ do
29− it "is empty" $
30− size empty `shouldBe` 0
31− it "is of size 1 when 1 element inserted" $
32− size (one 'a') `shouldBe` 1
33− it "doesn't increase size when element of the same type is added" $
34− size (insert 'b' $ insert 'a' empty) `shouldBe` 1
35− it "returns 10 when 10 different types are inserted" $
36− size mapOf10 `shouldBe` 10
37−
38− describe "Union test" $ do
39− let m = fromList [WrapTypeable $ Identity 'a', WrapTypeable $ Identity True] `union`
40− fromList [WrapTypeable $ Identity 'b']
41− it "lookup works on union as expected" $ do
42− lookup m `shouldBe` Just 'a'
43− lookup m `shouldBe` Just True
44− lookup @Int m `shouldBe` Nothing
45−
46− describe "Keys Test" $ do
47− it "returns nothing on empty map" $
48− keys empty `shouldBe` []
49− it "returns the correct TypeRep" $
50− keys (one 'a') `shouldBe` [SomeTypeRep $ typeRep @Char]
51− it "returns the correct TypeReps for 10 different types" $
52− keys mapOf10 `shouldMatchList`
53− [ SomeTypeRep $ typeRep @Bool
54− , SomeTypeRep $ typeRep @[Bool]
55− , SomeTypeRep $ typeRep @(Maybe Bool)
56− , SomeTypeRep $ typeRep @(Maybe ())
57− , SomeTypeRep $ typeRep @[()]
58− , SomeTypeRep $ typeRep @()
59− , SomeTypeRep $ typeRep @String
60− , SomeTypeRep $ typeRep @(Maybe Char)
61− , SomeTypeRep $ typeRep @Char
62− , SomeTypeRep $ typeRep @Int
63− ]
64−
65− describe "ToList Test" $ do
66− it "returns nothing on empty map" $
67− toList empty `shouldSatisfy` null
68− it "returns correct result when 1 element is inserted" $
69− case toList (one 'a') of
70− [WrapTypeable (Identity x)] -> cast x `shouldBe` Just 'a'
71− _ -> expectationFailure "did not return exactly 1 result"
72− it "returns correct result when 10 elements are inserted" $ do
73− let
74− getTypeRep (WrapTypeable (Identity (_ :: a))) = SomeTypeRep $ typeRep @a
75− got = sortBy (comparing getTypeRep) (toList mapOf10)
76− expected = sortBy (comparing fst)
77− [ (SomeTypeRep $ typeRep @Bool,
78− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just True)
79− , (SomeTypeRep $ typeRep @[Bool],
80− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [True, False])
81− , (SomeTypeRep $ typeRep @(Maybe Bool),
82− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just True))
83− , (SomeTypeRep $ typeRep @(Maybe ()),
84− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just ()))
85− , (SomeTypeRep $ typeRep @[()],
86− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just [()])
87− , (SomeTypeRep $ typeRep @(),
88− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ())
89− , (SomeTypeRep $ typeRep @String,
90− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just ("aaa" :: String))
91− , (SomeTypeRep $ typeRep @(Maybe Char),
92− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (Just 'a'))
93− , (SomeTypeRep $ typeRep @Char,
94− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just 'a')
95− , (SomeTypeRep $ typeRep @Int,
96− \(WrapTypeable (Identity x)) -> cast x `shouldBe` Just (11 :: Int))
97− ]
98− length got `shouldBe` 10
99− sequence_ $ zipWith snd expected got
100−
101−mapOf10 :: TMap
102−mapOf10 = insert True
103− $ insert [True, False]
104− $ insert (Just True)
105− $ insert (Just ())
106− $ insert [()]
107− $ insert ()
108− $ insert @String "aaa"
109− $ insert (Just 'a')
110− $ insert 'a'
111− $ insert (11 :: Int) empty
test/Test/TypeRep/TypeRepMapProperty.hsdeleted−184
@@ -1,184 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE PolyKinds #-}
5−
6−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
7−
8−module Test.TypeRep.TypeRepMapProperty
9− ( typeRepMapPropertySpec
10− ) where
11−
12−import Prelude hiding (lookup)
13−
14−import Data.Proxy (Proxy (..))
15−import Data.Semigroup (Semigroup (..))
16−import GHC.Exts (fromList)
17−import GHC.TypeLits (Nat, SomeNat (..), someNatVal)
18−import Hedgehog (MonadGen, assert, forAll, (===))
19−import Test.Hspec (Arg, Expectation, Spec, SpecWith, describe, it)
20−import Test.Hspec.Hedgehog (hedgehog)
21−
22−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), delete, insert, invariantCheck,
23− lookup, member, union, generateOrderMapping, fromSortedList,
24− adjust, alter, intersection)
25−
26−import qualified Hedgehog.Gen as Gen
27−import qualified Hedgehog.Range as Range
28−
29−
30−typeRepMapPropertySpec :: Spec
31−typeRepMapPropertySpec = describe "TypeRepMap Property tests" $ do
32− describe "Map modification properties" $ do
33− insertLookupSpec
34− insertInsertSpec
35− deleteMemberSpec
36− insertInvariantSpec
37− deleteInvariantSpec
38− alterInsertSpec
39− alterDeleteSpec
40− alterAdjustSpec
41− alterModifySpec
42− intersectionSpec
43− describe "Internal helpers" $ do
44− generateOrderMappingInvariantSpec
45− describe "Instance Laws" $ do
46− semigroupAssocSpec
47− monoidIdentitySpec
48−
49−----------------------------------------------------------------------------
50−-- Map modification properties
51−----------------------------------------------------------------------------
52−
53−type Property = SpecWith (Arg Expectation)
54−
55−insertLookupSpec :: Property
56−insertLookupSpec = it "lookup k (insert k v m) == Just v" $ hedgehog $ do
57− m <- forAll genMap
58− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
59− lookup @n @IntProxy (insert proxy m) === Just proxy
60−
61−insertInsertSpec :: Property
62−insertInsertSpec = it "insert k b . insert k a == insert k b" $ hedgehog $ do
63− m <- forAll genMap
64− WrapTypeable a@(IntProxy (proxy :: Proxy n) i) <- forAll genTF
65− let b = IntProxy proxy (i + 1)
66− lookup @n @IntProxy (insert b $ insert a m) === Just b
67−
68−deleteMemberSpec :: Property
69−deleteMemberSpec = it "member k . delete k == False" $ hedgehog $ do
70− m <- forAll genMap
71− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
72− shouldInsert <- forAll Gen.bool
73−
74− if shouldInsert then
75− member @n (delete @n $ insert proxy m) === False
76− else
77− member @n (delete @n m) === False
78−
79−insertInvariantSpec :: Property
80−insertInvariantSpec = it "invariantCheck (insert k b) == True" $ hedgehog $ do
81− m <- forAll genMap
82− WrapTypeable a <- forAll genTF
83− assert $ invariantCheck (insert a m)
84−
85−deleteInvariantSpec :: Property
86−deleteInvariantSpec = it "invariantCheck (delete k b) == True" $ hedgehog $ do
87− m <- forAll genMap
88− WrapTypeable (_ :: IntProxy n) <- forAll genTF
89− assert $ invariantCheck (delete @n m)
90−
91−alterInsertSpec :: Property
92−alterInsertSpec = it "insert proxy m === alter (const (Just proxy)) m" $ hedgehog $ do
93− m <- forAll genMap
94− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
95− insert proxy m === alter (const (Just proxy)) m
96−
97−alterDeleteSpec :: Property
98−alterDeleteSpec = it "delete proxy m === alter (const Nothing) m" $ hedgehog $ do
99− WrapTypeable (proxy :: IntProxy n) <- forAll genTF
100− m <- insert proxy <$> forAll genMap
101− delete @n @IntProxy m === alter @n @IntProxy (const Nothing) m
102−
103−alterAdjustSpec :: Property
104−alterAdjustSpec = it "adjust f m == alter (fmap f) m" $ hedgehog $ do
105− m <- forAll genMap
106− WrapTypeable (_ :: IntProxy n) <- forAll genTF
107− let f (IntProxy p n) = IntProxy p (n * 10)
108− adjust @n @IntProxy f m === alter @n @IntProxy (fmap f) m
109−
110−alterModifySpec :: Property
111−alterModifySpec = it "lookup k (alter f) == f (lookup k m)" $ hedgehog $ do
112− m <- forAll genMap
113− WrapTypeable (_ :: IntProxy n) <- forAll genTF
114− randInt <- forAll (Gen.int Range.constantBounded)
115− -- Function with some interesting behaviour, which inserts, seletes and modifies
116− let f Nothing = Just (IntProxy Proxy randInt)
117− f (Just (IntProxy p n))
118− | even n = Nothing
119− | otherwise = Just $ IntProxy p (n * 10)
120− lookup @n @IntProxy (alter @n f m) === f (lookup @n @IntProxy m)
121−
122−intersectionSpec :: Property
123−intersectionSpec = it "m `intersection` (m `union` n) == m" $ hedgehog $ do
124− m <- forAll genMap
125− n <- forAll genMap
126− m `intersection` (m `union` n) === m
127−
128−----------------------------------------------------------------------------
129−-- Internal helpers
130−----------------------------------------------------------------------------
131−generateOrderMappingInvariantSpec :: Property
132−generateOrderMappingInvariantSpec =
133− it "fmap (fromSortedList [1 .. n] !!) (generateOrderMapping n) == [1 .. n]" $ hedgehog $ do
134− n <- forAll $ Gen.int (Range.linear 0 100)
135− fmap (fromSortedList [1 .. n] !!) (generateOrderMapping n) === [1 .. n]
136−
137−----------------------------------------------------------------------------
138−-- Semigroup and Monoid laws
139−----------------------------------------------------------------------------
140−
141−semigroupAssocSpec :: Property
142−semigroupAssocSpec = it "x <> (y <> z) == (x <> y) <> z" $ hedgehog $ do
143− x <- forAll genMap
144− y <- forAll genMap
145− z <- forAll genMap
146− (x <> (y <> z)) === ((x <> y) <> z)
147−
148−monoidIdentitySpec :: Property
149−monoidIdentitySpec = it "x <> mempty == mempty <> x == x" $ hedgehog $ do
150− x <- forAll genMap
151− x <> mempty === x
152− mempty <> x === x
153−
154−----------------------------------------------------------------------------
155−-- Generators
156−----------------------------------------------------------------------------
157−
158−data IntProxy (n :: Nat) = IntProxy (Proxy n) Int
159− deriving stock (Show, Eq)
160−
161−genMap :: MonadGen m => m (TypeRepMap IntProxy)
162−genMap = fromList <$> Gen.list (Range.linear 0 1000) genTF
163−
164−genTF :: MonadGen m => m (WrapTypeable IntProxy)
165−genTF = do
166− randNat :: Integer <- Gen.integral (Range.linear 0 10000)
167− randInt <- Gen.int Range.constantBounded
168− case someNatVal randNat of
169− Just (SomeNat proxyNat) -> pure $ WrapTypeable $ IntProxy proxyNat randInt
170− Nothing -> error "Invalid test generator"
171−
172−----------------------------------------------------------------------------
173−-- Helpers
174−----------------------------------------------------------------------------
175−#if __GLASGOW_HASKELL__ < 806
176−{- | We add an orphan Eq instance for old GHC versions just to make testing easier.
177−It's not a good idea to write such 'Eq' instance for 'TypeRepMap' itself because
178−it doesn't compare values so it's not true equality. But this should be enough
179−for tests.
180−-}
181−instance Eq (TypeRepMap f) where
182− TypeRepMap as1 bs1 _ _ == TypeRepMap as2 bs2 _ _ =
183− as1 == as2 && bs1 == bs2
184−#endif
test/Test/TypeRep/Vector.hs+6−8
@@ -1,18 +1,16 @@
1−module Test.TypeRep.Vector
2− ( vectorSpec
3− ) where
1+module Test.TypeRep.Vector where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.Vector (TF (..), fromList, lookup)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.Vector
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−vectorSpec :: Spec
15−vectorSpec = describe "Vector TypeRep" $
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup =
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (fromList [TF (Identity 'a')]) `shouldBe` Just (Identity 'a')
test/Test/TypeRep/VectorOpt.hs+6−8
@@ -1,18 +1,16 @@
1−module Test.TypeRep.VectorOpt
2− ( optimalVectorSpec
3− ) where
1+module Test.TypeRep.VectorOpt where
42
53import Prelude hiding (lookup)
64
75import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
96
10−import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
7+import Test.Tasty.Hspec
118
9+import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
1210
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−optimalVectorSpec :: Spec
15−optimalVectorSpec = describe "Optimal Vector TypeRep" $
11+-- Simple test for 'lookup', 'insert' and 'size' functions.
12+spec_insertLookup :: Spec
13+spec_insertLookup =
1614 describe "Lookup Test" $ do
1715 it "returns the inserted element" $
1816 lookup (fromList [TF $ Identity 'a']) `shouldBe` Just (Identity 'a')
typerep-extra-impls/Data/TypeRep/CMap.hs+3−16
@@ -1,12 +1,5 @@
1−{-# LANGUAGE PolyKinds #-}
2−
3−{- |
4−Copyright: (c) 2017-2020 Kowainik
5−SPDX-License-Identifier: MPL-2.0
6−Maintainer: Kowainik <xrom.xkov@gmail.com>
7−
8−TypeRepMap implementation based on @containers@ 'Map'.
9−-}
1+{-# LANGUAGE KindSignatures #-}
2+{-# LANGUAGE PolyKinds #-}
103
114module Data.TypeRep.CMap
125 ( TypeRepMap (..)
@@ -19,8 +12,6 @@ module Data.TypeRep.CMap
1912
2013import Prelude hiding (lookup)
2114
22−import Control.DeepSeq
23−import Data.Kind (Type)
2415import Data.Proxy (Proxy (..))
2516import Data.Typeable (TypeRep, Typeable, typeRep)
2617import GHC.Base (Any)
@@ -28,15 +19,11 @@ import Unsafe.Coerce (unsafeCoerce)
2819
2920import qualified Data.Map.Lazy as LMap
3021
31−
3222-- | Map-like data structure with types served as the keys.
33−newtype TypeRepMap (f :: k -> Type) = TypeRepMap
23+newtype TypeRepMap (f :: k -> *) = TypeRepMap
3424 { unMap :: LMap.Map TypeRep Any
3525 }
3626
37−instance NFData (TypeRepMap f) where
38− rnf x = rnf (keys x) `seq` ()
39−
4027-- | Empty structure.
4128empty :: TypeRepMap f
4229empty = TypeRepMap mempty
typerep-extra-impls/Data/TypeRep/OptimalVector.hs+8−20
@@ -1,16 +1,9 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE TypeFamilies #-}
6−
7−{- |
8−Copyright: (c) 2017-2020 Kowainik
9−SPDX-License-Identifier: MPL-2.0
10−Maintainer: Kowainik <xrom.xkov@gmail.com>
11−
12−TypeRepMap implementation based on the optimal Vector.
13−-}
1+{-# LANGUAGE BangPatterns #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE MagicHash #-}
5+{-# LANGUAGE PolyKinds #-}
6+{-# LANGUAGE TypeFamilies #-}
147
158module Data.TypeRep.OptimalVector
169 ( -- * Map type
@@ -30,7 +23,6 @@ module Data.TypeRep.OptimalVector
3023import Prelude hiding (lookup)
3124
3225import Control.Arrow ((&&&))
33−import Control.DeepSeq
3426import Data.Kind (Type)
3527import Data.Proxy (Proxy (..))
3628import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
@@ -43,16 +35,12 @@ import Unsafe.Coerce (unsafeCoerce)
4335import qualified Data.Vector as V
4436import qualified Data.Vector.Unboxed as Unboxed
4537
46−
4738data TypeRepMap (f :: k -> Type) = TypeRepMap
4839 { fingerprintAs :: Unboxed.Vector Word64
4940 , fingerprintBs :: Unboxed.Vector Word64
5041 , anys :: V.Vector Any
5142 }
5243
53−instance NFData (TypeRepMap f) where
54− rnf x = x `seq` ()
55−
5644fromAny :: Any -> f a
5745fromAny = unsafeCoerce
5846
@@ -61,7 +49,7 @@ empty :: TypeRepMap f
6149empty = TypeRepMap mempty mempty mempty
6250
6351-- | Inserts the value with its type as a key.
64−insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
52+insert :: forall a f . Typeable a => a -> TypeRepMap f -> TypeRepMap f
6553insert = undefined
6654
6755-- | Looks up the value at the type.
@@ -111,7 +99,7 @@ binarySearch (Fingerprint a b) fpAs fpBs =
11199data TF f where
112100 TF :: Typeable a => f a -> TF f
113101
114−fromF :: f a -> Proxy a
102+fromF :: Typeable a => f a -> Proxy a
115103fromF _ = Proxy
116104
117105fromList :: forall f . [TF f] -> TypeRepMap f
typerep-extra-impls/Data/TypeRep/Vector.hs+2−11
@@ -7,14 +7,6 @@
77{-# LANGUAGE PolyKinds #-}
88{-# LANGUAGE TypeFamilies #-}
99
10−{- |
11−Copyright: (c) 2017-2020 Kowainik
12−SPDX-License-Identifier: MPL-2.0
13−Maintainer: Kowainik <xrom.xkov@gmail.com>
14−
15−TypeRepMap implementation based on Vector.
16−-}
17−
1810module Data.TypeRep.Vector
1911 ( TypeRepVector (..)
2012 , TF (..)
@@ -41,7 +33,6 @@ import qualified Data.Vector.Generic as G
4133import qualified Data.Vector.Generic.Mutable as M
4234import qualified Data.Vector.Unboxed as Unboxed
4335
44−
4536data instance Unboxed.MVector s Fingerprint = MFingerprintVector (Unboxed.MVector s Word64) (Unboxed.MVector s Word64)
4637data instance Unboxed.Vector Fingerprint = FingerprintVector (Unboxed.Vector Word64) (Unboxed.Vector Word64)
4738
@@ -144,7 +135,7 @@ empty :: TypeRepVector f
144135empty = TypeRepVect mempty mempty
145136
146137-- | Inserts the value with its type as a key.
147−insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
138+insert :: forall a f . Typeable a => a -> TypeRepVector f -> TypeRepVector f
148139insert = undefined
149140
150141-- | Looks up the value at the type.
@@ -164,7 +155,7 @@ size = Unboxed.length . fingerprints
164155data TF f where
165156 TF :: Typeable a => f a -> TF f
166157
167−fromF :: f a -> Proxy a
158+fromF :: Typeable a => f a -> Proxy a
168159fromF _ = Proxy
169160
170161fromList :: forall f . [TF f] -> TypeRepVector f
typerep-map.cabal+54−69
@@ -1,6 +1,5 @@
1−cabal-version: 2.4
21name: typerep-map
3−version: 0.3.3.0
2+version: 0.2.0
43synopsis: Efficient implementation of a dependent map with types as keys
54description:
65 A dependent map from type representations to values of these types.
@@ -19,115 +18,101 @@ description:
1918
2019homepage: https://github.com/kowainik/typerep-map
2120bug-reports: https://github.com/kowainik/typerep-map/issues
22−license: MPL-2.0
21+license: MIT
2322license-file: LICENSE
24−author: Veronika Romashkina, Vladislav Zavialov, Dmitrii Kovanikov
25−maintainer: Kowainik <xrom.xkov@gmail.com>
26−copyright: 2017-2020 Kowainik
23+author: Kowainik, Vladislav Zavialov
24+maintainer: xrom.xkov@gmail.com
25+copyright: 2017-2018 Kowainik
2726category: Data, Data Structures, Types
2827build-type: Simple
2928extra-doc-files: README.md
3029 , CHANGELOG.md
30+cabal-version: 2.0
3131tested-with: GHC == 8.2.2
32− , GHC == 8.4.4
33− , GHC == 8.6.5
34− , GHC == 8.8.3
35− , GHC == 8.10.1
32+ , GHC == 8.4.3
3633
3734source-repository head
3835 type: git
3936 location: https://github.com/kowainik/typerep-map.git
4037
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−
6938library
70− import: common-options
7139 hs-source-dirs: src
7240 exposed-modules: Data.TMap
7341 Data.TypeRepMap
7442 Data.TypeRepMap.Internal
75−
76− build-depends: ghc-prim >= 0.5.1.1 && < 0.7
77− , primitive ^>= 0.7.0
78− , deepseq ^>= 1.4
43+ ghc-options: -Wall
44+ build-depends: base >= 4.9 && < 5
45+ , containers
46+ , ghc-prim
47+ , primitive >= 0.6.4
48+ default-extensions: OverloadedStrings
49+ RecordWildCards
50+ ScopedTypeVariables
51+ TypeApplications
52+ default-language: Haskell2010
7953
8054library typerep-extra-impls
81− import: common-options
8255 hs-source-dirs: typerep-extra-impls
8356 exposed-modules: Data.TypeRep.CMap
8457 Data.TypeRep.OptimalVector
8558 Data.TypeRep.Vector
59+ ghc-options: -Wall
60+ build-depends: base
61+ , containers
62+ , vector
63+ default-extensions: OverloadedStrings
64+ RecordWildCards
65+ ScopedTypeVariables
66+ TypeApplications
8667
87− build-depends: containers >= 0.5.10.2 && < 0.7
88− , vector ^>= 0.12.0.1
89− , deepseq ^>= 1.4
68+ default-language: Haskell2010
9069
9170test-suite typerep-map-test
92− import: common-options
9371 type: exitcode-stdio-1.0
9472 hs-source-dirs: test
95−
9673 main-is: Test.hs
9774 other-modules: Test.TypeRep.CMap
98− , Test.TypeRep.TypeRepMap
99− , Test.TypeRep.TypeRepMapProperty
75+ , Test.TypeRep.CacheMap
76+ , Test.TypeRep.MapProperty
10077 , Test.TypeRep.Vector
10178 , Test.TypeRep.VectorOpt
102−
103− build-depends: ghc-typelits-knownnat >= 0.4.2 && < 0.8
104− , hedgehog ^>= 1.0
105− , hspec ^>= 2.7.1
106− , hspec-hedgehog ^>= 0.0.1
79+ build-depends: base
80+ , ghc-typelits-knownnat
81+ , hedgehog
10782 , typerep-map
10883 , typerep-extra-impls
84+ , tasty
85+ , tasty-discover >= 4.1.1
86+ , tasty-hedgehog
87+ , tasty-hspec
88+ build-tool-depends: tasty-discover:tasty-discover
89+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N
90+ default-extensions: ScopedTypeVariables
91+ TypeApplications
10992
110− ghc-options: -threaded -rtsopts -with-rtsopts=-N
93+ default-language: Haskell2010
11194
11295benchmark typerep-map-benchmark
113− import: common-options
11496 type: exitcode-stdio-1.0
97+ default-language: Haskell2010
98+ ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
11599 hs-source-dirs: benchmark
116−
117100 main-is: Main.hs
118101 other-modules: CMap
119102 , CacheMap
120− , DMap
121− , Spec
122103 , Vector
123104 , OptimalVector
124−
125− build-depends: criterion >= 1.4.1.0 && < 1.6
126− , deepseq ^>= 1.4.3.0
127− , dependent-map >= 0.2.4.0 && < 0.5
128− , dependent-sum >= 0.5 && < 0.8
129− , ghc-typelits-knownnat >= 0.4.2 && < 0.8
105+ build-depends: base
106+ , criterion
107+ , deepseq
108+ , dependent-map
109+ , dependent-sum
110+ , ghc-typelits-knownnat
130111 , typerep-map
131112 , typerep-extra-impls
132−
133− ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
113+ default-extensions: OverloadedStrings
114+ RecordWildCards
115+ ScopedTypeVariables
116+ TypeApplications
117+ if impl(ghc >= 8.2.2)
118+ other-modules: DMap