on 92b6021 help sign in
33 files changed+668−1812
since splitsnapshots
unifiedsplit
.github/CODEOWNERS−1 .github/workflows/ci.yml−47 .gitignore−1 .stylish-haskell.yaml−59 .travis.yml+55−8 CHANGELOG.md+7−33 LICENSE+21−373 README.md+9−17 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+4−54 src/Data/TypeRepMap.hs+40−50 src/Data/TypeRepMap/Internal.hs+80−347 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+7−33
@@ -1,44 +1,18 @@
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
7+# 0.3.0
318
329* [#46](https://github.com/kowainik/typerep-map/issues/46):
3310 Make `Show` instance for `TypeRepMap` show keys.
3411 Add `keys` function.
3512* [#48](https://github.com/kowainik/typerep-map/issues/48):
3613 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`.
4014
41−## 0.2.0
15+# 0.2.0
4216
4317* [#43](https://github.com/kowainik/typerep-map/issues/43):
4418 Implement `IsList` instance for `TypeRepMap`.
@@ -48,9 +22,9 @@ The changelog is available [on GitHub][2].
4822 Add `map` function for `TMap`.
4923* Drop support for `ghc-8.0.2`.
5024
51−## 0.1.0
25+# 0.1.0
5226
5327* Initially created.
5428
5529[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
30+[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+9−17
@@ -1,21 +1,13 @@
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+![electricity](https://user-images.githubusercontent.com/8126674/44323413-788dd700-a484-11e8-842e-f224cfaa4206.png)
4+[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg)](https://hackage.haskell.org/package/typerep-map)
5+[![Build status](https://secure.travis-ci.org/kowainik/typerep-map.svg)](https://travis-ci.org/kowainik/typerep-map)
6+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
137
148`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.
159
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)
10+For the more details on the implementation see [this blog post](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step.html).
1911
2012## Usage example
2113
@@ -55,10 +47,10 @@ False
5547Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
5648on structure with size `10^4`:
5749
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 |
50+| | ghc-8.2.2 | ghc-8.4.3 |
51+|----------------|-----------|-----------|
52+| `DMap TypeRep` | 517.5 ns | 779.9 ns |
53+| `typerep-map` | 205.3 ns | 187.2 ns |
6254
6355 ghc-8.2.2 | ghc-8.4.3
6456:---------:|:-----------:
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+4−54
@@ -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,14 @@ module Data.TMap
3733 , delete
3834 , unionWith
3935 , union
40− , intersectionWith
41− , intersection
4236 , map
4337 , adjust
44− , alter
4538
4639 -- * Query
4740 , lookup
4841 , member
4942 , size
5043 , keys
51− , keysWith
52− , toListWith
5344 ) where
5445
5546import Prelude hiding (lookup, map)
@@ -57,7 +48,7 @@ import Prelude hiding (lookup, map)
5748import Data.Functor.Identity (Identity (..))
5849import Data.Typeable (Typeable)
5950import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
51+import Type.Reflection (SomeTypeRep)
6152
6253import qualified Data.TypeRepMap as F
6354
@@ -92,7 +83,6 @@ one x = coerce (F.one @a @Identity $ coerce x)
9283{- |
9384
9485Insert a value into a 'TMap'.
95−TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
9686
9787prop> size (insert v tm) >= size tm
9888prop> member @a (insert (x :: a) tm) == True
@@ -104,9 +94,6 @@ insert x = coerce (F.insert @a @Identity $ coerce x)
10494
10595{- | Delete a value from a 'TMap'.
10696
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−
11097prop> size (delete @a tm) <= size tm
11198prop> member @a (delete @a tm) == False
11299
@@ -123,10 +110,10 @@ delete = F.delete @a @Identity
123110{-# INLINE delete #-}
124111
125112-- | The union of two 'TMap's using a combining function.
126−unionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
113+unionWith :: (forall x. x -> x -> x) -> TMap -> TMap -> TMap
127114unionWith f = F.unionWith fId
128115 where
129− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
116+ fId :: forall y . Identity y -> Identity y -> Identity y
130117 fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
131118{-# INLINE unionWith #-}
132119
@@ -136,24 +123,6 @@ union :: TMap -> TMap -> TMap
136123union = F.union
137124{-# INLINE union #-}
138125
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−
157126{- | Lookup a value of the given type in a 'TMap'.
158127
159128>>> x = lookup $ insert (11 :: Int) empty
@@ -187,16 +156,6 @@ keys :: TMap -> [SomeTypeRep]
187156keys = F.keys
188157{-# INLINE keys #-}
189158
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−
200159-- | Map a function over the values.
201160map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202161map f = F.hoistWithKey (liftToIdentity f)
@@ -207,14 +166,5 @@ adjust :: Typeable a => (a -> a) -> TMap -> TMap
207166adjust f = F.adjust (liftToIdentity f)
208167{-# INLINE adjust #-}
209168
210−-- | Updates a value at a specific key, whether or not it exists.
211−-- This can be used to insert, delete, or update a value of a given type in the map.
212−alter :: Typeable a => (Maybe a -> Maybe a) -> TMap -> TMap
213−alter f = F.alter (liftF f)
214− where
215− liftF :: forall a. (Maybe a -> Maybe a) -> Maybe (Identity a) -> Maybe (Identity a)
216− liftF = coerce
217−{-# INLINE alter #-}
218−
219−liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
169+liftToIdentity :: forall a. Typeable a => (a -> a) -> Identity a -> Identity a
220170liftToIdentity = coerce
src/Data/TypeRepMap.hs+40−50
@@ -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()
@@ -57,22 +52,17 @@ module Data.TypeRepMap
5752 , insert
5853 , delete
5954 , adjust
60− , alter
6155 , hoist
6256 , hoistA
6357 , hoistWithKey
6458 , unionWith
6559 , union
66− , intersectionWith
67− , intersection
6860
6961 -- * Query
7062 , lookup
7163 , member
7264 , size
7365 , keys
74− , keysWith
75− , toListWith
7666
7767 -- * 'IsList'
7868 , WrapTypeable (..)
src/Data/TypeRepMap/Internal.hs+80−347
@@ -1,69 +1,50 @@
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)
26+import Control.Monad.ST (runST)
4227import Control.Monad.Zip (mzip)
4328import Data.Function (on)
29+import Data.IntMap.Strict (IntMap)
4430import Data.Kind (Type)
45−import Data.Type.Equality ((:~:) (..), TestEquality (..))
4631import 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(..))
32+import Data.Maybe (fromJust)
33+import Data.Primitive.Array (Array, freezeArray, indexArray, mapArray', readArray, sizeofArray,
34+ thawArray, writeArray)
35+import Data.Primitive.PrimArray (PrimArray, indexPrimArray, sizeofPrimArray)
36+import Data.Semigroup (Semigroup (..))
5237import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
5338import GHC.Exts (IsList (..), inline, sortWith)
5439import GHC.Fingerprint (Fingerprint (..))
55−#if WORD_SIZE_IN_BITS >= 64
5640import GHC.Prim (eqWord#, ltWord#)
57−#else
58−import GHC.IntWord64 (eqWord64#, ltWord64#)
59−#define eqWord eqWord64
60−#define ltWord ltWord64
61−#endif
6241import GHC.Word (Word64 (..))
6342import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeable)
6443import Type.Reflection.Unsafe (typeRepFingerprint)
6544import Unsafe.Coerce (unsafeCoerce)
6645
46+import qualified Data.IntMap.Strict as IM
47+import qualified Data.Map.Strict as Map
6748import qualified GHC.Exts as GHC (fromList, toList)
6849
6950{- |
@@ -96,9 +77,6 @@ data TypeRepMap (f :: k -> Type) =
9677 }
9778 -- ^ an unsafe constructor for 'TypeRepMap'
9879
99−instance NFData (TypeRepMap f) where
100− rnf x = rnf (keys x) `seq` ()
101−
10280-- | Shows only keys.
10381instance Show (TypeRepMap f) where
10482 show TypeRepMap{..} = "TypeRepMap [" ++ showKeys ++ "]"
@@ -118,31 +96,6 @@ instance Monoid (TypeRepMap f) where
11896 {-# INLINE mempty #-}
11997 {-# INLINE mappend #-}
12098
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−
14699-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
147100toFingerprints :: TypeRepMap f -> [Fingerprint]
148101toFingerprints TypeRepMap{..} =
@@ -169,49 +122,33 @@ prop> member @a (one (x :: f a)) == True
169122
170123-}
171124one :: 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)
125+one x = insert x empty
178126{-# INLINE one #-}
179127
180128{- |
181129
182130Insert a value into a 'TypeRepMap'.
183−TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
184131
185132prop> size (insert v tm) >= size tm
186133prop> member @a (insert (x :: f a) tm) == True
187134
188135-}
189136insert :: 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)}
137+insert x = fromTriples . addX . toTriples
195138 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
139+ tripleX :: (Fingerprint, Any, Any)
140+ tripleX@(fpX, _, _) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
141+
142+ addX :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
143+ addX l = tripleX : deleteByFst fpX l
202144{-# INLINE insert #-}
203145
204146-- Extract the kind of a type. We use it to work around lack of syntax for
205147-- inferred type variables (which are not subject to type applications).
206148type KindOf (a :: k) = k
207149
208−type ArgKindOf (f :: k -> l) = k
209−
210150{- | Delete a value from a 'TypeRepMap'.
211151
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−
215152prop> size (delete @a tm) <= size tm
216153prop> member @a (delete @a tm) == False
217154
@@ -224,60 +161,30 @@ False
224161True
225162-}
226163delete :: 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
164+delete = fromTriples . deleteByFst (typeFp @a) . toTriples
234165{-# INLINE delete #-}
235166
236−deleteFirst :: (a -> Bool) -> [a] -> [a]
237−deleteFirst _ [] = []
238−deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239−
240167{- |
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.
168+Update a value at a specific key with the result of the provided function. When
169+the key is not a member of the map, the original map is returned.
243170
244171>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
245172>>> lookup @String $ adjust (fmap (++ "ww")) trmap
246173Just (Identity "aww")
247174-}
248175adjust :: 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)}
176+adjust fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
177+ Nothing -> tr
178+ Just i -> tr {trAnys = changeAnyArr i (trAnys tr)}
273179 where
274− replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275− replaceAnyAt i v trAs = runST $ do
180+ changeAnyArr :: Int -> Array Any -> Array Any
181+ changeAnyArr i trAs = runST $ do
276182 let n = sizeofArray trAs
277183 mutArr <- thawArray trAs 0 n
278− writeArray mutArr i v
279− unsafeFreezeArray mutArr
280−{-# INLINE alter #-}
184+ a <- toAny . fun . fromAny <$> readArray mutArr i
185+ writeArray mutArr i a
186+ freezeArray mutArr 0 n
187+{-# INLINE adjust #-}
281188
282189{- | Map over the elements of a 'TypeRepMap'.
283190
@@ -311,89 +218,30 @@ hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
311218 withTr t = withTypeable t f
312219{-# INLINE hoistWithKey #-}
313220
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)
221+-- | The union of two 'TypeRepMap's using a combining function.
222+unionWith :: (forall x. f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
223+unionWith f m1 m2 = fromTriples
224+ $ toTripleList
225+ $ Map.unionWith combine
226+ (fromTripleList $ toTriples m1)
227+ (fromTripleList $ toTriples m2)
318228 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
229+ combine :: (Any, Any) -> (Any, Any) -> (Any, Any)
230+ combine (av, ak) (bv, _) = (toAny $ f (fromAny av) (fromAny bv), ak)
231+
232+ fromTripleList :: Ord a => [(a, b, c)] -> Map.Map a (b, c)
233+ fromTripleList = Map.fromList . map (\(a, b, c) -> (a, (b, c)))
234+
235+ toTripleList :: Map.Map a (b, c) -> [(a, b, c)]
236+ toTripleList = map (\(a, (b, c)) -> (a, b, c)) . Map.toList
347237{-# INLINE unionWith #-}
348238
349−-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
239+-- | The (left-biased) union of two 'TypeRepMap's. It prefers the first map when
350240-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
351241union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352242union = unionWith const
353243{-# INLINE union #-}
354244
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−
397245{- | Check if a value of the given type is present in a 'TypeRepMap'.
398246
399247>>> member @Char $ one (Identity 'a')
@@ -429,22 +277,9 @@ size = sizeofPrimArray . fingerprintAs
429277
430278-- | Return the list of 'SomeTypeRep' from the keys.
431279keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
280+keys TypeRepMap{..} = SomeTypeRep . anyToTypeRep <$> toList trKeys
433281{-# INLINE keys #-}
434282
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−
448283-- | Binary searched based on this article
449284-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450285-- with modification for our two-vector search case.
@@ -488,20 +323,8 @@ typeFp = typeRepFingerprint $ typeRep @a
488323toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
489324toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
490325
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)
326+deleteByFst :: Eq a => a -> [(a, b, c)] -> [(a, b, c)]
327+deleteByFst x = filter ((/= x) . fst3)
505328
506329nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
507330nubByFst = nubBy ((==) `on` fst3)
@@ -548,123 +371,33 @@ instance IsList (TypeRepMap f) where
548371 k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549372
550373 toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
374+ toList = map toWrapTypeable . toTriples
375+ where
376+ toWrapTypeable :: (Fingerprint, Any, Any) -> WrapTypeable f
377+ toWrapTypeable (_, an, k) = withTypeable (unsafeCoerce k) $ fromAny an
552378
553379calcFp :: forall a . Typeable a => Fingerprint
554380calcFp = typeRepFingerprint $ typeRep @a
555381
556382fromTriples :: [(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)
383+fromTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
561384 where
562385 (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
563− (fps, ans, ks) = unzip3 $ fromSortedList kvs
386+ (fps, ans, ks) = unzip3 $ fromSortedList $ sortWith fst3 $ nubByFst kvs
564387
565388----------------------------------------------------------------------------
566389-- Tree-like conversion
567390----------------------------------------------------------------------------
568391
569392fromSortedList :: 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
393+fromSortedList l = IM.elems $ fst $ go 0 0 mempty (IM.fromList $ zip [0..] l)
576394 where
577395 -- 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− ]
396+ go :: Int -> Int -> IntMap a -> IntMap a -> (IntMap a, Int)
397+ go i first result vector =
398+ if i >= IM.size vector
399+ then (result, first)
400+ else do
401+ let (newResult, newFirst) = go (2 * i + 1) first result vector
402+ let withCur = IM.insert i (fromJust $ IM.lookup newFirst vector) newResult
403+ 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.3.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