git.iko.soy/monoidal-containers
on master help sign in
10 files changed+356−229
since splitsnapshots
unifiedsplit
.travis.yml+124−106 Changelog.md+24−1 README.md+4 default.nix+16 monoidal-containers.cabal+18−7 src/Data/HashMap/Monoidal.hs+46−24 src/Data/IntMap/Monoidal.hs+21−38 src/Data/IntMap/Monoidal/Strict.hs+21−37 src/Data/Map/Monoidal.hs+42−8 src/Data/Map/Monoidal/Strict.hs+40−8
.travis.yml+124−106
@@ -1,135 +1,153 @@
11# This Travis job script has been generated by a script via
22#
3−# runghc make_travis_yml_2.hs 'monoidal-containers.cabal'
3+# haskell-ci 'monoidal-containers.cabal' '-o' '.travis.yml'
4+#
5+# To regenerate the script (for example after adjusting tested-with) run
6+#
7+# haskell-ci regenerate
48#
59# For more information, see https://github.com/haskell-CI/haskell-ci
610#
11+# version: 0.10.3
12+#
13+version: ~> 1.0
714language: c
8−sudo: false
9−
15+os: linux
16+dist: xenial
1017git:
11− submodules: false # whether to recursively clone submodules
12−
18+ # whether to recursively clone submodules
19+ submodules: false
1320cache:
1421 directories:
1522 - $HOME/.cabal/packages
1623 - $HOME/.cabal/store
17−
24+ - $HOME/.hlint
1825before_cache:
19− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/build-reports.log
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
2027 # remove files that are regenerated by 'cabal update'
21− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/00-index.*
22− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/*.json
23− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.cache
24− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar
25− - rm -fv $HOME/.cabal/packages/hackage.haskell.org/01-index.tar.idx
26−
27− - rm -rfv $HOME/.cabal/packages/head.hackage
28−
29−matrix:
28+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
29+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
30+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
31+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
32+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
33+ - rm -rfv $CABALHOME/packages/head.hackage
34+jobs:
3035 include:
31− - compiler: "ghc-8.6.1"
32− env: GHCHEAD=true
33− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-head,ghc-8.6.1], sources: [hvr-ghc]}}
34− - compiler: "ghc-8.4.3"
35− # env: TEST=--disable-tests BENCH=--disable-benchmarks
36− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.4.3], sources: [hvr-ghc]}}
37− - compiler: "ghc-8.2.2"
38− # env: TEST=--disable-tests BENCH=--disable-benchmarks
39− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.2.2], sources: [hvr-ghc]}}
40− - compiler: "ghc-8.0.2"
41− # env: TEST=--disable-tests BENCH=--disable-benchmarks
42− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-8.0.2], sources: [hvr-ghc]}}
43− - compiler: "ghc-7.8.4"
44− # env: TEST=--disable-tests BENCH=--disable-benchmarks
45− addons: {apt: {packages: [ghc-ppa-tools,cabal-install-2.2,ghc-7.8.4], sources: [hvr-ghc]}}
46−
47− allow_failures:
48− - compiler: "ghc-8.6.1"
49−
36+ - compiler: ghc-8.10.2
37+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.10.2","cabal-install-3.2"]}}
38+ os: linux
39+ - compiler: ghc-8.8.4
40+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.8.4","cabal-install-3.2"]}}
41+ os: linux
42+ - compiler: ghc-8.6.5
43+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.6.5","cabal-install-3.2"]}}
44+ os: linux
45+ - compiler: ghc-8.4.3
46+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.4.3","cabal-install-3.2"]}}
47+ os: linux
48+ - compiler: ghc-8.2.2
49+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.2.2","cabal-install-3.2"]}}
50+ os: linux
51+ - compiler: ghc-8.0.2
52+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-8.0.2","cabal-install-3.2"]}}
53+ os: linux
54+ - compiler: ghc-7.8.4
55+ addons: {"apt":{"sources":[{"sourceline":"deb http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main","key_url":"https://keyserver.ubuntu.com/pks/lookup?op=get&search=0x063dab2bdc0b3f9fcebc378bff3aeacef6f88286"}],"packages":["ghc-7.8.4","cabal-install-3.2"]}}
56+ os: linux
5057before_install:
51− - HC=${CC}
52− - HCPKG=${HC/ghc/ghc-pkg}
58+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
59+ - WITHCOMPILER="-w $HC"
60+ - HADDOCK=$(echo "/opt/$CC/bin/haddock" | sed 's/-/\//')
61+ - HCPKG="$HC-pkg"
5362 - unset CC
54− - ROOTDIR=$(pwd)
55− - mkdir -p $HOME/.local/bin
56− - "PATH=/opt/ghc/bin:/opt/ghc-ppa-tools/bin:$HOME/local/bin:$PATH"
57− - HCNUMVER=$(( $(${HC} --numeric-version|sed -E 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\1 * 10000 + \2 * 100 + \3/') ))
63+ - CABAL=/opt/ghc/bin/cabal
64+ - CABALHOME=$HOME/.cabal
65+ - export PATH="$CABALHOME/bin:$PATH"
66+ - TOP=$(pwd)
67+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
5868 - echo $HCNUMVER
59−
69+ - CABAL="$CABAL -vnormal+nowrap"
70+ - set -o pipefail
71+ - TEST=--enable-tests
72+ - BENCH=--enable-benchmarks
73+ - HEADHACKAGE=false
74+ - rm -f $CABALHOME/config
75+ - |
76+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
77+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
78+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
79+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
80+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
81+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
82+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
83+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
84+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
85+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
86+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
87+ echo "install-dirs user" >> $CABALHOME/config
88+ echo " prefix: $CABALHOME" >> $CABALHOME/config
89+ echo "repository hackage.haskell.org" >> $CABALHOME/config
90+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
6091install:
61− - cabal --version
92+ - ${CABAL} --version
6293 - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
63− - BENCH=${BENCH---enable-benchmarks}
64− - TEST=${TEST---enable-tests}
65− - HADDOCK=${HADDOCK-true}
66− - UNCONSTRAINED=${UNCONSTRAINED-true}
67− - NOINSTALLEDCONSTRAINTS=${NOINSTALLEDCONSTRAINTS-false}
68− - GHCHEAD=${GHCHEAD-false}
69− - travis_retry cabal update -v
70− - "sed -i.bak 's/^jobs:/-- jobs:/' ${HOME}/.cabal/config"
71− - rm -fv cabal.project cabal.project.local
72− # Overlay Hackage Package Index for GHC HEAD: https://github.com/hvr/head.hackage
7394 - |
74− if $GHCHEAD; then
75− sed -i 's/-- allow-newer: .*/allow-newer: *:base/' ${HOME}/.cabal/config
76− for pkg in $($HCPKG list --simple-output); do pkg=$(echo $pkg | sed 's/-[^-]*$//'); sed -i "s/allow-newer: /allow-newer: *:$pkg, /" ${HOME}/.cabal/config; done
77−
78− echo 'repository head.hackage' >> ${HOME}/.cabal/config
79− echo ' url: http://head.hackage.haskell.org/' >> ${HOME}/.cabal/config
80− echo ' secure: True' >> ${HOME}/.cabal/config
81− echo ' root-keys: 07c59cb65787dedfaef5bd5f987ceb5f7e5ebf88b904bbd4c5cbdeb2ff71b740' >> ${HOME}/.cabal/config
82− echo ' 2e8555dde16ebd8df076f1a8ef13b8f14c66bad8eafefd7d9e37d0ed711821fb' >> ${HOME}/.cabal/config
83− echo ' 8f79fd2389ab2967354407ec852cbe73f2e8635793ac446d09461ffb99527f6e' >> ${HOME}/.cabal/config
84− echo ' key-threshold: 3' >> ${HOME}/.cabal.config
85−
86− grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
87−
88− cabal new-update head.hackage -v
89− fi
90− - grep -Ev -- '^\s*--' ${HOME}/.cabal/config | grep -Ev '^\s*$'
91− - "printf 'packages: \".\"\\n' > cabal.project"
92− - touch cabal.project.local
93− - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- monoidal-containers | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
95+ echo "program-default-options" >> $CABALHOME/config
96+ echo " ghc-options: $GHCJOBS +RTS -M6G -RTS" >> $CABALHOME/config
97+ - cat $CABALHOME/config
98+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
99+ - travis_retry ${CABAL} v2-update -v
100+ # Generate cabal.project
101+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
102+ - touch cabal.project
103+ - |
104+ echo "packages: ." >> cabal.project
105+ - if [ $HCNUMVER -ge 80200 ] ; then echo 'package monoidal-containers' >> cabal.project ; fi
106+ - "if [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
107+ - |
108+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(monoidal-containers)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
94109 - cat cabal.project || true
95110 - cat cabal.project.local || true
96− - if [ -f "./configure.ac" ]; then
97− (cd "." && autoreconf -i);
98− fi
99− - rm -f cabal.project.freeze
100− - cabal new-build -w ${HC} ${TEST} ${BENCH} --project-file="cabal.project" --dep -j2 all
101− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks --project-file="cabal.project" --dep -j2 all
102− - rm -rf .ghc.environment.* "."/dist
103− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
104−
105−# Here starts the actual work to be performed for the package under test;
106−# any command which exits with a non-zero exit code causes the build to fail.
111+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
112+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH}
113+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
114+ - rm cabal.project.freeze
115+ - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} --dep -j2 all
116+ - travis_wait 40 ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --dep -j2 all
107117script:
108− # test that source-distributions can be generated
109− - (cd "." && cabal sdist)
110− - mv "."/dist/monoidal-containers-*.tar.gz ${DISTDIR}/
118+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
119+ # Packaging...
120+ - ${CABAL} v2-sdist all
121+ # Unpacking...
122+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
111123 - cd ${DISTDIR} || false
112− - find . -maxdepth 1 -name '*.tar.gz' -exec tar -xvf '{}' \;
113− - "printf 'packages: monoidal-containers-*/*.cabal\\n' > cabal.project"
114− - touch cabal.project.local
115− - "if ! $NOINSTALLEDCONSTRAINTS; then for pkg in $($HCPKG list --simple-output); do echo $pkg | grep -vw -- monoidal-containers | sed 's/^/constraints: /' | sed 's/-[^-]*$/ installed/' >> cabal.project.local; done; fi"
124+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
125+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
126+ - PKGDIR_monoidal_containers="$(find . -maxdepth 1 -type d -regex '.*/monoidal-containers-[0-9.]*')"
127+ # Generate cabal.project
128+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
129+ - touch cabal.project
130+ - |
131+ echo "packages: ${PKGDIR_monoidal_containers}" >> cabal.project
132+ - if [ $HCNUMVER -ge 80200 ] ; then echo 'package monoidal-containers' >> cabal.project ; fi
133+ - "if [ $HCNUMVER -ge 80200 ] ; then echo ' ghc-options: -Werror=missing-methods' >> cabal.project ; fi"
134+ - |
135+ - "for pkg in $($HCPKG list --simple-output); do echo $pkg | sed 's/-[^-]*$//' | (grep -vE -- '^(monoidal-containers)$' || true) | sed 's/^/constraints: /' | sed 's/$/ installed/' >> cabal.project.local; done"
116136 - cat cabal.project || true
117137 - cat cabal.project.local || true
138+ # Building...
118139 # this builds all libraries and executables (without tests/benchmarks)
119− - cabal new-build -w ${HC} --disable-tests --disable-benchmarks all
120−
140+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all
141+ # Building with tests and benchmarks...
121142 # build & run tests, build benchmarks
122− - cabal new-build -w ${HC} ${TEST} ${BENCH} all
123−
124− # cabal check
125− - (cd monoidal-containers-* && cabal check)
126−
127− # haddock
128− - rm -rf ./dist-newstyle
129− - if $HADDOCK; then cabal new-haddock -w ${HC} ${TEST} ${BENCH} all; else echo "Skipping haddock generation";fi
130−
131− # Build without installed constraints for packages in global-db
132− - if $UNCONSTRAINED; then rm -f cabal.project.local; echo cabal new-build -w ${HC} --disable-tests --disable-benchmarks all; else echo "Not building without installed constraints"; fi
133−
134−# REGENDATA ["monoidal-containers.cabal"]
143+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all
144+ # cabal check...
145+ - (cd ${PKGDIR_monoidal_containers} && ${CABAL} -vnormal check)
146+ # haddock...
147+ - ${CABAL} v2-haddock $WITHCOMPILER --with-haddock $HADDOCK ${TEST} ${BENCH} all
148+ # Building without installed constraints for packages in global-db...
149+ - rm -f cabal.project.local
150+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all
151+
152+# REGENDATA ("0.10.3",["monoidal-containers.cabal","-o",".travis.yml"])
135153# EOF
Changelog.md+24−1
@@ -1,9 +1,32 @@
11# Monoidal containers
22
3+# 0.6.0.1
4+
5+ * Support semialign-1.1
6+
7+# 0.6
8+
9+ * Deprecates 0.5.* and reverts behavior of fromList, insert, mapKeys, etc. to match behavior in Data.Map. The only difference in behavior between Data.Map.Monoidal.MonoidalMap and Data.Map.Map is now the semigroup and monoid instances (as was the case in 0.4 and earlier).
10+ * Fix the argument order of Data.HashMap.Monoidal.insert
11+ * Remove Data.HashMap.Monoidal.insertOrReplace as it is now identical to Data.HashMap.Monoidal.insert
12+ * Added Data.HashMap.Monoidal.insertWith and Data.HashMap.Monoidal.fromListWith
13+
14+# 0.5.0.1
15+
16+ * Add a flag, `split-these`, to select whether to use the new "these"/"semialign" packages or the older combined "these" package.
17+ * Add default.nix to make it easier to hack on this package in nix
18+
19+# 0.5.0.0
20+
21+ * Added Data.IntMap.Monoidal and Data.IntMap.Monoidal.Strict, corresponding to Data.IntMap and Data.IntMap.Strict
22+ * Make `fromList`, `insert`, and `mapKeys` from `Data.Map.Monoidal` and `Data.Map.Monoidal.Strict` require `Semigroup` on values to properly capture monoidal behavior instead of reverting to the left-biased semantics of `Data.Map`.
23+ * Add Align instances and, for sufficiently recent versions of `these`, Semialign instances
24+ * Support `these` 0.8.0
25+
326# 0.4.0.0
427
528General changes:
6−
29+
730 * Added support for `unordered-containers < 0.2.8`
831 * Added many more functions in `Data.Map.[Strict.]Monoid`
932
README.md+4
@@ -6,3 +6,7 @@ of the values to be used when resolving conflicts between keys when merging
66structures. Sadly, these are not the semantics offered by the provided
77instances. This package provides `newtypes` with an appropriate set of
88instances and utility functions to make them usable.
9+
10+## Important Note
11+
12+This library is designed to be a drop-in replacement for `Data.Map` and similar. Absolutely no other semantic changes are introduced in functions like `fromList` or `insert`. They are still left-biased like `Data.Map`'s. The only difference is in instances for `Semigroup`.
default.nixnew file+16
@@ -0,0 +1,16 @@
1+{ mkDerivation, aeson, base, containers, deepseq, hashable, lens
2+, newtype, semialign, semigroups, stdenv, these
3+, unordered-containers
4+}:
5+mkDerivation {
6+ pname = "monoidal-containers";
7+ version = "0.6";
8+ src = ./.;
9+ libraryHaskellDepends = [
10+ aeson base containers deepseq hashable lens newtype semialign
11+ semigroups these unordered-containers
12+ ];
13+ homepage = "http://github.com/bgamari/monoidal-containers";
14+ description = "Containers with monoidal accumulation";
15+ license = stdenv.lib.licenses.bsd3;
16+}
monoidal-containers.cabal+18−7
@@ -1,5 +1,5 @@
11name: monoidal-containers
2−version: 0.4.0.0
2+version: 0.6.0.1
33synopsis: Containers with monoidal accumulation
44description:
55 Containers with merging via monoidal accumulation. The 'Monoid' instances
@@ -24,12 +24,17 @@ category: Data
2424build-type: Simple
2525cabal-version: >=1.10
2626extra-source-files: Changelog.md
27−tested-with: GHC ==7.8.4, GHC ==8.0.2, GHC ==8.2.2, GHC ==8.4.3, GHC ==8.6.1
27+tested-with: GHC ==7.8.4, GHC ==8.0.2, GHC ==8.2.2, GHC ==8.4.3, GHC ==8.6.5, GHC ==8.8.4, GHC ==8.10.2
2828
2929source-repository head
3030 type: git
3131 location: git://github.com/bgamari/monoidal-containers
3232
33+flag split-these
34+ description: Use split these/semialign packages
35+ manual: False
36+ default: True
37+
3338library
3439 exposed-modules: Data.Map.Monoidal
3540 Data.HashMap.Monoidal
@@ -41,15 +46,21 @@ library
4146 GeneralizedNewtypeDeriving,
4247 DeriveTraversable,
4348 DeriveDataTypeable
44− build-depends: base >=4.5 && <4.13,
45− aeson >=1.0 && <1.5,
49+ build-depends: base >=4.7 && <4.16,
50+ aeson >=1.0 && <1.6,
4651 containers >=0.5 && <0.7,
4752 deepseq >=1.3 && <1.5,
4853 unordered-containers >= 0.2 && < 0.3,
49− hashable >= 1.2 && < 1.3,
54+ hashable >= 1.2 && < 1.4,
5055 lens >=4.4 && <5,
5156 newtype >=0.2 && <0.3,
52− semigroups >= 0.18 && < 0.19,
53− these
57+ semigroups >= 0.18 && < 0.20
58+
59+ if flag(split-these)
60+ build-depends: these >= 1 && <1.2,
61+ semialign >=1 && <1.2
62+ else
63+ build-depends: these >= 0.7 && <0.9
64+
5465 hs-source-dirs: src
5566 default-language: Haskell2010
src/Data/HashMap/Monoidal.hs+46−24
@@ -20,6 +20,7 @@ module Data.HashMap.Monoidal
2020 -- * Often-needed functions
2121 , toList
2222 , fromList
23+ , fromListWith
2324 , singleton
2425 , size
2526 , member
@@ -31,7 +32,7 @@ module Data.HashMap.Monoidal
3132 , delete
3233 , mapKeys
3334 , insert
34− , insertOrReplace
35+ , insertWith
3536 , modify
3637 , modifyDef
3738 , map
@@ -59,6 +60,12 @@ import Data.Hashable.Lifted (Hashable1)
5960import Control.Lens
6061import Control.Newtype
6162import Data.Align
63+#ifdef MIN_VERSION_semialign
64+import Data.Semialign (Unalign)
65+#if MIN_VERSION_semialign(1,1,0)
66+import Data.Zip (Zip)
67+#endif
68+#endif
6269
6370-- | A 'HashMap' with monoidal accumulation
6471newtype MonoidalHashMap k a = MonoidalHashMap { getMonoidalHashMap :: M.HashMap k a }
@@ -66,6 +73,15 @@ newtype MonoidalHashMap k a = MonoidalHashMap { getMonoidalHashMap :: M.HashMap
6673 , Foldable, Traversable, Data, Typeable, Hashable, Align
6774#if MIN_VERSION_unordered_containers(0,2,8)
6875 , Hashable1
76+#endif
77+#if MIN_VERSION_these(0,8,0)
78+ , Semialign
79+#endif
80+#ifdef MIN_VERSION_semialign
81+ , Unalign
82+#if MIN_VERSION_semialign(1,1,0)
83+ , Zip
84+#endif
6985#endif
7086 )
7187
@@ -122,7 +138,7 @@ instance Newtype (MonoidalHashMap k a) (M.HashMap k a) where
122138#if MIN_VERSION_base(4,7,0)
123139instance (Eq k, Hashable k, Semigroup a) => Exts.IsList (MonoidalHashMap k a) where
124140 type Item (MonoidalHashMap k a) = (k, a)
125− fromList = MonoidalHashMap . M.fromListWith (<>)
141+ fromList = MonoidalHashMap . M.fromList
126142 {-# INLINE fromList #-}
127143 toList = M.toList . unpack
128144 {-# INLINE toList #-}
@@ -179,38 +195,44 @@ keys = M.keys . unpack
179195{-# INLINE keys #-}
180196
181197-- | /O(n*log n)/. Construct a map with the supplied mappings. If the list
182−-- contains duplicate mappings, values will be merged using (mappend newVal oldVal).
183−fromList :: (Eq k, Hashable k, Semigroup a) => [(k,a)] -> MonoidalHashMap k a
184−fromList = pack . M.fromListWith (<>)
198+-- contains duplicate mappings, values will be replaced.
199+fromList :: (Eq k, Hashable k) => [(k,a)] -> MonoidalHashMap k a
200+fromList = pack . M.fromList
185201{-# INLINE fromList #-}
186202
203+-- | /O(n*log n)/. Construct a map with the supplied mappings. If the list
204+-- contains duplicate mappings, values will be merged using the provided combining function.
205+fromListWith :: (Eq k, Hashable k) => (a -> a -> a) -> [(k,a)] -> MonoidalHashMap k a
206+fromListWith f = pack . M.fromListWith f
207+{-# INLINE fromListWith #-}
208+
209+
187210-- | /O(n*log n)/. Return a list of this map's elements. The list is produced
188211-- lazily. The order of its elements is unspecified.
189212toList :: MonoidalHashMap k a -> [(k,a)]
190213toList = M.toList . unpack
191214{-# INLINE toList #-}
192215
193−-- | /O(log n)/. Insert a value on some key, if it exists -- mappend
194−-- to the existing one.
195−insert :: (Semigroup a, Hashable k, Eq k)
196− => a
197− -> k
216+-- | /O(log n)/. Insert a value on some key, if it exists replace the value.
217+insert :: (Hashable k, Eq k)
218+ => k
219+ -> a
198220 -> MonoidalHashMap k a
199221 -> MonoidalHashMap k a
200−insert x k = pack
201− . M.insertWith (<>) k x
222+insert k x = pack
223+ . M.insert k x
202224 . unpack
203225
204−-- | /O(log n)/. Insert a value on some key with a function, if value
205−-- under key exists -- replace it.
206−insertOrReplace :: (Semigroup a, Hashable k, Eq k)
207− => a
208− -> k
209− -> MonoidalHashMap k a
210− -> MonoidalHashMap k a
211−insertOrReplace x k = pack
212− . M.insert k x
213− . unpack
226+-- | /O(log n)/. Insert a value on some key, if it exists apply the combining function.
227+insertWith :: (Hashable k, Eq k)
228+ => (a -> a -> a)
229+ -> k
230+ -> a
231+ -> MonoidalHashMap k a
232+ -> MonoidalHashMap k a
233+insertWith f k x = pack
234+ . M.insertWith f k x
235+ . unpack
214236
215237-- | /O(log n)/. Modify a value on some key with a function, if value
216238-- under key doesn't exist -- use mempty.
@@ -225,7 +247,7 @@ modify f k = pack
225247
226248-- | /O(log n)/. Modify a value on some key with a function, providing
227249-- a default value if that key doesn't exist.
228−modifyDef :: (Semigroup a, Hashable k, Eq k)
250+modifyDef :: (Hashable k, Eq k)
229251 => a -> (a -> a)
230252 -> k -> MonoidalHashMap k a
231253 -> MonoidalHashMap k a
@@ -236,7 +258,7 @@ modifyDef d f k = pack
236258
237259-- | /O(n)/. Map a function to each key of a map, if it will result
238260-- in duplicated mappings, their values will be merged in unspecified order
239−mapKeys :: (Semigroup a, Hashable k, Eq k, Hashable k', Eq k')
261+mapKeys :: (Hashable k, Eq k, Hashable k', Eq k')
240262 => (k -> k') -> MonoidalHashMap k a -> MonoidalHashMap k' a
241263mapKeys f = fromList
242264 . fmap (\(k, v) -> (f k, v))
src/Data/IntMap/Monoidal.hs+21−38
@@ -151,13 +151,29 @@ import Data.Aeson(FromJSON, ToJSON, FromJSON1, ToJSON1)
151151import Data.Functor.Classes
152152#endif
153153import Data.Align
154+#ifdef MIN_VERSION_semialign
155+import Data.Semialign (Unalign)
156+#if MIN_VERSION_semialign(1,1,0)
157+import Data.Zip (Zip)
158+#endif
159+#endif
154160
155161-- | An 'IntMap' with monoidal accumulation
156162newtype MonoidalIntMap a = MonoidalIntMap { getMonoidalIntMap :: M.IntMap a }
157163 deriving (Show, Read, Functor, Eq, Ord, NFData,
158164 Foldable, Traversable,
159165 FromJSON, ToJSON, FromJSON1, ToJSON1,
160− Data, Typeable, Align)
166+ Data, Typeable, Align
167+#if MIN_VERSION_these(0,8,0)
168+ , Semialign
169+#endif
170+#ifdef MIN_VERSION_semialign
171+ , Unalign
172+#if MIN_VERSION_semialign(1,1,0)
173+ , Zip
174+#endif
175+#endif
176+ )
161177
162178#if MIN_VERSION_containers(0,5,9)
163179deriving instance Eq1 MonoidalIntMap
@@ -477,8 +493,8 @@ toList :: forall a. MonoidalIntMap a -> [(Int, a)]
477493toList = coerce (M.toList :: M.IntMap a -> [(Int, a)])
478494{-# INLINE toList #-}
479495
480−fromList :: forall a. Semigroup a => [(Int, a)] -> MonoidalIntMap a
481−fromList = fromListWith (<>)
496+fromList :: forall a. [(Int, a)] -> MonoidalIntMap a
497+fromList = coerce (M.fromList :: [(Int, a)] -> M.IntMap a)
482498{-# INLINE fromList #-}
483499
484500fromListWith :: forall a. (a -> a -> a) -> [(Int, a)] -> MonoidalIntMap a
@@ -497,8 +513,8 @@ toDescList :: forall a. MonoidalIntMap a -> [(Int, a)]
497513toDescList = coerce (M.toDescList :: M.IntMap a -> [(Int, a)])
498514{-# INLINE toDescList #-}
499515
500−fromAscList :: forall a. Semigroup a => [(Int, a)] -> MonoidalIntMap a
501−fromAscList = fromAscListWith (<>)
516+fromAscList :: forall a. [(Int, a)] -> MonoidalIntMap a
517+fromAscList = coerce (M.fromAscList :: [(Int, a)] -> M.IntMap a)
502518{-# INLINE fromAscList #-}
503519
504520fromAscListWith :: forall a. (a -> a -> a) -> [(Int, a)] -> MonoidalIntMap a
@@ -577,26 +593,6 @@ isProperSubmapOfBy :: forall a b. (a -> b -> Bool) -> MonoidalIntMap a -> Monoid
577593isProperSubmapOfBy = coerce (M.isProperSubmapOfBy :: (a -> b -> Bool) -> M.IntMap a -> M.IntMap b -> Bool)
578594{-# INLINE isProperSubmapOfBy #-}
579595
580−-- lookupIndex :: forall a. Ord k => k -> MonoidalIntMap a -> Maybe Int
581−-- lookupIndex = coerce (M.lookupIndex :: k -> M.IntMap a -> Maybe Int)
582−-- {-# INLINE lookupIndex #-}
583−
584−-- findIndex :: forall a. Ord k => k -> MonoidalIntMap a -> Int
585−-- findIndex = coerce (M.findIndex :: k -> M.IntMap a -> Int)
586−-- {-# INLINE findIndex #-}
587−
588−-- elemAt :: forall a. Int -> MonoidalIntMap a -> (Int, a)
589−-- elemAt = coerce (M.elemAt :: Int -> M.IntMap a -> (Int, a))
590−-- {-# INLINE elemAt #-}
591−
592−-- updateAt :: forall a. (Int -> a -> Maybe a) -> Int -> MonoidalIntMap a -> MonoidalIntMap a
593−-- updateAt = coerce (M.updateAt :: (Int -> a -> Maybe a) -> Int -> M.IntMap a -> M.IntMap a)
594−-- {-# INLINE updateAt #-}
595−
596−-- deleteAt :: forall a. Int -> MonoidalIntMap a -> MonoidalIntMap a
597−-- deleteAt = coerce (M.deleteAt :: Int -> M.IntMap a -> M.IntMap a)
598−-- {-# INLINE deleteAt #-}
599−
600596findMin :: forall a. MonoidalIntMap a -> (Int, a)
601597findMin = coerce (M.findMin :: M.IntMap a -> (Int, a))
602598{-# INLINE findMin #-}
@@ -653,16 +649,3 @@ maxViewWithKey :: forall a. MonoidalIntMap a -> Maybe ((Int, a), MonoidalIntMap
653649maxViewWithKey = coerce (M.maxViewWithKey :: M.IntMap a -> Maybe ((Int, a), M.IntMap a))
654650{-# INLINE maxViewWithKey #-}
655651
656−-- showTree :: forall a. (Show k, Show a) => MonoidalIntMap a -> String
657−-- showTree = coerce (M.showTree :: (Show k, Show a) => M.IntMap a -> String)
658−-- {-# INLINE showTree #-}
659−
660−-- showTreeWith :: forall a. (Int -> a -> String) -> Bool -> Bool -> MonoidalIntMap a -> String
661−-- showTreeWith = coerce (M.showTreeWith :: (Int -> a -> String) -> Bool -> Bool -> M.IntMap a -> String)
662−-- {-# INLINE showTreeWith #-}
663−
664−--valid :: forall a. Ord k => MonoidalIntMap a -> Bool
665−--valid = coerce (M.valid :: Ord k => M.IntMap a -> Bool)
666−--{-# INLINE valid #-}
667−
668−
src/Data/IntMap/Monoidal/Strict.hs+21−37
@@ -151,13 +151,29 @@ import Data.Aeson(FromJSON, ToJSON, FromJSON1, ToJSON1)
151151import Data.Functor.Classes
152152#endif
153153import Data.Align
154+#ifdef MIN_VERSION_semialign
155+import Data.Semialign (Unalign)
156+#if MIN_VERSION_semialign(1,1,0)
157+import Data.Zip (Zip)
158+#endif
159+#endif
154160
155161-- | An 'IntMap' with monoidal accumulation
156162newtype MonoidalIntMap a = MonoidalIntMap { getMonoidalIntMap :: M.IntMap a }
157163 deriving (Show, Read, Functor, Eq, Ord, NFData,
158164 Foldable, Traversable,
159165 FromJSON, ToJSON, FromJSON1, ToJSON1,
160− Data, Typeable, Align)
166+ Data, Typeable, Align
167+#if MIN_VERSION_these(0,8,0)
168+ , Semialign
169+#endif
170+#ifdef MIN_VERSION_semialign
171+ , Unalign
172+#if MIN_VERSION_semialign(1,1,0)
173+ , Zip
174+#endif
175+#endif
176+ )
161177
162178#if MIN_VERSION_containers(0,5,9)
163179deriving instance Eq1 MonoidalIntMap
@@ -477,8 +493,8 @@ toList :: forall a. MonoidalIntMap a -> [(Int, a)]
477493toList = coerce (M.toList :: M.IntMap a -> [(Int, a)])
478494{-# INLINE toList #-}
479495
480−fromList :: forall a. Semigroup a => [(Int, a)] -> MonoidalIntMap a
481−fromList = fromListWith (<>)
496+fromList :: forall a. [(Int, a)] -> MonoidalIntMap a
497+fromList = coerce (M.fromList :: [(Int, a)] -> M.IntMap a)
482498{-# INLINE fromList #-}
483499
484500fromListWith :: forall a. (a -> a -> a) -> [(Int, a)] -> MonoidalIntMap a
@@ -497,8 +513,8 @@ toDescList :: forall a. MonoidalIntMap a -> [(Int, a)]
497513toDescList = coerce (M.toDescList :: M.IntMap a -> [(Int, a)])
498514{-# INLINE toDescList #-}
499515
500−fromAscList :: forall a. Semigroup a => [(Int, a)] -> MonoidalIntMap a
501−fromAscList = fromAscListWith (<>)
516+fromAscList :: forall a. [(Int, a)] -> MonoidalIntMap a
517+fromAscList = coerce (M.fromAscList :: [(Int, a)] -> M.IntMap a)
502518{-# INLINE fromAscList #-}
503519
504520fromAscListWith :: forall a. (a -> a -> a) -> [(Int, a)] -> MonoidalIntMap a
@@ -577,26 +593,6 @@ isProperSubmapOfBy :: forall a b. (a -> b -> Bool) -> MonoidalIntMap a -> Monoid
577593isProperSubmapOfBy = coerce (M.isProperSubmapOfBy :: (a -> b -> Bool) -> M.IntMap a -> M.IntMap b -> Bool)
578594{-# INLINE isProperSubmapOfBy #-}
579595
580−--lookupIndex :: forall a. Ord k => k -> MonoidalIntMap a -> Maybe Int
581−--lookupIndex = coerce (M.lookupIndex :: k -> M.IntMap a -> Maybe Int)
582−--{-# INLINE lookupIndex #-}
583−
584−--findIndex :: forall a. Ord k => k -> MonoidalIntMap a -> Int
585−--findIndex = coerce (M.findIndex :: k -> M.IntMap a -> Int)
586−--{-# INLINE findIndex #-}
587−
588−--elemAt :: forall a. Int -> MonoidalIntMap a -> (Int, a)
589−--elemAt = coerce (M.elemAt :: Int -> M.IntMap a -> (Int, a))
590−--{-# INLINE elemAt #-}
591−
592−--updateAt :: forall a. (Int -> a -> Maybe a) -> Int -> MonoidalIntMap a -> MonoidalIntMap a
593−--updateAt = coerce (M.updateAt :: (Int -> a -> Maybe a) -> Int -> M.IntMap a -> M.IntMap a)
594−--{-# INLINE updateAt #-}
595−
596−--deleteAt :: forall a. Int -> MonoidalIntMap a -> MonoidalIntMap a
597−--deleteAt = coerce (M.deleteAt :: Int -> M.IntMap a -> M.IntMap a)
598−--{-# INLINE deleteAt #-}
599−
600596findMin :: forall a. MonoidalIntMap a -> (Int, a)
601597findMin = coerce (M.findMin :: M.IntMap a -> (Int, a))
602598{-# INLINE findMin #-}
@@ -653,15 +649,3 @@ maxViewWithKey :: forall a. MonoidalIntMap a -> Maybe ((Int, a), MonoidalIntMap
653649maxViewWithKey = coerce (M.maxViewWithKey :: M.IntMap a -> Maybe ((Int, a), M.IntMap a))
654650{-# INLINE maxViewWithKey #-}
655651
656−-- showTree :: forall a. (Show k, Show a) => MonoidalIntMap a -> String
657−-- showTree = coerce (M.showTree :: (Show k, Show a) => M.IntMap a -> String)
658−-- {-# INLINE showTree #-}
659−
660−-- showTreeWith :: forall a. (Int -> a -> String) -> Bool -> Bool -> MonoidalIntMap a -> String
661−-- showTreeWith = coerce (M.showTreeWith :: (Int -> a -> String) -> Bool -> Bool -> M.IntMap a -> String)
662−-- {-# INLINE showTreeWith #-}
663−
664−--valid :: forall a. Ord k => MonoidalIntMap a -> Bool
665−--valid = coerce (M.valid :: Ord k => M.IntMap a -> Bool)
666−--{-# INLINE valid #-}
667−
src/Data/Map/Monoidal.hs+42−8
@@ -151,13 +151,29 @@ import Data.Aeson(FromJSON, ToJSON, FromJSON1, ToJSON1)
151151import Data.Functor.Classes
152152#endif
153153import Data.Align
154+#ifdef MIN_VERSION_semialign
155+import Data.Semialign (Unalign)
156+#if MIN_VERSION_semialign(1,1,0)
157+import Data.Zip (Zip)
158+#endif
159+#endif
154160
155161-- | A 'Map' with monoidal accumulation
156162newtype MonoidalMap k a = MonoidalMap { getMonoidalMap :: M.Map k a }
157− deriving (Show, Read, Functor, Eq, Ord, NFData,
158− Foldable, Traversable,
159− FromJSON, ToJSON, FromJSON1, ToJSON1,
160− Data, Typeable, Align)
163+ deriving ( Show, Read, Functor, Eq, Ord, NFData
164+ , Foldable, Traversable
165+ , FromJSON, ToJSON, FromJSON1, ToJSON1
166+ , Data, Typeable, Align
167+#if MIN_VERSION_these(0,8,0)
168+ , Semialign
169+#endif
170+#ifdef MIN_VERSION_semialign
171+ , Unalign
172+#if MIN_VERSION_semialign(1,1,0)
173+ , Zip
174+#endif
175+#endif
176+ )
161177
162178#if MIN_VERSION_containers(0,5,9)
163179deriving instance (Ord k) => Eq1 (MonoidalMap k)
@@ -427,6 +443,24 @@ mapKeysWith :: forall k1 k2 a. Ord k2 => (a -> a -> a) -> (k1 -> k2) -> Monoidal
427443mapKeysWith = coerce (M.mapKeysWith :: (a -> a -> a) -> (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
428444{-# INLINE mapKeysWith #-}
429445
446+-- | /O(n)/.
447+-- @'mapKeysMonotonic' f s == 'mapKeys' f s@, but works only when @f@
448+-- is strictly increasing (both monotonic and injective).
449+-- That is, for any values @x@ and @y@, if @x@ < @y@ then @f x@ < @f y@
450+-- and @f@ is injective (i.e. it never maps two input keys to the same output key).
451+-- /The precondition is not checked./
452+-- Semi-formally, we have:
453+--
454+-- > and [x < y ==> f x < f y | x <- ls, y <- ls]
455+-- > ==> mapKeysMonotonic f s == mapKeys f s
456+-- > where ls = keys s
457+--
458+-- This means that @f@ maps distinct original keys to distinct resulting keys.
459+-- This function has better performance than 'mapKeys'.
460+--
461+-- > mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")]) == fromList [(6, "b"), (10, "a")]
462+-- > valid (mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")])) == True
463+-- > valid (mapKeysMonotonic (\ _ -> 1) (fromList [(5,"a"), (3,"b")])) == False
430464mapKeysMonotonic :: forall k1 k2 a. (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
431465mapKeysMonotonic = coerce (M.mapKeysMonotonic :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
432466{-# INLINE mapKeysMonotonic #-}
@@ -479,8 +513,8 @@ toList :: forall k a. MonoidalMap k a -> [(k, a)]
479513toList = coerce (M.toList :: M.Map k a -> [(k, a)])
480514{-# INLINE toList #-}
481515
482−fromList :: forall k a. (Ord k, Semigroup a) => [(k, a)] -> MonoidalMap k a
483−fromList = fromListWith (<>)
516+fromList :: forall k a. Ord k => [(k, a)] -> MonoidalMap k a
517+fromList = coerce (M.fromList :: [(k, a)] -> M.Map k a)
484518{-# INLINE fromList #-}
485519
486520fromListWith :: forall k a. Ord k => (a -> a -> a) -> [(k, a)] -> MonoidalMap k a
@@ -499,8 +533,8 @@ toDescList :: forall k a. MonoidalMap k a -> [(k, a)]
499533toDescList = coerce (M.toDescList :: M.Map k a -> [(k, a)])
500534{-# INLINE toDescList #-}
501535
502−fromAscList :: forall k a. (Eq k, Semigroup a) => [(k, a)] -> MonoidalMap k a
503−fromAscList = fromAscListWith (<>)
536+fromAscList :: forall k a. Eq k => [(k, a)] -> MonoidalMap k a
537+fromAscList = coerce (M.fromAscList :: [(k, a)] -> M.Map k a)
504538{-# INLINE fromAscList #-}
505539
506540fromAscListWith :: forall k a. Eq k => (a -> a -> a) -> [(k, a)] -> MonoidalMap k a
src/Data/Map/Monoidal/Strict.hs+40−8
@@ -151,13 +151,29 @@ import Data.Aeson(FromJSON, ToJSON, FromJSON1, ToJSON1)
151151import Data.Functor.Classes
152152#endif
153153import Data.Align
154+#ifdef MIN_VERSION_semialign
155+import Data.Semialign (Unalign)
156+#if MIN_VERSION_semialign(1,1,0)
157+import Data.Zip (Zip)
158+#endif
159+#endif
154160
155161-- | A 'Map' with monoidal accumulation
156162newtype MonoidalMap k a = MonoidalMap { getMonoidalMap :: M.Map k a }
157− deriving (Show, Read, Functor, Eq, Ord, NFData,
158− Foldable, Traversable,
159− FromJSON, ToJSON, FromJSON1, ToJSON1,
160− Data, Typeable, Align)
163+ deriving ( Show, Read, Functor, Eq, Ord, NFData
164+ , Foldable, Traversable
165+ , FromJSON, ToJSON, FromJSON1, ToJSON1
166+ , Data, Typeable, Align
167+#if MIN_VERSION_these(0,8,0)
168+ , Semialign
169+#endif
170+#ifdef MIN_VERSION_semialign
171+ , Unalign
172+#if MIN_VERSION_semialign(1,1,0)
173+ , Zip
174+#endif
175+#endif
176+ )
161177
162178#if MIN_VERSION_containers(0,5,9)
163179deriving instance (Ord k) => Eq1 (MonoidalMap k)
@@ -418,14 +434,32 @@ mapAccumRWithKey :: forall k a b c. (a -> k -> b -> (a, c)) -> a -> MonoidalMap
418434mapAccumRWithKey = coerce (M.mapAccumRWithKey :: (a -> k -> b -> (a, c)) -> a -> M.Map k b -> (a, M.Map k c))
419435{-# INLINE mapAccumRWithKey #-}
420436
421−mapKeys :: forall k1 k2 a. Ord k2 => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
422−mapKeys = coerce (M.mapKeys :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
437+mapKeys :: forall k1 k2 a. (Ord k2, Semigroup a) => (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
438+mapKeys = mapKeysWith (<>)
423439{-# INLINE mapKeys #-}
424440
425441mapKeysWith :: forall k1 k2 a. Ord k2 => (a -> a -> a) -> (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
426442mapKeysWith = coerce (M.mapKeysWith :: (a -> a -> a) -> (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
427443{-# INLINE mapKeysWith #-}
428444
445+-- | /O(n)/.
446+-- @'mapKeysMonotonic' f s == 'mapKeys' f s@, but works only when @f@
447+-- is strictly increasing (both monotonic and injective).
448+-- That is, for any values @x@ and @y@, if @x@ < @y@ then @f x@ < @f y@
449+-- and @f@ is injective (i.e. it never maps two input keys to the same output key).
450+-- /The precondition is not checked./
451+-- Semi-formally, we have:
452+--
453+-- > and [x < y ==> f x < f y | x <- ls, y <- ls]
454+-- > ==> mapKeysMonotonic f s == mapKeys f s
455+-- > where ls = keys s
456+--
457+-- This means that @f@ maps distinct original keys to distinct resulting keys.
458+-- This function has better performance than 'mapKeys'.
459+--
460+-- > mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")]) == fromList [(6, "b"), (10, "a")]
461+-- > valid (mapKeysMonotonic (\ k -> k * 2) (fromList [(5,"a"), (3,"b")])) == True
462+-- > valid (mapKeysMonotonic (\ _ -> 1) (fromList [(5,"a"), (3,"b")])) == False
429463mapKeysMonotonic :: forall k1 k2 a. (k1 -> k2) -> MonoidalMap k1 a -> MonoidalMap k2 a
430464mapKeysMonotonic = coerce (M.mapKeysMonotonic :: (k1 -> k2) -> M.Map k1 a -> M.Map k2 a)
431465{-# INLINE mapKeysMonotonic #-}
@@ -665,5 +699,3 @@ maxViewWithKey = coerce (M.maxViewWithKey :: M.Map k a -> Maybe ((k, a), M.Map k
665699valid :: forall k a. Ord k => MonoidalMap k a -> Bool
666700valid = coerce (M.valid :: Ord k => M.Map k a -> Bool)
667701{-# INLINE valid #-}
668−
669−