on 396eb06 help sign in
34 files changed+202−3012
since splitsnapshots
unifiedsplit
.github/CODEOWNERS−1 .github/workflows/ci.yml−47 .gitignore−3 .hlint.yaml−6 .stylish-haskell.yaml−59 .travis.yml+23−23 CHANGELOG.md+7−53 LICENSE+21−373 README.md+3−64 Setup.hs+3 appveyor.yml−35 benchmark/CMap.hs−77 benchmark/CacheMap.hs−79 benchmark/DMap.hs−89 benchmark/Main.hs+23−43 benchmark/OptimalVector.hs−60 benchmark/Spec.hs−64 benchmark/Vector.hs−63 src/Data/TMap.hs−220 src/Data/TypeRep/Map.hs+48 src/Data/TypeRepMap.hs−81 src/Data/TypeRepMap/Internal.hs−670 stack.yaml+9−4 test/Test.hs+1−21 test/Test/TypeRep/CMap.hs−43 test/Test/TypeRep/Map.hs+33 test/Test/TypeRep/TypeRepMap.hs−111 test/Test/TypeRep/TypeRepMapProperty.hs−184 test/Test/TypeRep/Vector.hs−20 test/Test/TypeRep/VectorOpt.hs−20 typerep-extra-impls/Data/TypeRep/CMap.hs−61 typerep-extra-impls/Data/TypeRep/OptimalVector.hs−127 typerep-extra-impls/Data/TypeRep/Vector.hs−197 typerep-map.cabal+31−114
.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−3
@@ -20,11 +20,8 @@ cabal.sandbox.config
2020cabal.config
2121cabal.project.local
2222.HTF/
23−.ghc.environment.*
24−
2523# Stack
2624.stack-work/
27−stack.yaml.lock
2825
2926### IDE/support
3027# Vim
.hlint.yamldeleted−6
@@ -1,6 +0,0 @@
1−- ignore: {name: "Use camelCase"}
2−
3−- arguments:
4− - -XTypeApplications
5−
6−- warn: {name: "Use explicit module export list"}
.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+23−23
@@ -1,28 +1,28 @@
1−sudo: true
2−language: generic
1+# Use new container infrastructure to enable caching
2+sudo: false
33
4−git:
5− depth: 5
4+# Choose a lightweight base image; we provide our own build tools.
5+language: c
66
7−cache:
8− directories:
9− - "$HOME/.stack"
10−
11−matrix:
12− include:
13− - env: GHC='8.8.3'
14− os: linux
15− - env: GHC='8.8.3'
16− os: osx
7+# GHC depends on GMP. You can add other dependencies here as well.
8+addons:
9+ apt:
10+ packages:
11+ - libgmp-dev
1712
18−install:
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
13+before_install:
14+# Download and unpack the stack executable
15+- mkdir -p ~/.local/bin
16+- export PATH=$HOME/.local/bin:$PATH
17+- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack'
2318
24−script:
25− - stack test --no-terminal
19+# This line does all of the work: installs GHC if necessary, builds the
20+# library, executables, and test suites, and runs the test suites.
21+# `--no-terminal works` around some quirks in Travis's terminal implementation.
22+script: stack --no-terminal --install-ghc test
2623
27−notifications:
28− email: false
24+# Caching so the next build will be fast too.
25+cache:
26+ directories:
27+ - $HOME/.stack
28+ - $HOME/build/vrom911/typerep-map/.stack-work
CHANGELOG.md+7−53
@@ -1,56 +1,10 @@
1−# Changelog
1+Change log
2+==========
23
3−`typerep-map` uses [PVP Versioning][1].
4−The changelog is available [on GitHub][2].
5−
6−## 0.3.3.0 — Apr 18, 2020
7−
8−* [#83](https://github.com/kowainik/typerep-map/issues/83):
9− Support GHC-8.10.
10−* [#80](https://github.com/kowainik/typerep-map/issues/80):
11− Build on 32-bit platforms.
12−* [#78](https://github.com/kowainik/typerep-map/issues/78):
13− Support GHC-8.8.3.
14−* [#63](https://github.com/kowainik/typerep-map/pull/63):
15− Add invariant check.
16−
17−## 0.3.2 — Mar 27, 2019
18−
19−* [#47](https://github.com/kowainik/typerep-map/issues/47):
20− Add `Eq` instance for `TypeRepMap` using `-XQuantifiedConstraints`.
21−* [#70](https://github.com/kowainik/typerep-map/issues/70):
22− Bump up to `dependent-sum-0.5`.
23−
24−## 0.3.1
25−
26−* [#64](https://github.com/kowainik/typerep-map/issues/64):
27− Fix segfault in `toList`.
28−* Support GHC 8.4.4 and 8.6.3.
29−
30−## 0.3.0
31−
32−* [#46](https://github.com/kowainik/typerep-map/issues/46):
33− Make `Show` instance for `TypeRepMap` show keys.
34− Add `keys` function.
35−* [#48](https://github.com/kowainik/typerep-map/issues/48):
36− Add `adjust` function for `TypeRepMap` and `TMap`.
37−* [#30](https://github.com/kowainik/typerep-map/issues/30):
38− Rewrite `fromSortedList` to use `Array` and `MutableArray`
39− instead of `IntMap`.
40−
41−## 0.2.0
42−
43−* [#43](https://github.com/kowainik/typerep-map/issues/43):
44− Implement `IsList` instance for `TypeRepMap`.
45− Add `hoistA` function.
46−* [#39](https://github.com/kowainik/typerep-map/issues/39):
47− Implement `hoistWithKey` function.
48− Add `map` function for `TMap`.
49−* Drop support for `ghc-8.0.2`.
50−
51−## 0.1.0
52−
53−* Initially created.
4+typerep-map uses [PVP Versioning][1].
5+The change log is available [on GitHub][2].
546
557[1]: https://pvp.haskell.org
56−[2]: https://github.com/kowainik/typerep-map/releases
8+[2]: https://github.com/vrom911/typerep-map/releases
9+# 0.1.0.0
10+* Initially created.
LICENSE+21−373
@@ -1,373 +1,21 @@
1−Mozilla Public License Version 2.0
2−==================================
3−
4−1. Definitions
5−--------------
6−
7−1.1. "Contributor"
8− means each individual or legal entity that creates, contributes to
9− the creation of, or owns Covered Software.
10−
11−1.2. "Contributor Version"
12− means the combination of the Contributions of others (if any) used
13− by a Contributor and that particular Contributor's Contribution.
14−
15−1.3. "Contribution"
16− means Covered Software of a particular Contributor.
17−
18−1.4. "Covered Software"
19− means Source Code Form to which the initial Contributor has attached
20− the notice in Exhibit A, the Executable Form of such Source Code
21− Form, and Modifications of such Source Code Form, in each case
22− including portions thereof.
23−
24−1.5. "Incompatible With Secondary Licenses"
25− means
26−
27− (a) that the initial Contributor has attached the notice described
28− in Exhibit B to the Covered Software; or
29−
30− (b) that the Covered Software was made available under the terms of
31− version 1.1 or earlier of the License, but not also under the
32− terms of a Secondary License.
33−
34−1.6. "Executable Form"
35− means any form of the work other than Source Code Form.
36−
37−1.7. "Larger Work"
38− means a work that combines Covered Software with other material, in
39− a separate file or files, that is not Covered Software.
40−
41−1.8. "License"
42− means this document.
43−
44−1.9. "Licensable"
45− means having the right to grant, to the maximum extent possible,
46− whether at the time of the initial grant or subsequently, any and
47− all of the rights conveyed by this License.
48−
49−1.10. "Modifications"
50− means any of the following:
51−
52− (a) any file in Source Code Form that results from an addition to,
53− deletion from, or modification of the contents of Covered
54− Software; or
55−
56− (b) any new file in Source Code Form that contains any Covered
57− Software.
58−
59−1.11. "Patent Claims" of a Contributor
60− means any patent claim(s), including without limitation, method,
61− process, and apparatus claims, in any patent Licensable by such
62− Contributor that would be infringed, but for the grant of the
63− License, by the making, using, selling, offering for sale, having
64− made, import, or transfer of either its Contributions or its
65− Contributor Version.
66−
67−1.12. "Secondary License"
68− means either the GNU General Public License, Version 2.0, the GNU
69− Lesser General Public License, Version 2.1, the GNU Affero General
70− Public License, Version 3.0, or any later versions of those
71− licenses.
72−
73−1.13. "Source Code Form"
74− means the form of the work preferred for making modifications.
75−
76−1.14. "You" (or "Your")
77− means an individual or a legal entity exercising rights under this
78− License. For legal entities, "You" includes any entity that
79− controls, is controlled by, or is under common control with You. For
80− purposes of this definition, "control" means (a) the power, direct
81− or indirect, to cause the direction or management of such entity,
82− whether by contract or otherwise, or (b) ownership of more than
83− fifty percent (50%) of the outstanding shares or beneficial
84− ownership of such entity.
85−
86−2. License Grants and Conditions
87−--------------------------------
88−
89−2.1. Grants
90−
91−Each Contributor hereby grants You a world-wide, royalty-free,
92−non-exclusive license:
93−
94−(a) under intellectual property rights (other than patent or trademark)
95− Licensable by such Contributor to use, reproduce, make available,
96− modify, display, perform, distribute, and otherwise exploit its
97− Contributions, either on an unmodified basis, with Modifications, or
98− as part of a Larger Work; and
99−
100−(b) under Patent Claims of such Contributor to make, use, sell, offer
101− for sale, have made, import, and otherwise transfer either its
102− Contributions or its Contributor Version.
103−
104−2.2. Effective Date
105−
106−The licenses granted in Section 2.1 with respect to any Contribution
107−become effective for each Contribution on the date the Contributor first
108−distributes such Contribution.
109−
110−2.3. Limitations on Grant Scope
111−
112−The licenses granted in this Section 2 are the only rights granted under
113−this License. No additional rights or licenses will be implied from the
114−distribution or licensing of Covered Software under this License.
115−Notwithstanding Section 2.1(b) above, no patent license is granted by a
116−Contributor:
117−
118−(a) for any code that a Contributor has removed from Covered Software;
119− or
120−
121−(b) for infringements caused by: (i) Your and any other third party's
122− modifications of Covered Software, or (ii) the combination of its
123− Contributions with other software (except as part of its Contributor
124− Version); or
125−
126−(c) under Patent Claims infringed by Covered Software in the absence of
127− its Contributions.
128−
129−This License does not grant any rights in the trademarks, service marks,
130−or logos of any Contributor (except as may be necessary to comply with
131−the notice requirements in Section 3.4).
132−
133−2.4. Subsequent Licenses
134−
135−No Contributor makes additional grants as a result of Your choice to
136−distribute the Covered Software under a subsequent version of this
137−License (see Section 10.2) or under the terms of a Secondary License (if
138−permitted under the terms of Section 3.3).
139−
140−2.5. Representation
141−
142−Each Contributor represents that the Contributor believes its
143−Contributions are its original creation(s) or it has sufficient rights
144−to grant the rights to its Contributions conveyed by this License.
145−
146−2.6. Fair Use
147−
148−This License is not intended to limit any rights You have under
149−applicable copyright doctrines of fair use, fair dealing, or other
150−equivalents.
151−
152−2.7. Conditions
153−
154−Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155−in Section 2.1.
156−
157−3. Responsibilities
158−-------------------
159−
160−3.1. Distribution of Source Form
161−
162−All distribution of Covered Software in Source Code Form, including any
163−Modifications that You create or to which You contribute, must be under
164−the terms of this License. You must inform recipients that the Source
165−Code Form of the Covered Software is governed by the terms of this
166−License, and how they can obtain a copy of this License. You may not
167−attempt to alter or restrict the recipients' rights in the Source Code
168−Form.
169−
170−3.2. Distribution of Executable Form
171−
172−If You distribute Covered Software in Executable Form then:
173−
174−(a) such Covered Software must also be made available in Source Code
175− Form, as described in Section 3.1, and You must inform recipients of
176− the Executable Form how they can obtain a copy of such Source Code
177− Form by reasonable means in a timely manner, at a charge no more
178− than the cost of distribution to the recipient; and
179−
180−(b) You may distribute such Executable Form under the terms of this
181− License, or sublicense it under different terms, provided that the
182− license for the Executable Form does not attempt to limit or alter
183− the recipients' rights in the Source Code Form under this License.
184−
185−3.3. Distribution of a Larger Work
186−
187−You may create and distribute a Larger Work under terms of Your choice,
188−provided that You also comply with the requirements of this License for
189−the Covered Software. If the Larger Work is a combination of Covered
190−Software with a work governed by one or more Secondary Licenses, and the
191−Covered Software is not Incompatible With Secondary Licenses, this
192−License permits You to additionally distribute such Covered Software
193−under the terms of such Secondary License(s), so that the recipient of
194−the Larger Work may, at their option, further distribute the Covered
195−Software under the terms of either this License or such Secondary
196−License(s).
197−
198−3.4. Notices
199−
200−You may not remove or alter the substance of any license notices
201−(including copyright notices, patent notices, disclaimers of warranty,
202−or limitations of liability) contained within the Source Code Form of
203−the Covered Software, except that You may alter any license notices to
204−the extent required to remedy known factual inaccuracies.
205−
206−3.5. Application of Additional Terms
207−
208−You may choose to offer, and to charge a fee for, warranty, support,
209−indemnity or liability obligations to one or more recipients of Covered
210−Software. However, You may do so only on Your own behalf, and not on
211−behalf of any Contributor. You must make it absolutely clear that any
212−such warranty, support, indemnity, or liability obligation is offered by
213−You alone, and You hereby agree to indemnify every Contributor for any
214−liability incurred by such Contributor as a result of warranty, support,
215−indemnity or liability terms You offer. You may include additional
216−disclaimers of warranty and limitations of liability specific to any
217−jurisdiction.
218−
219−4. Inability to Comply Due to Statute or Regulation
220−---------------------------------------------------
221−
222−If it is impossible for You to comply with any of the terms of this
223−License with respect to some or all of the Covered Software due to
224−statute, judicial order, or regulation then You must: (a) comply with
225−the terms of this License to the maximum extent possible; and (b)
226−describe the limitations and the code they affect. Such description must
227−be placed in a text file included with all distributions of the Covered
228−Software under this License. Except to the extent prohibited by statute
229−or regulation, such description must be sufficiently detailed for a
230−recipient of ordinary skill to be able to understand it.
231−
232−5. Termination
233−--------------
234−
235−5.1. The rights granted under this License will terminate automatically
236−if You fail to comply with any of its terms. However, if You become
237−compliant, then the rights granted under this License from a particular
238−Contributor are reinstated (a) provisionally, unless and until such
239−Contributor explicitly and finally terminates Your grants, and (b) on an
240−ongoing basis, if such Contributor fails to notify You of the
241−non-compliance by some reasonable means prior to 60 days after You have
242−come back into compliance. Moreover, Your grants from a particular
243−Contributor are reinstated on an ongoing basis if such Contributor
244−notifies You of the non-compliance by some reasonable means, this is the
245−first time You have received notice of non-compliance with this License
246−from such Contributor, and You become compliant prior to 30 days after
247−Your receipt of the notice.
248−
249−5.2. If You initiate litigation against any entity by asserting a patent
250−infringement claim (excluding declaratory judgment actions,
251−counter-claims, and cross-claims) alleging that a Contributor Version
252−directly or indirectly infringes any patent, then the rights granted to
253−You by any and all Contributors for the Covered Software under Section
254−2.1 of this License shall terminate.
255−
256−5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257−end user license agreements (excluding distributors and resellers) which
258−have been validly granted by You or Your distributors under this License
259−prior to termination shall survive termination.
260−
261−************************************************************************
262−* *
263−* 6. Disclaimer of Warranty *
264−* ------------------------- *
265−* *
266−* Covered Software is provided under this License on an "as is" *
267−* basis, without warranty of any kind, either expressed, implied, or *
268−* statutory, including, without limitation, warranties that the *
269−* Covered Software is free of defects, merchantable, fit for a *
270−* particular purpose or non-infringing. The entire risk as to the *
271−* quality and performance of the Covered Software is with You. *
272−* Should any Covered Software prove defective in any respect, You *
273−* (not any Contributor) assume the cost of any necessary servicing, *
274−* repair, or correction. This disclaimer of warranty constitutes an *
275−* essential part of this License. No use of any Covered Software is *
276−* authorized under this License except under this disclaimer. *
277−* *
278−************************************************************************
279−
280−************************************************************************
281−* *
282−* 7. Limitation of Liability *
283−* -------------------------- *
284−* *
285−* Under no circumstances and under no legal theory, whether tort *
286−* (including negligence), contract, or otherwise, shall any *
287−* Contributor, or anyone who distributes Covered Software as *
288−* permitted above, be liable to You for any direct, indirect, *
289−* special, incidental, or consequential damages of any character *
290−* including, without limitation, damages for lost profits, loss of *
291−* goodwill, work stoppage, computer failure or malfunction, or any *
292−* and all other commercial damages or losses, even if such party *
293−* shall have been informed of the possibility of such damages. This *
294−* limitation of liability shall not apply to liability for death or *
295−* personal injury resulting from such party's negligence to the *
296−* extent applicable law prohibits such limitation. Some *
297−* jurisdictions do not allow the exclusion or limitation of *
298−* incidental or consequential damages, so this exclusion and *
299−* limitation may not apply to You. *
300−* *
301−************************************************************************
302−
303−8. Litigation
304−-------------
305−
306−Any litigation relating to this License may be brought only in the
307−courts of a jurisdiction where the defendant maintains its principal
308−place of business and such litigation shall be governed by laws of that
309−jurisdiction, without reference to its conflict-of-law provisions.
310−Nothing in this Section shall prevent a party's ability to bring
311−cross-claims or counter-claims.
312−
313−9. Miscellaneous
314−----------------
315−
316−This License represents the complete agreement concerning the subject
317−matter hereof. If any provision of this License is held to be
318−unenforceable, such provision shall be reformed only to the extent
319−necessary to make it enforceable. Any law or regulation which provides
320−that the language of a contract shall be construed against the drafter
321−shall not be used to construe this License against a Contributor.
322−
323−10. Versions of the License
324−---------------------------
325−
326−10.1. New Versions
327−
328−Mozilla Foundation is the license steward. Except as provided in Section
329−10.3, no one other than the license steward has the right to modify or
330−publish new versions of this License. Each version will be given a
331−distinguishing version number.
332−
333−10.2. Effect of New Versions
334−
335−You may distribute the Covered Software under the terms of the version
336−of the License under which You originally received the Covered Software,
337−or under the terms of any subsequent version published by the license
338−steward.
339−
340−10.3. Modified Versions
341−
342−If you create software not governed by this License, and you want to
343−create a new license for such software, you may create and use a
344−modified version of this License if you rename the license and remove
345−any references to the name of the license steward (except to note that
346−such modified license differs from this License).
347−
348−10.4. Distributing Source Code Form that is Incompatible With Secondary
349−Licenses
350−
351−If You choose to distribute Source Code Form that is Incompatible With
352−Secondary Licenses under the terms of this version of the License, the
353−notice described in Exhibit B of this License must be attached.
354−
355−Exhibit A - Source Code Form License Notice
356−-------------------------------------------
357−
358− This Source Code Form is subject to the terms of the Mozilla Public
359− License, v. 2.0. If a copy of the MPL was not distributed with this
360− file, You can obtain one at http://mozilla.org/MPL/2.0/.
361−
362−If it is not possible or desirable to put the notice in a particular
363−file, then You may include the notice in a location (such as a LICENSE
364−file in a relevant directory) where a recipient would be likely to look
365−for such a notice.
366−
367−You may add additional accurate notices of copyright ownership.
368−
369−Exhibit B - "Incompatible With Secondary Licenses" Notice
370−---------------------------------------------------------
371−
372− This Source Code Form is "Incompatible With Secondary Licenses", as
373− defined by the Mozilla Public License, v. 2.0.
1+MIT License
2+
3+Copyright (c) 2017 Veronika Romashkina
4+
5+Permission is hereby granted, free of charge, to any person obtaining a copy
6+of this software and associated documentation files (the "Software"), to deal
7+in the Software without restriction, including without limitation the rights
8+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+copies of the Software, and to permit persons to whom the Software is
10+furnished to do so, subject to the following conditions:
11+
12+The above copyright notice and this permission notice shall be included in all
13+copies or substantial portions of the Software.
14+
15+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+SOFTWARE.
README.md+3−64
@@ -1,66 +1,5 @@
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−
13−
14−`typerep-map` introduces `TMap` and `TypeRepMap` — data structures like [`Map`](http://hackage.haskell.org/package/containers-0.6.0.1/docs/Data-Map-Lazy.html#t:Map), but where types serve as keys, and values have the types specified in the corresponding key spots.
15−
16−For the more details on the implementation see the following blog post:
17−
18−* [typerep-map step by step](https://kowainik.github.io/posts/2018-07-11-typerep-map-step-by-step)
19−
20−## Usage example
21−
22−```haskell
23−ghci> import Data.TMap
24−
25−ghci> tm = insert True $ one (42 :: Int)
26−
27−ghci> size tm
28−2
29−
30−ghci> res = lookup tm
31−
32−ghci> res :: Maybe Int
33−Just 42
34−
35−ghci> res :: Maybe Bool
36−Just True
37−
38−ghci> res :: Maybe String
39−Nothing
40−
41−ghci> lookup (insert "hello" tm) :: Maybe String
42−Just "hello"
43−
44−ghci> member @Int tm
45−True
46−
47−ghci> tm' = delete @Int tm
48−
49−ghci> member @Int tm'
50−False
51−```
52−
53−## Benchmarks
54−
55−Tables below contain comparision with `DMap TypeRep` of ten `lookup` operations
56−on structure with size `10^4`:
57−
58−| | ghc-8.2.2 | ghc-8.4.3 | ghc-8.8.3 | ghc-8.10.1 |
59−|----------------|-----------|-----------|-----------|------------|
60−| `DMap TypeRep` | 517.5 ns | 779.9 ns | 1.559 μs | 1.786 μs |
61−| `typerep-map` | 205.3 ns | 187.2 ns | 190.1 ns | 169.1 ns |
62−
63− ghc-8.2.2 | ghc-8.4.3
64−:---------:|:-----------:
65−![DMap 8.2.2](https://user-images.githubusercontent.com/4276606/42495129-c700f21e-8454-11e8-98b4-ba080259c712.png) | ![DMap 8.4.3](https://user-images.githubusercontent.com/4276606/42495168-ebb1d13c-8454-11e8-9d17-f6da29d2169a.png)
66−![TMap 8.2.2](https://user-images.githubusercontent.com/4276606/42494935-3a352d96-8454-11e8-985e-ebc77cc51ca0.png) | ![TMap 8.4.3](https://user-images.githubusercontent.com/4276606/42495147-d884bdf4-8454-11e8-887f-9815fd2b8d68.png)
3+[![Hackage](https://img.shields.io/hackage/v/typerep-map.svg)](https://hackage.haskell.org/package/typerep-map)
4+[![Build status](https://secure.travis-ci.org/vrom911/typerep-map.svg)](https://travis-ci.org/vrom911/typerep-map)
5+[![MIT license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/vrom911/typerep-map/blob/master/LICENSE)
Setup.hsnew file+3
@@ -0,0 +1,3 @@
1+import Distribution.Simple
2+
3+main = defaultMain
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.hsdeleted−77
@@ -1,77 +0,0 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE TypeOperators #-}
7−{-# LANGUAGE UndecidableInstances #-}
8−
9−module CMap
10− ( spec
11− ) where
12−
13−import Prelude hiding (lookup)
14−
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
17−import Data.Maybe (fromJust)
18−import Data.Proxy (Proxy (..))
19−import Data.Typeable (Typeable)
20−import GHC.TypeLits (type (+), KnownNat, Nat)
21−
22−import Data.TypeRep.CMap (TypeRepMap (..), empty, insert, lookup)
23−
24−import Spec (BenchSpec (..))
25−
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− )
50−tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
51− where
52− lp :: forall (a::Nat). Typeable a => Proxy a
53− lp = fromJust $ lookup tmap
54−
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
69−
70−buildBigMap
71− :: forall a . (KnownNat a)
72− => Int
73− -> Proxy (a :: Nat)
74− -> TypeRepMap (Proxy :: Nat -> Type)
75− -> TypeRepMap (Proxy :: Nat -> Type)
76−buildBigMap 1 x = insert x
77−buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/CacheMap.hsdeleted−79
@@ -1,79 +0,0 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE TypeOperators #-}
7−{-# LANGUAGE UndecidableInstances #-}
8−
9−module CacheMap
10− ( spec
11− ) where
12−
13−import Prelude hiding (lookup)
14−
15−import Criterion.Main (bench, env, nf, whnf)
16−import Data.Kind (Type)
17−import Data.Maybe (fromJust)
18−import Data.Proxy (Proxy (..))
19−import Data.Typeable (Typeable)
20−import GHC.Exts (fromList)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
22−
23−import Data.TypeRepMap.Internal (TypeRepMap (..), WrapTypeable (..), empty, insert, lookup)
24−
25−import Spec (BenchSpec (..))
26−
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− )
51−tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
52− where
53− lp :: forall (a :: Nat) . Typeable a => Proxy a
54− lp = fromJust $ lookup tmap
55−
56−inserts
57− :: forall a . (KnownNat a)
58− => TypeRepMap (Proxy :: Nat -> Type)
59− -> Int
60− -> Proxy (a :: Nat)
61− -> TypeRepMap (Proxy :: Nat -> Type)
62−inserts !c 0 _ = c
63−inserts !c n x = inserts
64− (insert x c)
65− (n-1)
66− (Proxy :: Proxy (a + 1))
67−
68−mkMap :: Int -> IO (TypeRepMap (Proxy :: Nat -> Type))
69−mkMap n = pure $ fromList $ buildBigMap n (Proxy :: Proxy 0) []
70−
71−
72−buildBigMap
73− :: forall a . (KnownNat a)
74− => Int
75− -> Proxy (a :: Nat)
76− -> [WrapTypeable (Proxy :: Nat -> Type)]
77− -> [WrapTypeable (Proxy :: Nat -> Type)]
78−buildBigMap 1 x = (WrapTypeable x :)
79−buildBigMap n x = (WrapTypeable x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
benchmark/DMap.hsdeleted−89
@@ -1,89 +0,0 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE InstanceSigs #-}
5−{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE TypeOperators #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−
10−module DMap
11− ( spec
12− ) where
13−
14−import Prelude hiding (lookup)
15−
16−import Control.DeepSeq (NFData (..))
17−import Criterion.Main (bench, env, nf, whnf)
18−import Data.Kind (Type)
19−import Data.Maybe (fromJust)
20−import Data.Proxy (Proxy (..))
21−import GHC.TypeLits (type (+), KnownNat, Nat)
22−import Type.Reflection (TypeRep, Typeable, typeRep)
23−import Type.Reflection.Unsafe (typeRepFingerprint)
24−
25−import Data.Dependent.Map (DMap, empty, insert, keys, lookup)
26−import Data.Some (Some (Some))
27−
28−import Spec (BenchSpec (..))
29−
30−
31−type TypeRepMap = DMap TypeRep
32−
33−spec :: BenchSpec
34−spec = BenchSpec
35− { benchLookup = Just $ \name ->
36− env mkBigMap $ \ ~(DMapNF bigMap) ->
37− bench name $ nf tenLookups bigMap
38− , benchInsertSmall = Just $ \name ->
39− bench name $ whnf (inserts empty 10) (Proxy @ 99999)
40− , benchInsertBig = Just $ \name ->
41− env mkBigMap $ \ ~(DMapNF bigMap) ->
42− bench name $ whnf (inserts bigMap 1) (Proxy @ 99999)
43− , benchUpdateSmall = Nothing -- Not implemented
44− , benchUpdateBig = Nothing -- Not implemented
45− }
46−
47−tenLookups
48− :: TypeRepMap (Proxy :: Nat -> Type)
49− -> ( Proxy 10, Proxy 20, Proxy 30, Proxy 40
50− , Proxy 50, Proxy 60, Proxy 70, Proxy 80
51− )
52−tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
53− where
54− lp :: forall (a :: Nat) . Typeable a => Proxy a
55− lp = fromJust $ lookup (typeRep @a) tmap
56−
57−inserts
58− :: forall a . (KnownNat a)
59− => TypeRepMap (Proxy :: Nat -> Type)
60− -> Int
61− -> Proxy (a :: Nat)
62− -> TypeRepMap (Proxy :: Nat -> Type)
63−inserts !c 0 _ = c
64−inserts !c n x = inserts
65− (insert (typeRep @ a) x c)
66− (n-1)
67− (Proxy :: Proxy (a+1))
68−
69−-- TypeRepMap of 10000 elements
70−mkBigMap :: IO (DMapNF (Proxy :: Nat -> Type))
71−mkBigMap = pure . DMapNF $ buildBigMap 10000 (Proxy :: Proxy 0) empty
72−
73−buildBigMap
74− :: forall a . (KnownNat a)
75− => Int
76− -> Proxy (a :: Nat)
77− -> TypeRepMap (Proxy :: Nat -> Type)
78− -> TypeRepMap (Proxy :: Nat -> Type)
79−buildBigMap 1 x = insert (typeRep @a) x
80−buildBigMap n x = insert (typeRep @a) x
81− . buildBigMap (n - 1) (Proxy @(a + 1))
82−
83−-- | Wrapper that provides NFData instance to the 'DMap' structure.
84−newtype DMapNF f = DMapNF (TypeRepMap f)
85−
86−instance NFData (DMapNF f) where
87− rnf :: DMapNF f -> ()
88− rnf (DMapNF x) =
89− rnf . map (\(Some t) -> typeRepFingerprint t) $ keys x
benchmark/Main.hs+23−43
@@ -1,52 +1,32 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE ExplicitForAll #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
23
3−module Main
4− ( main
5− ) where
4+import Criterion.Main (bench, defaultMain, whnf)
65
7−import Criterion.Main (bgroup, defaultMain)
6+import Prelude hiding (lookup)
87
9−import Spec (BenchSpec (..))
10−
11−import qualified CacheMap
12−import qualified CMap
13−#if ( __GLASGOW_HASKELL__ >= 802 )
14−import qualified DMap
15−#endif
16−import qualified OptimalVector as OptVec
8+import Data.Proxy (Proxy (..))
9+import Data.Typeable (Typeable)
1710
11+import Data.TypeRep.Map (TypeRepMap, empty, insert, lookup, size, dbgShowTree)
1812
1913main :: IO ()
2014main = do
21− let specs = [("CMap", CMap.spec)
22− ,("CacheMap", CacheMap.spec)
23−#if ( __GLASGOW_HASKELL__ >= 802 )
24− , ("DMap", DMap.spec)
25−#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− ]
15+ putStrLn $ "size: " ++ (show $ size bigMap)
16+ putStrLn $ "tree:\n" ++ (dbgShowTree bigMap)
4217 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− ]
18+ [ bench "lookup" $ whnf (lookup :: TypeRepMap -> Maybe Char) bigMap
19+ , bench "insert new" $ whnf (\x -> insert x bigMap) (111 :: Int)
20+ , bench "update old" $ whnf (\x -> insert x bigMap) 'b'
5221 ]
22+
23+-- TypeRepMap of 10000 elements
24+bigMap :: TypeRepMap
25+bigMap = buildBigMap 15 (Proxy :: Proxy Z) empty
26+
27+data Z
28+data S a
29+
30+buildBigMap :: forall a . Typeable a => Int -> Proxy a -> TypeRepMap -> TypeRepMap
31+buildBigMap 1 x = insert 'a' . insert x
32+buildBigMap n x = insert x . buildBigMap (n - 1) (Proxy :: Proxy (S a))
benchmark/OptimalVector.hsdeleted−60
@@ -1,60 +0,0 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE TypeOperators #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−
10−module OptimalVector
11− ( spec
12− ) where
13−
14−import Prelude hiding (lookup)
15−
16−import Criterion.Main (bench, env, nf)
17−import Data.Kind (Type)
18−import Data.Maybe (fromJust)
19−import Data.Proxy (Proxy (..))
20−import Data.Typeable (Typeable)
21−import GHC.TypeLits (type (+), KnownNat, Nat)
22−
23−import Data.TypeRep.OptimalVector (TF (..), TypeRepMap (..), fromList, lookup)
24−
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− )
44−tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
45− where
46− lp :: forall (a::Nat). Typeable a => Proxy a
47− lp = fromJust $ lookup tmap
48−
49−-- 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)]
59−buildBigMap 1 x = (TF x :)
60−buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
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.hsdeleted−63
@@ -1,63 +0,0 @@
1−{-# OPTIONS_GHC -fplugin GHC.TypeLits.KnownNat.Solver #-}
2−
3−{-# LANGUAGE DataKinds #-}
4−{-# LANGUAGE PolyKinds #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE TypeOperators #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−
10−module Vector
11− ( benchVector
12− , prepareBenchVector
13− ) where
14−
15−import Prelude hiding (lookup)
16−
17−import Control.DeepSeq (rnf)
18−import Control.Exception (evaluate)
19−import Criterion.Main (Benchmark, bench, bgroup, nf)
20−import Data.Kind (Type)
21−import Data.Maybe (fromJust)
22−import Data.Proxy (Proxy (..))
23−import Data.Typeable (Typeable)
24−import GHC.TypeLits (type (+), KnownNat, Nat)
25−
26−import Data.TypeRep.Vector (TF (..), TypeRepVector, fingerprints, fromList, lookup)
27−
28−
29−benchVector :: Benchmark
30−benchVector = bgroup "vector"
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− ]
35−
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− )
41−tenLookups tmap = (lp, lp, lp, lp, lp, lp, lp, lp)
42− where
43− lp :: forall (a::Nat). Typeable a => Proxy a
44− lp = fromJust $ lookup tmap
45−
46−-- TypeRepMap of 10000 elements
47−bigMap :: TypeRepVector (Proxy :: Nat -> Type)
48−bigMap = fromList $ buildBigMap 10000 (Proxy :: Proxy 0) []
49−
50−buildBigMap
51− :: forall a . (KnownNat a)
52− => Int
53− -> Proxy (a :: Nat)
54− -> [TF (Proxy :: Nat -> Type)]
55− -> [TF (Proxy :: Nat -> Type)]
56−buildBigMap 1 x = (TF x :)
57−buildBigMap n x = (TF x :) . buildBigMap (n - 1) (Proxy :: Proxy (a + 1))
58−
59−rknf :: TypeRepVector f -> ()
60−rknf = rnf . fingerprints
61−
62−prepareBenchVector :: IO ()
63−prepareBenchVector = evaluate (rknf bigMap)
src/Data/TMap.hsdeleted−220
@@ -1,220 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE Rank2Types #-}
3−
4−{- |
5−Copyright: (c) 2017-2020 Kowainik
6−SPDX-License-Identifier: MPL-2.0
7−Maintainer: Kowainik <xrom.xkov@gmail.com>
8−
9−'TMap' is a heterogeneous data structure similar in its essence to
10−'Data.Map.Map' with types as keys, where each value has the type of its key.
11−
12−Here is an example of a 'TMap' with a comparison to 'Data.Map.Map':
13−
14−@
15− 'Data.Map.Map' 'Prelude.String' 'Prelude.String' 'TMap'
16−-------------------- -----------------
17− \"Int\" -> \"5\" 'Prelude.Int' -> 5
18− \"Bool\" -> \"True\" 'Prelude.Bool' -> 'Prelude.True'
19− \"Char\" -> \"\'x\'\" 'Prelude.Char' -> \'x\'
20−@
21−
22−The runtime representation of 'TMap' is an array, not a tree. This makes
23−'lookup' significantly more efficient.
24−
25−-}
26−
27−module Data.TMap
28− ( -- * Map type
29− TMap
30−
31− -- * Construction
32− , empty
33− , one
34−
35− -- * Modification
36− , insert
37− , delete
38− , unionWith
39− , union
40− , intersectionWith
41− , intersection
42− , map
43− , adjust
44− , alter
45−
46− -- * Query
47− , lookup
48− , member
49− , size
50− , keys
51− , keysWith
52− , toListWith
53− ) where
54−
55−import Prelude hiding (lookup, map)
56−
57−import Data.Functor.Identity (Identity (..))
58−import Data.Typeable (Typeable)
59−import GHC.Exts (coerce)
60−import Type.Reflection (SomeTypeRep, TypeRep)
61−
62−import qualified Data.TypeRepMap as F
63−
64−-- | 'TMap' is a special case of 'F.TypeRepMap' when the interpretation is
65−-- 'Identity'.
66−type TMap = F.TypeRepMap Identity
67−
68−{- |
69−
70−A 'TMap' with no values stored in it.
71−
72−prop> size empty == 0
73−prop> member @a empty == False
74−
75−-}
76−empty :: TMap
77−empty = F.empty
78−{-# INLINE empty #-}
79−
80−{- |
81−
82−Construct a 'TMap' with a single element.
83−
84−prop> size (one x) == 1
85−prop> member @a (one (x :: a)) == True
86−
87−-}
88−one :: forall a . Typeable a => a -> TMap
89−one x = coerce (F.one @a @Identity $ coerce x)
90−{-# INLINE one #-}
91−
92−{- |
93−
94−Insert a value into a 'TMap'.
95−TMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
96−
97−prop> size (insert v tm) >= size tm
98−prop> member @a (insert (x :: a) tm) == True
99−
100−-}
101−insert :: forall a . Typeable a => a -> TMap -> TMap
102−insert x = coerce (F.insert @a @Identity $ coerce x)
103−{-# INLINE insert #-}
104−
105−{- | Delete a value from a 'TMap'.
106−
107−TMap optimizes for fast reads rather than modifications, as a trade-off deletes are @O(n)@,
108−with an @O(log(n))@ optimization for when the element is already missing.
109−
110−prop> size (delete @a tm) <= size tm
111−prop> member @a (delete @a tm) == False
112−
113−>>> tm = delete @Bool $ insert True $ one 'a'
114−>>> size tm
115−1
116−>>> member @Bool tm
117−False
118−>>> member @Char tm
119−True
120−-}
121−delete :: forall a . Typeable a => TMap -> TMap
122−delete = F.delete @a @Identity
123−{-# INLINE delete #-}
124−
125−-- | The union of two 'TMap's using a combining function.
126−unionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
127−unionWith f = F.unionWith fId
128− where
129− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
130− fId y1 y2 = Identity $ f (coerce y1) (coerce y2)
131−{-# INLINE unionWith #-}
132−
133−-- | The (left-biased) union of two 'TMap's. It prefers the first map when
134−-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
135−union :: TMap -> TMap -> TMap
136−union = F.union
137−{-# INLINE union #-}
138−
139−-- | The intersection of two 'TMap's using a combining function.
140−--
141−-- @O(n + m)@
142−intersectionWith :: (forall x. Typeable x => x -> x -> x) -> TMap -> TMap -> TMap
143−intersectionWith f = F.intersectionWith fId
144− where
145− fId :: forall y . Typeable y => Identity y -> Identity y -> Identity y
146− fId y1 y2 = f (coerce y1) (coerce y2)
147−{-# INLINE intersectionWith #-}
148−
149−-- | The intersection of two 'TMap's.
150−-- It keeps all values from the first map whose keys are present in the second.
151−--
152−-- @O(n + m)@
153−intersection :: TMap -> TMap -> TMap
154−intersection = F.intersection
155−{-# INLINE intersection #-}
156−
157−{- | Lookup a value of the given type in a 'TMap'.
158−
159−>>> x = lookup $ insert (11 :: Int) empty
160−>>> x :: Maybe Int
161−Just 11
162−>>> x :: Maybe ()
163−Nothing
164−-}
165−lookup :: forall a. Typeable a => TMap -> Maybe a
166−lookup = coerce (F.lookup @a @Identity)
167−{-# INLINE lookup #-}
168−
169−{- | Check if a value of the given type is present in a 'TMap'.
170−
171−>>> member @Char $ one 'a'
172−True
173−>>> member @Bool $ one 'a'
174−False
175−-}
176−member :: forall a . Typeable a => TMap -> Bool
177−member = F.member @a @Identity
178−{-# INLINE member #-}
179−
180−-- | Get the amount of elements in a 'TMap'.
181−size :: TMap -> Int
182−size = F.size
183−{-# INLINE size #-}
184−
185−-- | Returns the list of 'SomeTypeRep's from keys.
186−keys :: TMap -> [SomeTypeRep]
187−keys = F.keys
188−{-# INLINE keys #-}
189−
190−-- | Return the list of keys by wrapping them with a user-provided function.
191−keysWith :: (forall a. TypeRep a -> r) -> TMap -> [r]
192−keysWith = F.keysWith
193−{-# INLINE keysWith #-}
194−
195−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
196−toListWith :: (forall a. Typeable a => a -> r) -> TMap -> [r]
197−toListWith = F.toListWith
198−{-# INLINE toListWith #-}
199−
200−-- | Map a function over the values.
201−map :: (forall a. Typeable a => a -> a) -> TMap -> TMap
202−map f = F.hoistWithKey (liftToIdentity f)
203−{-# INLINE map #-}
204−
205−-- | Update a value with the result of the provided function.
206−adjust :: Typeable a => (a -> a) -> TMap -> TMap
207−adjust f = F.adjust (liftToIdentity f)
208−{-# INLINE adjust #-}
209−
210−-- | Updates a value at a specific key, whether or not it exists.
211−-- This can be used to insert, delete, or update a value of a given type in the map.
212−alter :: Typeable a => (Maybe a -> Maybe a) -> TMap -> TMap
213−alter f = F.alter (liftF f)
214− where
215− liftF :: forall a. (Maybe a -> Maybe a) -> Maybe (Identity a) -> Maybe (Identity a)
216− liftF = coerce
217−{-# INLINE alter #-}
218−
219−liftToIdentity :: forall a. (a -> a) -> Identity a -> Identity a
220−liftToIdentity = coerce
src/Data/TypeRep/Map.hsnew file+48
@@ -0,0 +1,48 @@
1+{-# LANGUAGE ExplicitForAll #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+
4+module Data.TypeRep.Map
5+ ( TypeRepMap (..)
6+ , empty
7+ , insert
8+ , lookup
9+ , size
10+ , dbgShowTree
11+ ) where
12+
13+import Prelude hiding (lookup)
14+
15+import Data.Proxy (Proxy (..))
16+import Data.Typeable (TypeRep, Typeable, typeOf, typeRep)
17+import GHC.Base (Any)
18+import Unsafe.Coerce (unsafeCoerce)
19+
20+import qualified Data.Map.Lazy as LMap
21+
22+-- | Map-like data structure with types served as the keys.
23+newtype TypeRepMap = TypeRepMap
24+ { unMap :: LMap.Map TypeRep Any
25+ }
26+
27+-- | Empty structure.
28+empty :: TypeRepMap
29+empty = TypeRepMap mempty
30+
31+-- | Inserts the value with its type as a key.
32+insert :: Typeable a => a -> TypeRepMap -> TypeRepMap
33+insert val = TypeRepMap . LMap.insert (typeOf val) (unsafeCoerce val) . unMap
34+
35+-- | Looks up the value at the type.
36+-- >>> let x = lookup $ insert (11 :: Int) empty
37+-- >>> x :: Maybe Int
38+-- Just 11
39+-- >>> x :: Maybe ()
40+-- Nothing
41+lookup :: forall a . Typeable a => TypeRepMap -> Maybe a
42+lookup = fmap unsafeCoerce . LMap.lookup (typeRep (Proxy :: Proxy a)) . unMap
43+
44+size :: TypeRepMap -> Int
45+size = LMap.size . unMap
46+
47+dbgShowTree :: TypeRepMap -> String
48+dbgShowTree = LMap.showTreeWith (\k _ -> show k) False True . unMap
src/Data/TypeRepMap.hsdeleted−81
@@ -1,81 +0,0 @@
1−{-# LANGUAGE NoImplicitPrelude #-}
2−
3−{- |
4−Copyright: (c) 2017-2020 Kowainik
5−SPDX-License-Identifier: MPL-2.0
6−Maintainer: Kowainik <xrom.xkov@gmail.com>
7−
8−A version of 'Data.TMap.TMap' parametrized by an interpretation @f@. This
9−sort of parametrization may be familiar to users of @vinyl@ records.
10−
11−@'TypeRepMap' f@ is a more efficient replacement for @DMap
12−'Type.Reflection.TypeRep' f@ (where @DMap@ is from the @dependent-map@
13−package).
14−
15−Here is an example of using 'Prelude.Maybe' as an interpretation, with a
16−comparison to 'Data.TMap.TMap':
17−
18−@
19− 'Data.TMap.TMap' 'TypeRepMap' 'Prelude.Maybe'
20−-------------- -------------------
21− Int -> 5 Int -> Just 5
22− Bool -> True Bool -> Nothing
23− Char -> \'x\' Char -> Just \'x\'
24−@
25−
26−In fact, a 'Data.TMap.TMap' is defined as 'TypeRepMap'
27−'Data.Functor.Identity'.
28−
29−Since 'Type.Reflection.TypeRep' is poly-kinded, the interpretation can use
30−any kind for the keys. For instance, we can use the 'GHC.TypeLits.Symbol'
31−kind to use 'TypeRepMap' as an extensible record:
32−
33−@
34−newtype Field name = F (FType name)
35−
36−type family FType (name :: Symbol) :: Type
37−type instance FType "radius" = Double
38−type instance FType "border-color" = RGB
39−type instance FType "border-width" = Double
40−
41− 'TypeRepMap' Field
42−--------------------------------------
43− "radius" -> F 5.7
44− "border-color" -> F (rgb 148 0 211)
45− "border-width" -> F 0.5
46−@
47−--}
48−module Data.TypeRepMap
49− ( -- * Map type
50− TypeRepMap()
51−
52− -- * Construction
53− , empty
54− , one
55−
56− -- * Modification
57− , insert
58− , delete
59− , adjust
60− , alter
61− , hoist
62− , hoistA
63− , hoistWithKey
64− , unionWith
65− , union
66− , intersectionWith
67− , intersection
68−
69− -- * Query
70− , lookup
71− , member
72− , size
73− , keys
74− , keysWith
75− , toListWith
76−
77− -- * 'IsList'
78− , WrapTypeable (..)
79− ) where
80−
81−import Data.TypeRepMap.Internal
src/Data/TypeRepMap/Internal.hsdeleted−670
@@ -1,670 +0,0 @@
1−{-# OPTIONS_GHC -Wno-missing-export-lists #-}
2−
3−{-# LANGUAGE AllowAmbiguousTypes #-}
4−{-# LANGUAGE BangPatterns #-}
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE DataKinds #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE InstanceSigs #-}
9−{-# LANGUAGE MagicHash #-}
10−{-# LANGUAGE PolyKinds #-}
11−{-# LANGUAGE Rank2Types #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeInType #-}
14−{-# LANGUAGE ViewPatterns #-}
15−
16−#if __GLASGOW_HASKELL__ >= 806
17−{-# LANGUAGE QuantifiedConstraints #-}
18−#endif
19−
20−-- {-# OPTIONS_GHC -ddump-simpl -dsuppress-idinfo -dsuppress-coercions -dsuppress-type-applications -dsuppress-uniques -dsuppress-module-prefixes #-}
21−
22−{- |
23−Copyright: (c) 2017-2020 Kowainik
24−SPDX-License-Identifier: MPL-2.0
25−Maintainer: Kowainik <xrom.xkov@gmail.com>
26−
27−Internal API for 'TypeRepMap' and operations on it. The functions here do
28−not have any stability guarantees and can change between minor versions.
29−
30−If you need to use this module for purposes other than tests,
31−create an issue.
32−-}
33−
34−#include "MachDeps.h"
35−
36−module Data.TypeRepMap.Internal where
37−
38−import Prelude hiding (lookup)
39−
40−import Control.DeepSeq
41−import Control.Monad.ST (ST, runST)
42−import Control.Monad.Zip (mzip)
43−import Data.Function (on)
44−import Data.Kind (Type)
45−import Data.Type.Equality ((:~:) (..), TestEquality (..))
46−import Data.List (intercalate, nubBy)
47−import Data.Maybe (fromMaybe)
48−import Data.Primitive.Array (Array, MutableArray, indexArray, mapArray', sizeofArray,
49− thawArray, unsafeFreezeArray, writeArray)
50−import Data.Primitive.PrimArray (writePrimArray, newPrimArray, unsafeFreezePrimArray, primArrayToList, MutablePrimArray, primArrayFromListN, PrimArray, indexPrimArray, sizeofPrimArray)
51−import Data.Semigroup (Semigroup (..), All(..))
52−import GHC.Base (Any, Int (..), Int#, (*#), (+#), (<#))
53−import GHC.Exts (IsList (..), inline, sortWith)
54−import GHC.Fingerprint (Fingerprint (..))
55−#if WORD_SIZE_IN_BITS >= 64
56−import GHC.Prim (eqWord#, ltWord#)
57−#else
58−import GHC.IntWord64 (eqWord64#, ltWord64#)
59−#define eqWord eqWord64
60−#define ltWord ltWord64
61−#endif
62−import GHC.Word (Word64 (..))
63−import Type.Reflection (SomeTypeRep (..), TypeRep, Typeable, typeRep, withTypeable)
64−import Type.Reflection.Unsafe (typeRepFingerprint)
65−import Unsafe.Coerce (unsafeCoerce)
66−
67−import qualified GHC.Exts as GHC (fromList, toList)
68−
69−{- |
70−
71−'TypeRepMap' is a heterogeneous data structure similar in its essence to
72−'Data.Map.Map' with types as keys, where each value has the type of its key. In
73−addition to that, each value is wrapped in an interpretation @f@.
74−
75−Here is an example of using 'Prelude.Maybe' as an interpretation, with a
76−comparison to 'Data.Map.Map':
77−
78−@
79− 'Data.Map.Map' 'Prelude.String' ('Prelude.Maybe' 'Prelude.String') 'TypeRepMap' 'Prelude.Maybe'
80−--------------------------- ---------------------
81− \"Int\" -> Just \"5\" 'Prelude.Int' -> Just 5
82− \"Bool\" -> Just \"True\" 'Prelude.Bool' -> Just 'Prelude.True'
83− \"Char\" -> Nothing 'Prelude.Char' -> Nothing
84−@
85−
86−The runtime representation of 'TypeRepMap' is an array, not a tree. This makes
87−'lookup' significantly more efficient.
88−
89−-}
90−data TypeRepMap (f :: k -> Type) =
91− TypeRepMap
92− { fingerprintAs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ first components of key fingerprints
93− , fingerprintBs :: {-# UNPACK #-} !(PrimArray Word64) -- ^ second components of key fingerprints
94− , trAnys :: {-# UNPACK #-} !(Array Any) -- ^ values stored in the map
95− , trKeys :: {-# UNPACK #-} !(Array Any) -- ^ typerep keys
96− }
97− -- ^ an unsafe constructor for 'TypeRepMap'
98−
99−instance NFData (TypeRepMap f) where
100− rnf x = rnf (keys x) `seq` ()
101−
102−-- | Shows only keys.
103−instance Show (TypeRepMap f) where
104− show TypeRepMap{..} = "TypeRepMap [" ++ showKeys ++ "]"
105− where
106− showKeys :: String
107− showKeys = intercalate ", " $ toList $ mapArray' (show . anyToTypeRep) trKeys
108−
109−-- | Uses 'union' to combine 'TypeRepMap's.
110−instance Semigroup (TypeRepMap f) where
111− (<>) :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
112− (<>) = union
113− {-# INLINE (<>) #-}
114−
115−instance Monoid (TypeRepMap f) where
116− mempty = TypeRepMap mempty mempty mempty mempty
117− mappend = (<>)
118− {-# INLINE mempty #-}
119− {-# INLINE mappend #-}
120−
121−#if __GLASGOW_HASKELL__ >= 806
122−instance (forall a. Typeable a => Eq (f a)) => Eq (TypeRepMap f) where
123− tm1 == tm2 = size tm1 == size tm2 && go 0
124− where
125− go :: Int -> Bool
126− go i
127− | i == size tm1 = True
128− | otherwise = case testEquality tr1i tr2i of
129− Nothing -> False
130− Just Refl -> repEq tr1i (fromAny tv1i) (fromAny tv2i) && go (i + 1)
131− where
132− tr1i :: TypeRep x
133− tr1i = anyToTypeRep $ indexArray (trKeys tm1) i
134−
135− tr2i :: TypeRep y
136− tr2i = anyToTypeRep $ indexArray (trKeys tm2) i
137−
138− tv1i, tv2i :: Any
139− tv1i = indexArray (trAnys tm1) i
140− tv2i = indexArray (trAnys tm2) i
141−
142− repEq :: TypeRep x -> f x -> f x -> Bool
143− repEq tr = withTypeable tr (==)
144−#endif
145−
146−-- | Returns the list of 'Fingerprint's from 'TypeRepMap'.
147−toFingerprints :: TypeRepMap f -> [Fingerprint]
148−toFingerprints TypeRepMap{..} =
149− zipWith Fingerprint (GHC.toList fingerprintAs) (GHC.toList fingerprintBs)
150−
151−{- |
152−
153−A 'TypeRepMap' with no values stored in it.
154−
155−prop> size empty == 0
156−prop> member @a empty == False
157−
158−-}
159−empty :: TypeRepMap f
160−empty = mempty
161−{-# INLINE empty #-}
162−
163−{- |
164−
165−Construct a 'TypeRepMap' with a single element.
166−
167−prop> size (one x) == 1
168−prop> member @a (one (x :: f a)) == True
169−
170−-}
171−one :: forall a f . Typeable a => f a -> TypeRepMap f
172−one x = TypeRepMap (primArrayFromListN 1 [fa])
173− (primArrayFromListN 1 [fb])
174− (pure @Array v)
175− (pure @Array k)
176− where
177− (Fingerprint fa fb, v, k) = (calcFp @a, toAny x, unsafeCoerce $ typeRep @a)
178−{-# INLINE one #-}
179−
180−{- |
181−
182−Insert a value into a 'TypeRepMap'.
183−TypeRepMap optimizes for fast reads rather than inserts, as a trade-off inserts are @O(n)@.
184−
185−prop> size (insert v tm) >= size tm
186−prop> member @a (insert (x :: f a) tm) == True
187−
188−-}
189−insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
190−insert x m
191− | size m == 0 = one x
192− | otherwise = case cachedBinarySearch (typeFp @a) (fingerprintAs m) (fingerprintBs m) of
193− Nothing -> union m $ one x
194− Just i -> m {trAnys = changeAnyArr i (trAnys m)}
195− where
196− changeAnyArr :: Int -> Array Any -> Array Any
197− changeAnyArr i trAs = runST $ do
198− let n = sizeofArray trAs
199− mutArr <- thawArray trAs 0 n
200− writeArray mutArr i $ toAny x
201− unsafeFreezeArray mutArr
202−{-# INLINE insert #-}
203−
204−-- Extract the kind of a type. We use it to work around lack of syntax for
205−-- inferred type variables (which are not subject to type applications).
206−type KindOf (a :: k) = k
207−
208−type ArgKindOf (f :: k -> l) = k
209−
210−{- | Delete a value from a 'TypeRepMap'.
211−
212−TypeRepMap optimizes for fast reads rather than modifications, as a trade-off deletes are
213−@O(n)@, with an @O(log(n))@ optimization for when the element is already missing.
214−
215−prop> size (delete @a tm) <= size tm
216−prop> member @a (delete @a tm) == False
217−
218−>>> tm = delete @Bool $ insert (Just True) $ one (Just 'a')
219−>>> size tm
220−1
221−>>> member @Bool tm
222−False
223−>>> member @Char tm
224−True
225−-}
226−delete :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> TypeRepMap f
227−delete m
228− -- Lookups are fast, so check if we even have the element first.
229− | not (member @a m) = m
230− -- We know we have the element, If the map has exactly one element, we can return the empty map
231− | size m == 1 = empty
232− -- Otherwise, filter out the element in linear time.
233− | otherwise = fromSortedTriples . deleteFirst ((== typeFp @a) . fst3) . toSortedTriples $ m
234−{-# INLINE delete #-}
235−
236−deleteFirst :: (a -> Bool) -> [a] -> [a]
237−deleteFirst _ [] = []
238−deleteFirst p (x : xs) = if p x then xs else x : deleteFirst p xs
239−
240−{- |
241−Update a value at a specific key with the result of the provided function.
242−When the key is not a member of the map, the original map is returned.
243−
244−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "a"]
245−>>> lookup @String $ adjust (fmap (++ "ww")) trmap
246−Just (Identity "aww")
247−-}
248−adjust :: forall a f . Typeable a => (f a -> f a) -> TypeRepMap f -> TypeRepMap f
249−adjust fun = alter (fmap fun)
250−{-# INLINE adjust #-}
251−
252−{- |
253−Updates a value at a specific key, whether or not it exists.
254−This can be used to insert, delete, or update a value of a given type in the map.
255−
256−>>> func = (\case Nothing -> Just (Identity "new"); Just (Identity s) -> Just (Identity (reverse s)))
257−>>> lookup @String $ alter func empty
258−Just (Identity "new")
259−>>> trmap = fromList @(TypeRepMap Identity) [WrapTypeable $ Identity "helllo"]
260−>>> lookup @String $ alter func trmap
261−>>> Just (Identity "olleh")
262−-}
263−alter :: forall a f . Typeable a => (Maybe (f a) -> Maybe (f a)) -> TypeRepMap f -> TypeRepMap f
264−alter fun tr = case cachedBinarySearch (typeFp @a) (fingerprintAs tr) (fingerprintBs tr) of
265− Nothing ->
266− case (fun Nothing) of
267− Nothing -> tr
268− Just v -> insert v tr
269− Just i ->
270− case fun (Just . fromAny $ indexArray (trAnys tr) i) of
271− Nothing -> delete @a tr
272− Just v -> tr{trAnys = replaceAnyAt i (toAny v) (trAnys tr)}
273− where
274− replaceAnyAt :: Int -> Any -> Array Any -> Array Any
275− replaceAnyAt i v trAs = runST $ do
276− let n = sizeofArray trAs
277− mutArr <- thawArray trAs 0 n
278− writeArray mutArr i v
279− unsafeFreezeArray mutArr
280−{-# INLINE alter #-}
281−
282−{- | Map over the elements of a 'TypeRepMap'.
283−
284−>>> tm = insert (Identity True) $ one (Identity 'a')
285−>>> lookup @Bool tm
286−Just (Identity True)
287−>>> lookup @Char tm
288−Just (Identity 'a')
289−>>> tm2 = hoist ((:[]) . runIdentity) tm
290−>>> lookup @Bool tm2
291−Just [True]
292−>>> lookup @Char tm2
293−Just "a"
294−-}
295−hoist :: (forall x. f x -> g x) -> TypeRepMap f -> TypeRepMap g
296−hoist f (TypeRepMap as bs ans ks) = TypeRepMap as bs (mapArray' (toAny . f . fromAny) ans) ks
297−{-# INLINE hoist #-}
298−
299−hoistA :: (Applicative t) => (forall x. f x -> t (g x)) -> TypeRepMap f -> t (TypeRepMap g)
300−hoistA f (TypeRepMap as bs (toList -> ans) ks) = (\l -> TypeRepMap as bs (fromList $ map toAny l) ks)
301− <$> traverse (f . fromAny) ans
302−{-# INLINE hoistA #-}
303−
304−hoistWithKey :: forall f g. (forall x. Typeable x => f x -> g x) -> TypeRepMap f -> TypeRepMap g
305−hoistWithKey f (TypeRepMap as bs ans ks) = TypeRepMap as bs newAns ks
306− where
307− newAns = mapArray' mapAns (mzip ans ks)
308− mapAns (a, k) = toAny $ withTr (unsafeCoerce k) $ fromAny a
309−
310− withTr :: forall x. TypeRep x -> f x -> g x
311− withTr t = withTypeable t f
312−{-# INLINE hoistWithKey #-}
313−
314−-- | The union of two 'TypeRepMap's using a combining function for conflicting entries. @O(n + m)@
315−unionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
316−unionWith f ma mb = do
317− fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
318− where
319− f' :: forall x. TypeRep x -> f x -> f x -> f x
320− f' tr = withTypeable tr f
321−
322− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
323− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
324−
325− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
326− -- Using 'toSortedTriples' allows us to assume the triples are sorted by fingerprint,
327− -- Given O(n) performance from 'toSortedTriples', and given that we can merge-sort in
328− -- O(n + m) time, then can '.fromSortedTriples' back into cachedBinarySearch order in O(n + m)
329− -- that gives a total of O(n + m).
330− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
331− -- We've addressed all elements from both maps
332− mergeMaps as [] = as
333− mergeMaps [] bs = bs
334− -- Merge
335− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
336− case compare af bf of
337− -- Fingerprints are equal, union the elements using our function
338− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
339− -- fingerprints
340− EQ -> combine a b : mergeMaps as bs
341− -- First fingerprint must not be in the second map or we would have seen it by now
342− -- Add it to the result as-is
343− LT -> a : mergeMaps as (b : bs)
344− -- Second fingerprint must not be in the first map or we would have seen it by now
345− -- Add it to the result as-is
346− GT -> b : mergeMaps (a:as) bs
347−{-# INLINE unionWith #-}
348−
349−-- | The (left-biased) union of two 'TypeRepMap's in @O(n + m)@. It prefers the first map when
350−-- duplicate keys are encountered, i.e. @'union' == 'unionWith' const@.
351−union :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
352−union = unionWith const
353−{-# INLINE union #-}
354−
355−-- | The 'intersection' of two 'TypeRepMap's using a combining function
356−--
357−-- @O(n + m)@
358−intersectionWith :: forall f. (forall x. Typeable x => f x -> f x -> f x) -> TypeRepMap f -> TypeRepMap f -> TypeRepMap f
359−intersectionWith f ma mb =
360− fromSortedTriples $ mergeMaps (toSortedTriples ma) (toSortedTriples mb)
361− where
362− f' :: forall x. TypeRep x -> f x -> f x -> f x
363− f' tr = withTypeable tr f
364−
365− combine :: (Fingerprint, Any, Any) -> (Fingerprint, Any, Any) -> (Fingerprint, Any, Any)
366− combine (fp, av, ak) (_, bv, _) = (fp, toAny $ f' (fromAny ak) (fromAny av) (fromAny bv), ak)
367−
368− -- Merges two typrepmaps into a sorted, dedup'd list of triples.
369− mergeMaps :: [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)] -> [(Fingerprint, Any, Any)]
370− -- If either list is empty, the intersection must be finished.
371− mergeMaps _ [] = []
372− mergeMaps [] _ = []
373− -- Merge the two maps considering one element at a time.
374− mergeMaps (a@(af, _, _) : as) (b@(bf, _, _) : bs) =
375− case compare af bf of
376− -- Fingerprints are equal, union the elements using our function
377− -- If the incoming maps were de-duped, there shouldn't be any other equivalent
378− -- fingerprints
379− EQ -> combine a b : mergeMaps as bs
380− -- First fingerprint must not be in the second map or we would have seen it by now
381− -- Skip it an move on
382− LT -> mergeMaps as (b : bs)
383− -- Second fingerprint must not be in the first map or we would have seen it by now
384− -- Skip it an move on
385− GT -> mergeMaps (a:as) bs
386−{-# INLINE intersectionWith #-}
387−
388−-- | The intersection of two 'TypeRepMap's.
389−-- It keeps all values from the first map whose keys are present in the second.
390−--
391−-- @O(n + m)@
392−intersection :: TypeRepMap f -> TypeRepMap f -> TypeRepMap f
393−intersection = intersectionWith const
394−{-# INLINE intersection #-}
395−
396−
397−{- | Check if a value of the given type is present in a 'TypeRepMap'.
398−
399−>>> member @Char $ one (Identity 'a')
400−True
401−>>> member @Bool $ one (Identity 'a')
402−False
403−-}
404−member :: forall a (f :: KindOf a -> Type) . Typeable a => TypeRepMap f -> Bool
405−member tm = case lookup @a tm of
406− Nothing -> False
407− Just _ -> True
408−{-# INLINE member #-}
409−
410−{- | Lookup a value of the given type in a 'TypeRepMap'.
411−
412−>>> x = lookup $ insert (Identity (11 :: Int)) empty
413−>>> x :: Maybe (Identity Int)
414−Just (Identity 11)
415−>>> x :: Maybe (Identity ())
416−Nothing
417−-}
418−lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
419−lookup tVect = fromAny . (trAnys tVect `indexArray`)
420− <$> cachedBinarySearch (typeFp @a)
421− (fingerprintAs tVect)
422− (fingerprintBs tVect)
423−{-# INLINE lookup #-}
424−
425−-- | Get the amount of elements in a 'TypeRepMap'.
426−size :: TypeRepMap f -> Int
427−size = sizeofPrimArray . fingerprintAs
428−{-# INLINE size #-}
429−
430−-- | Return the list of 'SomeTypeRep' from the keys.
431−keys :: TypeRepMap f -> [SomeTypeRep]
432−keys = keysWith SomeTypeRep
433−{-# INLINE keys #-}
434−
435−-- | Return the list of keys by wrapping them with a user-provided function.
436−keysWith :: (forall (a :: ArgKindOf f). TypeRep a -> r) -> TypeRepMap f -> [r]
437−keysWith f TypeRepMap{..} = f . anyToTypeRep <$> toList trKeys
438−{-# INLINE keysWith #-}
439−
440−-- | Return the list of key-value pairs by wrapping them with a user-provided function.
441−toListWith :: forall f r. (forall (a :: ArgKindOf f). Typeable a => f a -> r) -> TypeRepMap f -> [r]
442−toListWith f = map toF . toTriples
443− where
444− withTypeRep :: TypeRep a -> f a -> r
445− withTypeRep tr an = withTypeable tr $ f an
446− toF (_, an, k) = withTypeRep (unsafeCoerce k) (fromAny an)
447−
448−-- | Binary searched based on this article
449−-- http://bannalia.blogspot.com/2015/06/cache-friendly-binary-search.html
450−-- with modification for our two-vector search case.
451−cachedBinarySearch :: Fingerprint -> PrimArray Word64 -> PrimArray Word64 -> Maybe Int
452−cachedBinarySearch (Fingerprint (W64# a) (W64# b)) fpAs fpBs = inline (go 0#)
453− where
454− go :: Int# -> Maybe Int
455− go i = case i <# len of
456− 0# -> Nothing
457− _ -> let !(W64# valA) = indexPrimArray fpAs (I# i) in case a `ltWord#` valA of
458− 0# -> case a `eqWord#` valA of
459− 0# -> go (2# *# i +# 2#)
460− _ -> let !(W64# valB) = indexPrimArray fpBs (I# i) in case b `eqWord#` valB of
461− 0# -> case b `ltWord#` valB of
462− 0# -> go (2# *# i +# 2#)
463− _ -> go (2# *# i +# 1#)
464− _ -> Just (I# i)
465− _ -> go (2# *# i +# 1#)
466−
467− len :: Int#
468− len = let !(I# l) = sizeofPrimArray fpAs in l
469−{-# INLINE cachedBinarySearch #-}
470−
471−----------------------------------------------------------------------------
472−-- Internal functions
473−----------------------------------------------------------------------------
474−
475−toAny :: f a -> Any
476−toAny = unsafeCoerce
477−
478−fromAny :: Any -> f a
479−fromAny = unsafeCoerce
480−
481−anyToTypeRep :: Any -> TypeRep f
482−anyToTypeRep = unsafeCoerce
483−
484−typeFp :: forall a . Typeable a => Fingerprint
485−typeFp = typeRepFingerprint $ typeRep @a
486−{-# INLINE typeFp #-}
487−
488−toTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
489−toTriples tm = zip3 (toFingerprints tm) (GHC.toList $ trAnys tm) (GHC.toList $ trKeys tm)
490−
491−-- | Efficiently get sorted triples from a map in O(n) time
492−--
493−-- We assume the incoming TypeRepMap is already sorted into 'cachedBinarySearch' order using fromSortedList.
494−-- Then we can construct the index mapping from the "cached" ordering into monotonically
495−-- increasing order using 'generateOrderMapping' with the length of the TRM. This takes @O(n).
496−-- We then pull those indexes from the source TRM to get the sorted triples in a total of @O(n).
497−toSortedTriples :: TypeRepMap f -> [(Fingerprint, Any, Any)]
498−toSortedTriples tm = trip <$> ordering
499− where
500− trip i = ( Fingerprint (indexPrimArray (fingerprintAs tm) i) (indexPrimArray (fingerprintBs tm) i)
501− , indexArray (trAnys tm) i
502− , indexArray (trKeys tm) i)
503− ordering :: [ Int ]
504− ordering = generateOrderMapping (size tm)
505−
506−nubByFst :: (Eq a) => [(a, b, c)] -> [(a, b, c)]
507−nubByFst = nubBy ((==) `on` fst3)
508−
509−fst3 :: (a, b, c) -> a
510−fst3 (a, _, _) = a
511−
512−----------------------------------------------------------------------------
513−-- Functions for testing and benchmarking
514−----------------------------------------------------------------------------
515−
516−-- | Existential wrapper around 'Typeable' indexed by @f@ type parameter.
517−-- Useful for 'TypeRepMap' structure creation form list of 'WrapTypeable's.
518−data WrapTypeable f where
519− WrapTypeable :: Typeable a => f a -> WrapTypeable f
520−
521−instance Show (WrapTypeable f) where
522− show (WrapTypeable (_ :: f a)) = show $ calcFp @a
523−
524−{- |
525−
526−prop> fromList . toList == 'id'
527−
528−Creates 'TypeRepMap' from a list of 'WrapTypeable's.
529−
530−>>> show $ fromList [WrapTypeable $ Identity True, WrapTypeable $ Identity 'a']
531−TypeRepMap [Bool, Char]
532−
533−
534−-}
535−instance IsList (TypeRepMap f) where
536− type Item (TypeRepMap f) = WrapTypeable f
537−
538− fromList :: [WrapTypeable f] -> TypeRepMap f
539− fromList = fromTriples . map (\x -> (fp x, an x, k x))
540− where
541− fp :: WrapTypeable f -> Fingerprint
542− fp (WrapTypeable (_ :: f a)) = calcFp @a
543−
544− an :: WrapTypeable f -> Any
545− an (WrapTypeable x) = toAny x
546−
547− k :: WrapTypeable f -> Any
548− k (WrapTypeable (_ :: f a)) = unsafeCoerce $ typeRep @a
549−
550− toList :: TypeRepMap f -> [WrapTypeable f]
551− toList = toListWith WrapTypeable
552−
553−calcFp :: forall a . Typeable a => Fingerprint
554−calcFp = typeRepFingerprint $ typeRep @a
555−
556−fromTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
557−fromTriples = fromSortedTriples . sortWith fst3 . nubByFst
558−
559−fromSortedTriples :: [(Fingerprint, Any, Any)] -> TypeRepMap f
560−fromSortedTriples kvs = TypeRepMap (GHC.fromList fpAs) (GHC.fromList fpBs) (GHC.fromList ans) (GHC.fromList ks)
561− where
562− (fpAs, fpBs) = unzip $ map (\(Fingerprint a b) -> (a, b)) fps
563− (fps, ans, ks) = unzip3 $ fromSortedList kvs
564−
565−----------------------------------------------------------------------------
566−-- Tree-like conversion
567−----------------------------------------------------------------------------
568−
569−fromSortedList :: forall a . [a] -> [a]
570−fromSortedList l = runST $ do
571− let n = length l
572− let arrOrigin = fromListN n l
573− arrResult <- thawArray arrOrigin 0 n
574− go n arrResult arrOrigin
575− toList <$> unsafeFreezeArray arrResult
576− where
577− -- state monad could be used here, but it's another dependency
578− go :: forall s . Int -> MutableArray s a -> Array a -> ST s ()
579− go len result origin = () <$ loop 0 0
580− where
581− loop :: Int -> Int -> ST s Int
582− loop i first =
583− if i >= len
584− then pure first
585− else do
586− newFirst <- loop (2 * i + 1) first
587− writeArray result i (indexArray origin newFirst)
588− loop (2 * i + 2) (newFirst + 1)
589−
590−-- Returns a list of indexes which represents the "sorted" order of an array generated by
591−-- fromSortedList of the provided length.
592−-- I.e. fmap (fromSortedList [1, 2, 3, 4, 5, 6] !!) (generateOrderMapping 6) == [1, 2, 3, 4, 5, 6]
593−--
594−-- >>> generateOrderMapping 6
595−-- [3,1,4,0,5,2]
596−--
597−-- >>> generateOrderMapping 8
598−-- [7,3,1,4,0,5,2,6]
599−generateOrderMapping :: Int -> [Int]
600−generateOrderMapping len = runST $ do
601− orderMappingArr <- newPrimArray len
602− _ <- loop orderMappingArr 0 0
603− primArrayToList <$> unsafeFreezePrimArray orderMappingArr
604− where
605− loop :: MutablePrimArray s Int -> Int -> Int -> ST s Int
606− loop result i first =
607− if i >= len
608− then pure first
609− else do
610− newFirst <- loop result (2 * i + 1) first
611− writePrimArray result newFirst i
612− loop result (2 * i + 2) (newFirst + 1)
613−
614−----------------------------------------------------------------------------
615−-- Helper functions.
616−----------------------------------------------------------------------------
617−
618−-- | Check that invariant of the structure holds.
619−-- The structure maintains the following invariant.
620−-- For each element @A@ at index @i@:
621−--
622−-- 1. if there is an element @B@ at index @2*i+1@,
623−-- then @B < A@.
624−--
625−-- 2. if there is an element @C@ at index @2*i+2@,
626−-- then @A < C@.
627−--
628−invariantCheck :: TypeRepMap f -> Bool
629−invariantCheck TypeRepMap{..} = getAll (check 0)
630− where
631− lastMay [] = Nothing
632− lastMay [x] = Just x
633− lastMay (_:xs) = lastMay xs
634− sz = sizeofPrimArray fingerprintAs
635− check i | i >= sz = All True
636− | otherwise =
637− let left = i*2+1
638− right = i*2+2
639− -- maximum value in the left branch
640− leftMax =
641− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
642− $ lastMay
643− $ takeWhile (<sz)
644− $ iterate (\j -> j*2+2) left
645− -- minimum value in the right branch
646− rightMin =
647− fmap (\j -> (indexPrimArray fingerprintAs j, indexPrimArray fingerprintBs j))
648− $ lastMay
649− $ takeWhile (<sz)
650− $ iterate (\j -> j*2+1) right
651− in mconcat
652− [ All $
653− if left < sz
654− then
655− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs left of
656− LT -> False
657− EQ -> indexPrimArray fingerprintBs i >= indexPrimArray fingerprintBs left
658− GT -> True
659− else True
660− , All $
661− if right < sz
662− then
663− case indexPrimArray fingerprintAs i `compare` indexPrimArray fingerprintAs right of
664− LT -> True
665− EQ -> indexPrimArray fingerprintBs i <= indexPrimArray fingerprintBs right
666− GT -> False
667− else True
668− , All $ fromMaybe True $ (<=) <$> leftMax <*> rightMin
669− , check (i+1)
670− ]
stack.yaml+9−4
@@ -1,6 +1,11 @@
1−resolver: lts-15.8
1+resolver: lts-9.18
2+
3+packages:
4+- .
25
36extra-deps:
4− - dependent-map-0.2.4.0
5− - dependent-sum-0.5
6− - hspec-hedgehog-0.0.1.2
7+- tasty-discover-4.1.1
8+
9+flags: {}
10+
11+extra-package-dbs: []
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.hsdeleted−43
@@ -1,43 +0,0 @@
1−module Test.TypeRep.CMap
2− ( cMapSpec
3− ) where
4−
5−import Prelude hiding (lookup)
6−
7−import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
9−
10−import Data.TypeRep.CMap (TypeRepMap, empty, insert, lookup, size)
11−
12−
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−cMapSpec :: Spec
15−cMapSpec = describe "Containers Map TypeRep" $ do
16− describe "Lookup Test" $ do
17− it "returns the inserted element" $
18− lookup (insert (Identity 'a') empty) `shouldBe` Just (Identity 'a')
19− it "returns the second inserted value of the same type" $
20− lookup (insert (Identity 'b') $ insert (Identity 'a') empty) `shouldBe` Just (Identity 'b')
21−
22− describe "Size Test" $ do
23− it "is empty" $
24− size empty `shouldBe` 0
25− it "is of size 1 when 1 element inserted" $
26− size (insert (Identity 'a') empty) `shouldBe` 1
27− it "doesn't increase size when element of the same type is added" $
28− size (insert (Identity 'b') $ insert (Identity 'a') empty) `shouldBe` 1
29− it "returns 10 when 10 different types are inserted" $
30− size mapOf10 `shouldBe` 10
31−
32−
33−mapOf10 :: TypeRepMap Identity
34−mapOf10 = insert (Identity True)
35− $ insert (Identity [True, False])
36− $ insert (Identity $ Just True)
37− $ insert (Identity $ Just ())
38− $ insert (Identity [()])
39− $ insert (Identity ())
40− $ insert (Identity @String "aaa")
41− $ insert (Identity $ Just 'a')
42− $ insert (Identity 'a')
43− $ insert (Identity (11 :: Int)) empty
test/Test/TypeRep/Map.hsnew file+33
@@ -0,0 +1,33 @@
1+module Test.TypeRep.Map where
2+
3+import Prelude hiding (lookup)
4+
5+import Test.Tasty.Hspec
6+
7+import Data.TypeRep.Map
8+
9+-- Simple test for 'lookup', 'insert' and 'size' functions.
10+spec_insertLookup :: Spec
11+spec_insertLookup = do
12+ describe "Lookup Test" $ do
13+ it "returns the inserted element" $
14+ lookup (insert 'a' empty) `shouldBe` Just 'a'
15+ it "returns the second inserted value of the same type" $
16+ lookup (insert 'b' $ insert 'a' empty) `shouldBe` Just 'b'
17+
18+ describe "Size Test" $ do
19+ it "is empty" $
20+ size empty `shouldBe` 0
21+ it "is of size 1 when 1 element inserted" $
22+ size (insert 'a' empty) `shouldBe` 1
23+ it "doesn't increase size when element of the same type is added" $
24+ size (insert 'b' $ insert 'a' empty) `shouldBe` 1
25+ it "returns 10 when 10 different types are inserted" $
26+ size mapOf10 `shouldBe` 10
27+
28+
29+mapOf10 :: TypeRepMap
30+mapOf10 = insert True $ insert [True, False] $ insert (Just True)
31+ $ insert (Just ()) $ insert [()] $ insert ()
32+ $ insert "aaa" $ insert (Just 'a') $ insert 'a'
33+ $ insert (11 :: Int) empty
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.hsdeleted−20
@@ -1,20 +0,0 @@
1−module Test.TypeRep.Vector
2− ( vectorSpec
3− ) where
4−
5−import Prelude hiding (lookup)
6−
7−import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
9−
10−import Data.TypeRep.Vector (TF (..), fromList, lookup)
11−
12−
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−vectorSpec :: Spec
15−vectorSpec = describe "Vector TypeRep" $
16− describe "Lookup Test" $ do
17− it "returns the inserted element" $
18− lookup (fromList [TF (Identity 'a')]) `shouldBe` Just (Identity 'a')
19− it "returns the second inserted value of the same type" $
20− lookup (fromList [TF (Identity 'b'), TF (Identity 'a')]) `shouldBe` Just (Identity 'b')
test/Test/TypeRep/VectorOpt.hsdeleted−20
@@ -1,20 +0,0 @@
1−module Test.TypeRep.VectorOpt
2− ( optimalVectorSpec
3− ) where
4−
5−import Prelude hiding (lookup)
6−
7−import Data.Functor.Identity (Identity (..))
8−import Test.Hspec (Spec, describe, it, shouldBe)
9−
10−import Data.TypeRep.OptimalVector (TF (..), fromList, lookup)
11−
12−
13−-- | Simple test for 'lookup', 'insert' and 'size' functions.
14−optimalVectorSpec :: Spec
15−optimalVectorSpec = describe "Optimal Vector TypeRep" $
16− describe "Lookup Test" $ do
17− it "returns the inserted element" $
18− lookup (fromList [TF $ Identity 'a']) `shouldBe` Just (Identity 'a')
19− it "returns the second inserted value of the same type" $
20− lookup (fromList [TF (Identity 'b'), TF (Identity 'a')]) `shouldBe` Just (Identity 'b')
typerep-extra-impls/Data/TypeRep/CMap.hsdeleted−61
@@ -1,61 +0,0 @@
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−-}
10−
11−module Data.TypeRep.CMap
12− ( TypeRepMap (..)
13− , empty
14− , insert
15− , keys
16− , lookup
17− , size
18− ) where
19−
20−import Prelude hiding (lookup)
21−
22−import Control.DeepSeq
23−import Data.Kind (Type)
24−import Data.Proxy (Proxy (..))
25−import Data.Typeable (TypeRep, Typeable, typeRep)
26−import GHC.Base (Any)
27−import Unsafe.Coerce (unsafeCoerce)
28−
29−import qualified Data.Map.Lazy as LMap
30−
31−
32−-- | Map-like data structure with types served as the keys.
33−newtype TypeRepMap (f :: k -> Type) = TypeRepMap
34− { unMap :: LMap.Map TypeRep Any
35− }
36−
37−instance NFData (TypeRepMap f) where
38− rnf x = rnf (keys x) `seq` ()
39−
40−-- | Empty structure.
41−empty :: TypeRepMap f
42−empty = TypeRepMap mempty
43−
44−-- | Inserts the value with its type as a key.
45−insert :: forall a f . Typeable a => f a -> TypeRepMap f -> TypeRepMap f
46−insert val = TypeRepMap . LMap.insert (typeRep (Proxy :: Proxy a)) (unsafeCoerce val) . unMap
47−
48−-- | Looks up the value at the type.
49−-- >>> let x = lookup $ insert (11 :: Int) empty
50−-- >>> x :: Maybe Int
51−-- Just 11
52−-- >>> x :: Maybe ()
53−-- Nothing
54−lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
55−lookup = fmap unsafeCoerce . LMap.lookup (typeRep (Proxy :: Proxy a)) . unMap
56−
57−size :: TypeRepMap f -> Int
58−size = LMap.size . unMap
59−
60−keys :: TypeRepMap f -> [TypeRep]
61−keys = LMap.keys . unMap
typerep-extra-impls/Data/TypeRep/OptimalVector.hsdeleted−127
@@ -1,127 +0,0 @@
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−-}
14−
15−module Data.TypeRep.OptimalVector
16− ( -- * Map type
17− TypeRepMap (..)
18−
19− -- 'TypeRepMap' interface
20− , empty
21− , insert
22− , lookup
23− , size
24−
25− -- * Helpful testing functions
26− , TF (..)
27− , fromList
28− ) where
29−
30−import Prelude hiding (lookup)
31−
32−import Control.Arrow ((&&&))
33−import Control.DeepSeq
34−import Data.Kind (Type)
35−import Data.Proxy (Proxy (..))
36−import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
37−import Data.Word (Word64)
38−import GHC.Base (Any, Int (..), Int#, uncheckedIShiftRA#, (+#), (-#), (<#))
39−import GHC.Exts (inline, sortWith)
40−import GHC.Fingerprint (Fingerprint (..))
41−import Unsafe.Coerce (unsafeCoerce)
42−
43−import qualified Data.Vector as V
44−import qualified Data.Vector.Unboxed as Unboxed
45−
46−
47−data TypeRepMap (f :: k -> Type) = TypeRepMap
48− { fingerprintAs :: Unboxed.Vector Word64
49− , fingerprintBs :: Unboxed.Vector Word64
50− , anys :: V.Vector Any
51− }
52−
53−instance NFData (TypeRepMap f) where
54− rnf x = x `seq` ()
55−
56−fromAny :: Any -> f a
57−fromAny = unsafeCoerce
58−
59−-- | Empty structure.
60−empty :: TypeRepMap f
61−empty = TypeRepMap mempty mempty mempty
62−
63−-- | Inserts the value with its type as a key.
64−insert :: forall a f . a -> TypeRepMap f -> TypeRepMap f
65−insert = undefined
66−
67−-- | Looks up the value at the type.
68−-- >>> let x = lookup $ insert (11 :: Int) empty
69−-- >>> x :: Maybe Int
70−-- Just 11
71−-- >>> x :: Maybe ()
72−-- Nothing
73−lookup :: forall a f . Typeable a => TypeRepMap f -> Maybe (f a)
74−lookup tVect = fromAny . (anys tVect V.!)
75− <$> binarySearch (typeRepFingerprint $ typeRep $ Proxy @a)
76− (fingerprintAs tVect)
77− (fingerprintBs tVect)
78−
79−-- | Returns the size of the 'TypeRepMap'.
80−size :: TypeRepMap f -> Int
81−size = Unboxed.length . fingerprintAs
82−
83−-- | Returns the index is found.
84−binarySearch :: Fingerprint -> Unboxed.Vector Word64 -> Unboxed.Vector Word64 -> Maybe Int
85−binarySearch (Fingerprint a b) fpAs fpBs =
86− let
87− !(I# len) = Unboxed.length fpAs
88− checkfpBs :: Int# -> Maybe Int
89− checkfpBs i =
90− case i <# len of
91− 0# -> Nothing
92− _ | a /= Unboxed.unsafeIndex fpAs (I# i) -> Nothing
93− | b == Unboxed.unsafeIndex fpBs (I# i) -> Just (I# i)
94− | otherwise -> checkfpBs (i +# 1#)
95− in
96− inline (checkfpBs (binSearchHelp (-1#) len))
97− where
98− binSearchHelp :: Int# -> Int# -> Int#
99− binSearchHelp l r = case l <# (r -# 1#) of
100− 0# -> r
101− _ ->
102− let m = uncheckedIShiftRA# (l +# r) 1# in
103− if Unboxed.unsafeIndex fpAs (I# m) < a
104− then binSearchHelp m r
105− else binSearchHelp l m
106−
107−----------------------------------------------------------------------------
108−-- Functions for testing and benchmarking
109−----------------------------------------------------------------------------
110−
111−data TF f where
112− TF :: Typeable a => f a -> TF f
113−
114−fromF :: f a -> Proxy a
115−fromF _ = Proxy
116−
117−fromList :: forall f . [TF f] -> TypeRepMap f
118−fromList tfs = TypeRepMap (Unboxed.fromList fpAs) (Unboxed.fromList fpBs) (V.fromList ans)
119− where
120− (fpAs, fpBs) = unzip $ fmap (\(Fingerprint a b) -> (a, b)) fps
121− (fps, ans) = unzip $ sortWith fst $ map (fp &&& an) tfs
122−
123− fp :: TF f -> Fingerprint
124− fp (TF x) = typeRepFingerprint $ typeRep $ fromF x
125−
126− an :: TF f -> Any
127− an (TF x) = unsafeCoerce x
typerep-extra-impls/Data/TypeRep/Vector.hsdeleted−197
@@ -1,197 +0,0 @@
1−{-# OPTIONS_GHC -fno-warn-orphans #-}
2−
3−{-# LANGUAGE BangPatterns #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MagicHash #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PolyKinds #-}
8−{-# LANGUAGE TypeFamilies #-}
9−
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−
18−module Data.TypeRep.Vector
19− ( TypeRepVector (..)
20− , TF (..)
21− , empty
22− , insert
23− , lookup
24− , size
25− , fromList
26− ) where
27−
28−import Prelude hiding (lookup)
29−
30−import Control.Arrow ((&&&))
31−import Data.Proxy (Proxy (..))
32−import Data.Typeable (Typeable, typeRep, typeRepFingerprint)
33−import Data.Word (Word64)
34−import GHC.Base hiding (empty)
35−import GHC.Exts (sortWith)
36−import GHC.Fingerprint (Fingerprint (..))
37−import Unsafe.Coerce (unsafeCoerce)
38−
39−import qualified Data.Vector as V
40−import qualified Data.Vector.Generic as G
41−import qualified Data.Vector.Generic.Mutable as M
42−import qualified Data.Vector.Unboxed as Unboxed
43−
44−
45−data instance Unboxed.MVector s Fingerprint = MFingerprintVector (Unboxed.MVector s Word64) (Unboxed.MVector s Word64)
46−data instance Unboxed.Vector Fingerprint = FingerprintVector (Unboxed.Vector Word64) (Unboxed.Vector Word64)
47−
48−instance Unboxed.Unbox Fingerprint
49−
50−instance M.MVector Unboxed.MVector Fingerprint where
51− {-# INLINE basicLength #-}
52− basicLength (MFingerprintVector x _) = M.basicLength x
53− {-# INLINE basicUnsafeSlice #-}
54− basicUnsafeSlice i m (MFingerprintVector a b) =
55− MFingerprintVector (M.basicUnsafeSlice i m a) (M.basicUnsafeSlice i m b)
56− {-# INLINE basicOverlaps #-}
57− basicOverlaps (MFingerprintVector as1 bs1) (MFingerprintVector as2 bs2) =
58− M.basicOverlaps as1 as2 || M.basicOverlaps bs1 bs2
59− {-# INLINE basicUnsafeNew #-}
60− basicUnsafeNew n_ = do
61− as <- M.basicUnsafeNew n_
62− bs <- M.basicUnsafeNew n_
63− return $ MFingerprintVector as bs
64− {-# INLINE basicInitialize #-}
65− basicInitialize (MFingerprintVector as bs) = do
66− M.basicInitialize as
67− M.basicInitialize bs
68− {-# INLINE basicUnsafeReplicate #-}
69− basicUnsafeReplicate n_ (Fingerprint a b) = do
70− as <- M.basicUnsafeReplicate n_ a
71− bs <- M.basicUnsafeReplicate n_ b
72− return $ MFingerprintVector as bs
73− {-# INLINE basicUnsafeRead #-}
74− basicUnsafeRead (MFingerprintVector as bs) i_ = do
75− a <- M.basicUnsafeRead as i_
76− b <- M.basicUnsafeRead bs i_
77− return (Fingerprint a b)
78− {-# INLINE basicUnsafeWrite #-}
79− basicUnsafeWrite (MFingerprintVector as bs) i_ (Fingerprint a b) = do
80− M.basicUnsafeWrite as i_ a
81− M.basicUnsafeWrite bs i_ b
82− {-# INLINE basicClear #-}
83− basicClear (MFingerprintVector as bs) = do
84− M.basicClear as
85− M.basicClear bs
86− {-# INLINE basicSet #-}
87− basicSet (MFingerprintVector as bs) (Fingerprint a b) = do
88− M.basicSet as a
89− M.basicSet bs b
90− {-# INLINE basicUnsafeCopy #-}
91− basicUnsafeCopy (MFingerprintVector as1 bs1) (MFingerprintVector as2 bs2) = do
92− M.basicUnsafeCopy as1 as2
93− M.basicUnsafeCopy bs1 bs2
94− {-# INLINE basicUnsafeMove #-}
95− basicUnsafeMove (MFingerprintVector as1 bs1) (MFingerprintVector as2 bs2) = do
96− M.basicUnsafeMove as1 as2
97− M.basicUnsafeMove bs1 bs2
98− {-# INLINE basicUnsafeGrow #-}
99− basicUnsafeGrow (MFingerprintVector as bs) m_ = do
100− as' <- M.basicUnsafeGrow as m_
101− bs' <- M.basicUnsafeGrow bs m_
102− return $ MFingerprintVector as' bs'
103−
104−instance G.Vector Unboxed.Vector Fingerprint where
105− {-# INLINE basicUnsafeFreeze #-}
106− basicUnsafeFreeze (MFingerprintVector as bs) = do
107− as' <- G.basicUnsafeFreeze as
108− bs' <- G.basicUnsafeFreeze bs
109− return $ FingerprintVector as' bs'
110− {-# INLINE basicUnsafeThaw #-}
111− basicUnsafeThaw (FingerprintVector as bs) = do
112− as' <- G.basicUnsafeThaw as
113− bs' <- G.basicUnsafeThaw bs
114− return $ MFingerprintVector as' bs'
115− {-# INLINE basicLength #-}
116− basicLength (FingerprintVector x _) = G.basicLength x
117− {-# INLINE basicUnsafeSlice #-}
118− basicUnsafeSlice i_ m_ (FingerprintVector as bs) =
119− FingerprintVector (G.basicUnsafeSlice i_ m_ as) (G.basicUnsafeSlice i_ m_ bs)
120− {-# INLINE basicUnsafeIndexM #-}
121− basicUnsafeIndexM (FingerprintVector as bs) i_ = do
122− a <- G.basicUnsafeIndexM as i_
123− b <- G.basicUnsafeIndexM bs i_
124− return (Fingerprint a b)
125− {-# INLINE basicUnsafeCopy #-}
126− basicUnsafeCopy (MFingerprintVector as1 bs1) (FingerprintVector as2 bs2) = do
127− G.basicUnsafeCopy as1 as2
128− G.basicUnsafeCopy bs1 bs2
129− {-# INLINE elemseq #-}
130− elemseq _ (Fingerprint a b)
131− = G.elemseq (undefined :: Unboxed.Vector a) a
132− . G.elemseq (undefined :: Unboxed.Vector b) b
133−
134−data TypeRepVector f = TypeRepVect
135− { fingerprints :: Unboxed.Vector Fingerprint
136− , anys :: V.Vector Any
137− }
138−
139−fromAny :: Any -> f a
140−fromAny = unsafeCoerce
141−
142−-- | Empty structure.
143−empty :: TypeRepVector f
144−empty = TypeRepVect mempty mempty
145−
146−-- | Inserts the value with its type as a key.
147−insert :: forall a f . a -> TypeRepVector f -> TypeRepVector f
148−insert = undefined
149−
150−-- | Looks up the value at the type.
151−-- >>> let x = lookup $ insert (11 :: Int) empty
152−-- >>> x :: Maybe Int
153−-- Just 11
154−-- >>> x :: Maybe ()
155−-- Nothing
156−lookup :: forall a f . Typeable a => TypeRepVector f -> Maybe (f a)
157−lookup tVect = fromAny . (anys tVect V.!)
158− <$> binarySearch (typeRepFingerprint (typeRep (Proxy :: Proxy a))) (fingerprints tVect)
159−
160−-- | Returns the size of the 'TypeRepVect'.
161−size :: TypeRepVector f -> Int
162−size = Unboxed.length . fingerprints
163−
164−data TF f where
165− TF :: Typeable a => f a -> TF f
166−
167−fromF :: f a -> Proxy a
168−fromF _ = Proxy
169−
170−fromList :: forall f . [TF f] -> TypeRepVector f
171−fromList tfs = TypeRepVect (Unboxed.fromList fps) (V.fromList ans)
172− where
173− (fps, ans) = unzip $ sortWith fst $ map (fp &&& an) tfs
174−
175− fp :: TF f -> Fingerprint
176− fp (TF x) = typeRepFingerprint $ typeRep $ fromF x
177−
178− an :: TF f -> Any
179− an (TF x) = unsafeCoerce x
180−
181−-- | Returns the index is found.
182−binarySearch :: Fingerprint -> Unboxed.Vector Fingerprint -> Maybe Int
183−binarySearch fp fpVect =
184− let
185− !(I# len) = Unboxed.length fpVect
186− ind = I# (binSearchHelp (-1#) len)
187− in
188− if fp == (fpVect Unboxed.! ind) then Just ind else Nothing
189− where
190− binSearchHelp :: Int# -> Int# -> Int#
191− binSearchHelp l r = case l <# (r -# 1#) of
192− 0# -> r
193− _ ->
194− let m = uncheckedIShiftRA# (l +# r) 1# in
195− if Unboxed.unsafeIndex fpVect (I# m) < fp
196− then binSearchHelp m r
197− else binSearchHelp l m
typerep-map.cabal+31−114
@@ -1,133 +1,50 @@
1−cabal-version: 2.4
21name: typerep-map
3−version: 0.3.3.0
4−synopsis: Efficient implementation of a dependent map with types as keys
5−description:
6− A dependent map from type representations to values of these types.
7− .
8− Here is an illustration of such a map:
9− .
10− > TMap
11− > ---------------
12− > Int -> 5
13− > Bool -> True
14− > Char -> 'x'
15− .
16− In addition to @TMap@, we provide @TypeRepMap@ parametrized by a
17− @vinyl@-style interpretation. This data structure is equivalent to @DMap
18− TypeRep@, but with significantly more efficient lookups.
19−
20−homepage: https://github.com/kowainik/typerep-map
21−bug-reports: https://github.com/kowainik/typerep-map/issues
22−license: MPL-2.0
2+version: 0.1.0.0
3+description: Efficient implementation of data type with types as keys
4+homepage: https://github.com/vrom911/typerep-map
5+bug-reports: https://github.com/vrom911/typerep-map/issues
6+license: MIT
237license-file: LICENSE
24−author: Veronika Romashkina, Vladislav Zavialov, Dmitrii Kovanikov
25−maintainer: Kowainik <xrom.xkov@gmail.com>
26−copyright: 2017-2020 Kowainik
27−category: Data, Data Structures, Types
8+author: Veronika Romashkina
9+maintainer: vrom911@gmail.com
10+copyright: 2017 Veronika Romashkina
11+category: Data Structures
2812build-type: Simple
29−extra-doc-files: README.md
30− , CHANGELOG.md
31−tested-with: GHC == 8.2.2
32− , GHC == 8.4.4
33− , GHC == 8.6.5
34− , GHC == 8.8.3
35− , GHC == 8.10.1
36−
37−source-repository head
38− type: git
39− location: https://github.com/kowainik/typerep-map.git
40−
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
13+extra-source-files: README.md
14+cabal-version: >=1.10
15+tested-with: GHC == 8.0.1
6816
6917library
70− import: common-options
7118 hs-source-dirs: src
72− exposed-modules: Data.TMap
73− Data.TypeRepMap
74− Data.TypeRepMap.Internal
75−
76− build-depends: ghc-prim >= 0.5.1.1 && < 0.7
77− , primitive ^>= 0.7.0
78− , deepseq ^>= 1.4
79−
80−library typerep-extra-impls
81− import: common-options
82− hs-source-dirs: typerep-extra-impls
83− exposed-modules: Data.TypeRep.CMap
84− Data.TypeRep.OptimalVector
85− Data.TypeRep.Vector
86−
87− build-depends: containers >= 0.5.10.2 && < 0.7
88− , vector ^>= 0.12.0.1
89− , deepseq ^>= 1.4
19+ exposed-modules: Data.TypeRep.Map
20+ ghc-options: -Wall
21+ build-depends: base
22+ , containers
23+ default-language: Haskell2010
9024
9125test-suite typerep-map-test
92− import: common-options
9326 type: exitcode-stdio-1.0
9427 hs-source-dirs: test
95−
9628 main-is: Test.hs
97− other-modules: Test.TypeRep.CMap
98− , Test.TypeRep.TypeRepMap
99− , Test.TypeRep.TypeRepMapProperty
100− , Test.TypeRep.Vector
101− , 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
29+ other-modules: Test.TypeRep.Map
30+ build-depends: base
10731 , typerep-map
108− , typerep-extra-impls
109−
110− ghc-options: -threaded -rtsopts -with-rtsopts=-N
32+ , tasty
33+ , tasty-discover >= 4.1.1
34+ , tasty-hspec
35+ ghc-options: -Wall -Werror -threaded -rtsopts -with-rtsopts=-N
36+ default-language: Haskell2010
11137
11238benchmark typerep-map-benchmark
113− import: common-options
11439 type: exitcode-stdio-1.0
40+ default-language: Haskell2010
41+ ghc-options: -Wall -Werror -O2 -threaded -rtsopts -with-rtsopts=-N
11542 hs-source-dirs: benchmark
116−
11743 main-is: Main.hs
118− other-modules: CMap
119− , CacheMap
120− , DMap
121− , Spec
122− , Vector
123− , 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
44+ build-depends: base
45+ , criterion
13046 , typerep-map
131− , typerep-extra-impls
13247
133− ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N -freduction-depth=0
48+source-repository head
49+ type: git
50+ location: https://github.com/vrom911/typerep-map.git