From 5414d8e9d58a4a77857781a785bc3b499d2688f5 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 12:02:41 +0000 Subject: [PATCH 1/8] Migrate to Spago and update CI --- .github/workflows/ci.yml | 30 +++++++++++++++--------------- .gitignore | 5 +++-- .tidyrc.json | 10 ++++++++++ CHANGELOG.md | 10 ++++------ bench/spago.yaml | 18 ++++++++++++++++++ bower.json | 37 ------------------------------------- package.json | 18 ++++++++++-------- spago.yaml | 38 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 98 insertions(+), 68 deletions(-) create mode 100644 .tidyrc.json create mode 100644 bench/spago.yaml delete mode 100644 bower.json create mode 100644 spago.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c69237a..1a68fb0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,30 +6,30 @@ on: pull_request: branches: [master] +permissions: + contents: read + jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - - uses: purescript-contrib/setup-purescript@main - with: - purescript: "unstable" + - uses: actions/checkout@v6 - - uses: actions/setup-node@v2 + - uses: actions/setup-node@v6 with: - node-version: "14.x" + node-version: "24" - name: Install dependencies - run: | - npm install -g bower - npm install - bower install --production + run: npm install - name: Build source - run: npm run-script build + run: npm run build - name: Run tests - run: | - bower install - npm run-script test --if-present + run: npm test + + - name: Build benchmarks + run: npm run bench:build + + - name: Check formatting + run: npm run format:check diff --git a/.gitignore b/.gitignore index 7224331..b76b25f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ /.* !/.gitignore -!/.eslintrc.json +!/.tidyrc.json !/.github/ -package-lock.json +/package-lock.json +/spago.lock /bower_components/ /node_modules/ /output/ diff --git a/.tidyrc.json b/.tidyrc.json new file mode 100644 index 0000000..9e39cf1 --- /dev/null +++ b/.tidyrc.json @@ -0,0 +1,10 @@ +{ + "importSort": "ide", + "importWrap": "source", + "indent": 2, + "operatorsFile": null, + "ribbon": 1, + "typeArrowPlacement": "first", + "unicode": "never", + "width": null +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 0adb6e8..543e0b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,14 @@ Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] - -Breaking changes: +## [v3.3.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.3.0) New features: -- Add `Data.Set.range` which works like `Data.Map.submap`. - -Bugfixes: +- Add `Data.Set.range` which works like `Data.Map.submap` (#75 by @tomcumming). Other improvements: +- Migrate from Bower/Pulp to Spago and update CI. +- Improve documentation for Map functions with bounds parameters (#74 by @aranchelk). ## [v3.2.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.2.0) - 2024-03-19 diff --git a/bench/spago.yaml b/bench/spago.yaml new file mode 100644 index 0000000..7cf2e1c --- /dev/null +++ b/bench/spago.yaml @@ -0,0 +1,18 @@ +package: + name: ordered-collections-bench + build: + strict: true + pedanticPackages: true + dependencies: + - console: ">=6.0.0 <7.0.0" + - control: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" + - foldable-traversable: ">=6.0.0 <7.0.0" + - lists: ">=7.0.0 <8.0.0" + - maybe: ">=6.0.0 <7.0.0" + - minibench: ">=4.0.0 <5.0.0" + - ordered-collections: "*" + - partial: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.0 <7.0.0" + - tuples: ">=7.0.0 <8.0.0" + - unfoldable: ">=6.0.0 <7.0.0" diff --git a/bower.json b/bower.json deleted file mode 100644 index 464ea8b..0000000 --- a/bower.json +++ /dev/null @@ -1,37 +0,0 @@ -{ - "name": "purescript-ordered-collections", - "homepage": "https://github.com/purescript/purescript-ordered-collections", - "repository": { - "type": "git", - "url": "https://github.com/purescript/purescript-ordered-collections.git" - }, - "license": "BSD-3-Clause", - "ignore": [ - "**/.*", - "bower_components", - "node_modules", - "output", - "test", - "bower.json", - "package.json" - ], - "dependencies": { - "purescript-arrays": "^7.0.0", - "purescript-foldable-traversable": "^6.0.0", - "purescript-gen": "^4.0.0", - "purescript-lists": "^7.0.0", - "purescript-maybe": "^6.0.0", - "purescript-partial": "^4.0.0", - "purescript-prelude": "^6.0.0", - "purescript-st": "^6.0.0", - "purescript-tailrec": "^6.0.0", - "purescript-tuples": "^7.0.0", - "purescript-unfoldable": "^6.0.0" - }, - "devDependencies": { - "purescript-assert": "^6.0.0", - "purescript-console": "^6.0.0", - "purescript-minibench": "^4.0.0", - "purescript-quickcheck": "^8.0.1" - } -} diff --git a/package.json b/package.json index 8d388d3..1d74be9 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,18 @@ { "private": true, "scripts": { - "clean": "rimraf output && rimraf .pulp-cache", - "build": "pulp build -- --censor-lib --strict", - "test": "pulp build -I test -- --censor-lib && node --input-type=module --eval \"import * as Test_Main from './output/Test.Main/index.js';Test_Main.main()\"", - "bench:build": "purs compile \"bench/**/*.purs\" \"src/**/*.purs\" \"bower_components/*/src/**/*.purs\"", + "build": "spago build -p ordered-collections", + "test": "spago test -p ordered-collections", + "bench:build": "spago build -p ordered-collections-bench", "bench:run": "node --expose-gc --input-type=module --eval \"import * as Bench_Main from './output/Bench.Main/index.js';Bench_Main.main()\"", - "bench": "npm run bench:build && npm run bench:run" + "bench": "npm run bench:build && npm run bench:run", + "format": "purs-tidy format-in-place src test bench/src && oxfmt --write .", + "format:check": "purs-tidy check src test bench/src && oxfmt --check ." }, "devDependencies": { - "pulp": "16.0.0-0", - "purescript-psa": "^0.8.2", - "rimraf": "^3.0.2" + "oxfmt": "^0.73.0", + "purescript": "^0.15.16", + "purs-tidy": "^0.11.1", + "spago": "^1.0.5" } } diff --git a/spago.yaml b/spago.yaml new file mode 100644 index 0000000..7f475bf --- /dev/null +++ b/spago.yaml @@ -0,0 +1,38 @@ +package: + name: ordered-collections + description: Purely-functional map and set data structures. + build: + strict: true + pedanticPackages: true + publish: + version: 3.3.0 + license: BSD-3-Clause + location: + githubOwner: purescript + githubRepo: purescript-ordered-collections + dependencies: + - arrays: ">=7.0.0 <8.0.0" + - control: ">=6.0.0 <7.0.0" + - foldable-traversable: ">=6.0.0 <7.0.0" + - functions: ">=6.0.0 <7.0.0" + - gen: ">=4.0.0 <5.0.0" + - lists: ">=7.0.0 <8.0.0" + - maybe: ">=6.0.0 <7.0.0" + - newtype: ">=5.0.0 <6.0.0" + - partial: ">=4.0.0 <5.0.0" + - prelude: ">=6.0.0 <7.0.0" + - safe-coerce: ">=2.0.0 <3.0.0" + - tailrec: ">=6.0.0 <7.0.0" + - tuples: ">=7.0.0 <8.0.0" + - unfoldable: ">=6.0.0 <7.0.0" + test: + main: Test.Main + strict: false + pedanticPackages: true + dependencies: + - assert: ">=6.0.0 <7.0.0" + - console: ">=6.0.0 <7.0.0" + - effect: ">=4.0.0 <5.0.0" + - exceptions: ">=6.0.0 <7.0.0" + - quickcheck: ">=8.0.1 <9.0.0" +workspace: {} From ff5116ef15c8c4c7deb7e687021e79c5cc27fce5 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 12:02:42 +0000 Subject: [PATCH 2/8] Move benchmark modules into Spago source directory --- bench/{ => src}/Bench/Data/Map.purs | 0 bench/{ => src}/Bench/Data/Map2a0bff.purs | 0 bench/{ => src}/Bench/Data/Mapf149d5.purs | 0 bench/{ => src}/Bench/Main.purs | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename bench/{ => src}/Bench/Data/Map.purs (100%) rename bench/{ => src}/Bench/Data/Map2a0bff.purs (100%) rename bench/{ => src}/Bench/Data/Mapf149d5.purs (100%) rename bench/{ => src}/Bench/Main.purs (100%) diff --git a/bench/Bench/Data/Map.purs b/bench/src/Bench/Data/Map.purs similarity index 100% rename from bench/Bench/Data/Map.purs rename to bench/src/Bench/Data/Map.purs diff --git a/bench/Bench/Data/Map2a0bff.purs b/bench/src/Bench/Data/Map2a0bff.purs similarity index 100% rename from bench/Bench/Data/Map2a0bff.purs rename to bench/src/Bench/Data/Map2a0bff.purs diff --git a/bench/Bench/Data/Mapf149d5.purs b/bench/src/Bench/Data/Mapf149d5.purs similarity index 100% rename from bench/Bench/Data/Mapf149d5.purs rename to bench/src/Bench/Data/Mapf149d5.purs diff --git a/bench/Bench/Main.purs b/bench/src/Bench/Main.purs similarity index 100% rename from bench/Bench/Main.purs rename to bench/src/Bench/Main.purs From 891a818789ba9254fa7bea0774e84f467811b56e Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 12:03:35 +0000 Subject: [PATCH 3/8] Put multiline conditional branches on separate lines --- bench/src/Bench/Data/Map2a0bff.purs | 6 ++++-- bench/src/Bench/Data/Mapf149d5.purs | 6 ++++-- src/Data/Map/Internal.purs | 6 ++++-- test/Test/Data/Map.purs | 6 ++++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/bench/src/Bench/Data/Map2a0bff.purs b/bench/src/Bench/Data/Map2a0bff.purs index 5e10932..0404d6f 100644 --- a/bench/src/Bench/Data/Map2a0bff.purs +++ b/bench/src/Bench/Data/Map2a0bff.purs @@ -424,8 +424,10 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | let m' = submap mmin mmax m in -- | if (maybe true (\min -> min <= key) mmin && -- | maybe true (\max -> max >= key) mmax) --- | then lookup key m == lookup key m' --- | else not (member key m') +-- | then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton diff --git a/bench/src/Bench/Data/Mapf149d5.purs b/bench/src/Bench/Data/Mapf149d5.purs index 9a6e903..4700e90 100644 --- a/bench/src/Bench/Data/Mapf149d5.purs +++ b/bench/src/Bench/Data/Mapf149d5.purs @@ -439,8 +439,10 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | let m' = submap mmin mmax m in -- | if (maybe true (\min -> min <= key) mmin && -- | maybe true (\max -> max >= key) mmax) --- | then lookup key m == lookup key m' --- | else not (member key m') +-- | then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton diff --git a/src/Data/Map/Internal.purs b/src/Data/Map/Internal.purs index 39ae443..2b50f1a 100644 --- a/src/Data/Map/Internal.purs +++ b/src/Data/Map/Internal.purs @@ -441,8 +441,10 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | let m' = submap mmin mmax m in -- | if (maybe true (\min -> min <= key) mmin && -- | maybe true (\max -> max >= key) mmax) --- | then lookup key m == lookup key m' --- | else not (member key m') +-- | then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index c1bb1ef..c7d86ef 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -372,8 +372,10 @@ mapTests = do in (if (maybe true (\min -> min <= key) mmin && maybe true (\max -> max >= key) mmax) - then M.lookup key m == M.lookup key m' - else (not (M.member key m'))) + then + M.lookup key m == M.lookup key m' + else + (not (M.member key m'))) "m: " <> show m <> ", mmin: " <> show mmin <> ", mmax: " <> show mmax From 8acde06876de49bde12339202815cd5b36afa57a Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 12:05:50 +0000 Subject: [PATCH 4/8] Format PureScript and repository files --- CHANGELOG.md | 16 +- bench/src/Bench/Data/Map.purs | 21 +- bench/src/Bench/Data/Map2a0bff.purs | 321 +++++++++++++++------------- bench/src/Bench/Data/Mapf149d5.purs | 317 ++++++++++++++------------- src/Data/Map.purs | 4 +- src/Data/Map/Gen.purs | 4 +- src/Data/Map/Internal.purs | 24 ++- src/Data/Set/NonEmpty.purs | 2 +- test/Test/Data/Map.purs | 318 +++++++++++++++------------ test/Test/Data/Set.purs | 41 ++-- 10 files changed, 596 insertions(+), 472 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 543e0b5..4cf3601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,35 +5,42 @@ Notable changes to this project are documented in this file. The format is based ## [v3.3.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.3.0) New features: + - Add `Data.Set.range` which works like `Data.Map.submap` (#75 by @tomcumming). Other improvements: + - Migrate from Bower/Pulp to Spago and update CI. - Improve documentation for Map functions with bounds parameters (#74 by @aranchelk). ## [v3.2.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.2.0) - 2024-03-19 New features: -* Add `Data.Map.any` (#73 by @flip111) -* Add `Data.Map.anyWithKey` (#73 by @flip111) + +- Add `Data.Map.any` (#73 by @flip111) +- Add `Data.Map.anyWithKey` (#73 by @flip111) ## [v3.1.1](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.1.1) - 2023-08-30 Bugfixes: + - Fixes Map/Set difference, which was broken due to the internal rewrite (#68 @natefaubion) ## [v3.1.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.1.0) - 2023-08-11 Other improvements: + - Speed up `difference` by using 1 fold instead of 2 (#64 by @JordanMartinez) - Rewrite internals for performance improvements (#66 by @natefaubion) ## [v3.0.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.0.0) - 2022-04-27 Breaking changes: + - Update project and deps to PureScript v0.15.0 (#53 by @JordanMartinez) New features: + - Exported `Data.Map.Internal` data constructors (#52 by @natefaubion) - Add unbiased `Semigroup`/`Monoid` instances to `Map` with `Warn` (#54 by @JordanMartinez) - Improved speed of `foldr`, `foldl`, `foldMap`, `foldlWithIndex`, `foldrWithIndex`, `foldMapWithIndex`, `unionWith`, `keys` and `values` (#60 by @xgrommx, #61 and #62 by @JordanMartinez) @@ -43,21 +50,25 @@ Bugfixes: ## [v2.0.2](https://github.com/purescript/purescript-ordered-collections/releases/tag/v2.0.2) - 2021-08-27 New features: + - Added `toggle` for `Set` ## [v2.0.1](https://github.com/purescript/purescript-ordered-collections/releases/tag/v2.0.1) - 2021-04-20 Other improvements: + - Fix warnings revealed by v0.14.1 PureScript release (#48 by @JordanMartinez) ## [v2.0.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v2.0.0) - 2021-02-26 Breaking changes: + - Added support for PureScript 0.14 and dropped support for all previous versions (#35, #43) - Dropped `Map`'s `Semigroup` and `Monoid` instances and provide unbiased instances via a `SemigroupMap` newtype instead (#38) - Updated the `Show` instances for (non empty) sets (#46) New features: + - Added `Apply` instance for `Map` (#16) - Added `Alt` and `Plus` instances for `Map` (#38) - Added `catMaybes` for maps and sets (#25) @@ -66,6 +77,7 @@ New features: Bugfixes: Other improvements: + - Migrated CI to GitHub Actions and updated installation instructions to use Spago (#37) - Tests: Add parens to account for changed precedence of `::` in PureScript 0.13 (#23) - Tests: Converted from `NonEmpty Array` to `NonEmptyArray` to accommodate QuickCheck changes (#42) diff --git a/bench/src/Bench/Data/Map.purs b/bench/src/Bench/Data/Map.purs index ec49df5..a90327d 100644 --- a/bench/src/Bench/Data/Map.purs +++ b/bench/src/Bench/Data/Map.purs @@ -2,11 +2,11 @@ module Bench.Data.Map where import Prelude -import Data.List as L -import Data.Map as M import Bench.Data.Mapf149d5 as Mapf149d5 import Data.Foldable as F import Data.FoldableWithIndex as FI +import Data.List as L +import Data.Map as M import Data.Tuple (Tuple(..)) import Effect (Effect) import Effect.Console (log) @@ -109,7 +109,7 @@ benchMap = do benchWith 100 \_ -> M.size midMap' log $ "size: big map (" <> show (M.size bigMap') <> ")" - benchWith 10 \_ -> M.size bigMap' + benchWith 10 \_ -> M.size bigMap' benchFoldable = do log $ "Mapf149d5.foldr big map (" <> show size <> ")" @@ -149,9 +149,10 @@ benchMap = do benchWith 10 \_ -> FI.foldMapWithIndex (\_ _ -> unit) bigMap' benchFromFoldable = do - let natStrs = show <$> nats - natStrsPairs = (flip Tuple) unit <$> natStrs - shortPairList = L.take 10000 natStrsPairs + let + natStrs = show <$> nats + natStrsPairs = (flip Tuple) unit <$> natStrs + shortPairList = L.take 10000 natStrsPairs log $ "fromFoldable (" <> show (L.length shortPairList) <> ")" benchWith 100 \_ -> M.fromFoldable shortPairList @@ -173,10 +174,10 @@ benchMap = do benchWith 100 \_ -> M.difference midMap' midMap' log $ "Mapf149d5.difference: big map (" <> show (Mapf149d5.size bigMap) <> ")" - benchWith 10 \_ -> Mapf149d5.difference bigMap midMap + benchWith 10 \_ -> Mapf149d5.difference bigMap midMap log $ "M.difference: big map (" <> show (M.size bigMap') <> ")" - benchWith 10 \_ -> M.difference bigMap' midMap' + benchWith 10 \_ -> M.difference bigMap' midMap' benchEq = do log $ "Mapf149d5.eq: small map (" <> show (Mapf149d5.size smallMap) <> ")" @@ -192,7 +193,7 @@ benchMap = do benchWith 100 \_ -> midMap' == midMap' log $ "Mapf149d5.eq: big map (" <> show (Mapf149d5.size bigMap) <> ")" - benchWith 10 \_ -> bigMap == bigMap + benchWith 10 \_ -> bigMap == bigMap log $ "M.eq: big map (" <> show (M.size bigMap') <> ")" - benchWith 10 \_ -> bigMap' == bigMap' + benchWith 10 \_ -> bigMap' == bigMap' diff --git a/bench/src/Bench/Data/Map2a0bff.purs b/bench/src/Bench/Data/Map2a0bff.purs index 0404d6f..34a2879 100644 --- a/bench/src/Bench/Data/Map2a0bff.purs +++ b/bench/src/Bench/Data/Map2a0bff.purs @@ -52,14 +52,14 @@ import Prelude import Control.Alt (class Alt) import Control.Plus (class Plus) import Data.Eq (class Eq1) -import Data.Foldable (foldl, foldMap, foldr, class Foldable) +import Data.Foldable (class Foldable, foldMap, foldl, foldr) import Data.FoldableWithIndex (class FoldableWithIndex, foldlWithIndex, foldrWithIndex) import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex) -import Data.List (List(..), (:), length, nub) +import Data.List (List(..), length, nub, (:)) import Data.List.Lazy as LL -import Data.Maybe (Maybe(..), maybe, isJust, fromMaybe) +import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) import Data.Ord (class Ord1) -import Data.Traversable (traverse, class Traversable) +import Data.Traversable (class Traversable, traverse) import Data.TraversableWithIndex (class TraversableWithIndex, traverseWithIndex) import Data.Tuple (Tuple(Tuple), snd, uncurry) import Data.Unfoldable (class Unfoldable, unfoldr) @@ -97,14 +97,16 @@ instance semigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Semigroup (Map k v) where + ) => + Semigroup (Map k v) where append l r = unionWith append l r instance monoidSemigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Monoid (Map k v) where + ) => + Monoid (Map k v) where mempty = empty instance altMap :: Ord k => Alt (Map k) where @@ -130,9 +132,9 @@ instance bindMap :: Ord k => Bind (Map k) where bind m f = mapMaybeWithKey (\k -> lookup k <<< f) m instance foldableMap :: Foldable (Map k) where - foldl f z m = foldl f z (values m) - foldr f z m = foldr f z (values m) - foldMap f m = foldMap f (values m) + foldl f z m = foldl f z (values m) + foldr f z m = foldr f z (values m) + foldMap f m = foldMap f (values m) instance foldableWithIndexMap :: FoldableWithIndex k (Map k) where foldlWithIndex f z m = foldl (uncurry <<< (flip f)) z $ asList $ toUnfoldable m @@ -143,34 +145,34 @@ instance traversableMap :: Traversable (Map k) where traverse _ Leaf = pure Leaf traverse f (Two left k v right) = Two <$> traverse f left - <*> pure k - <*> f v - <*> traverse f right + <*> pure k + <*> f v + <*> traverse f right traverse f (Three left k1 v1 mid k2 v2 right) = Three <$> traverse f left - <*> pure k1 - <*> f v1 - <*> traverse f mid - <*> pure k2 - <*> f v2 - <*> traverse f right + <*> pure k1 + <*> f v1 + <*> traverse f mid + <*> pure k2 + <*> f v2 + <*> traverse f right sequence = traverse identity instance traversableWithIndexMap :: TraversableWithIndex k (Map k) where traverseWithIndex _ Leaf = pure Leaf traverseWithIndex f (Two left k v right) = Two <$> traverseWithIndex f left - <*> pure k - <*> f k v - <*> traverseWithIndex f right + <*> pure k + <*> f k v + <*> traverseWithIndex f right traverseWithIndex f (Three left k1 v1 mid k2 v2 right) = Three <$> traverseWithIndex f left - <*> pure k1 - <*> f k1 v1 - <*> traverseWithIndex f mid - <*> pure k2 - <*> f k2 v2 - <*> traverseWithIndex f right + <*> pure k1 + <*> f k1 v1 + <*> traverseWithIndex f mid + <*> pure k2 + <*> f k2 v2 + <*> traverseWithIndex f right asList :: forall k v. List (Tuple k v) -> List (Tuple k v) asList = identity @@ -179,18 +181,29 @@ asList = identity showTree :: forall k v. Show k => Show v => Map k v -> String showTree Leaf = "Leaf" showTree (Two left k v right) = - "Two (" <> showTree left <> - ") (" <> show k <> - ") (" <> show v <> - ") (" <> showTree right <> ")" + "Two (" <> showTree left + <> ") (" + <> show k + <> ") (" + <> show v + <> ") (" + <> showTree right + <> ")" showTree (Three left k1 v1 mid k2 v2 right) = - "Three (" <> showTree left <> - ") (" <> show k1 <> - ") (" <> show v1 <> - ") (" <> showTree mid <> - ") (" <> show k2 <> - ") (" <> show v2 <> - ") (" <> showTree right <> ")" + "Three (" <> showTree left + <> ") (" + <> show k1 + <> ") (" + <> show v1 + <> ") (" + <> showTree mid + <> ") (" + <> show k2 + <> ") (" + <> show v2 + <> ") (" + <> showTree right + <> ")" -- | An empty map empty :: forall k v. Map k v @@ -220,109 +233,108 @@ checkValid tree = length (nub (allHeights tree)) == one lookup :: forall k v. Ord k => k -> Map k v -> Maybe v lookup k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v right) = - case comp k k1 of - EQ -> Just v - LT -> go left - _ -> go right - go (Three left k1 v1 mid k2 v2 right) = - case comp k k1 of - EQ -> Just v1 - c1 -> - case c1, comp k k2 of - _ , EQ -> Just v2 - LT, _ -> go left - _ , GT -> go right - _ , _ -> go mid + comp :: k -> k -> Ordering + comp = compare + go Leaf = Nothing + go (Two left k1 v right) = + case comp k k1 of + EQ -> Just v + LT -> go left + _ -> go right + go (Three left k1 v1 mid k2 v2 right) = + case comp k k1 of + EQ -> Just v1 + c1 -> + case c1, comp k k2 of + _, EQ -> Just v2 + LT, _ -> go left + _, GT -> go right + _, _ -> go mid -- | Look up a value for the specified key, or the greatest one less than it lookupLE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupLE k = go where - comp :: k -> k -> Ordering - comp = compare + comp :: k -> k -> Ordering + comp = compare - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> Just { key: k2, value: v2 } - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right + LT -> go left + go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of + EQ -> Just { key: k2, value: v2 } + GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right + LT -> go $ Two left k1 v1 mid -- | Look up a value for the greatest key less than the specified key lookupLT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupLT k = go where - comp :: k -> k -> Ordering - comp = compare + comp :: k -> k -> Ordering + comp = compare - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMax left - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> findMax $ Two left k1 v1 mid - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> findMax left + GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right + LT -> go left + go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of + EQ -> findMax $ Two left k1 v1 mid + GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right + LT -> go $ Two left k1 v1 mid -- | Look up a value for the specified key, or the least one greater than it lookupGE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupGE k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right + comp :: k -> k -> Ordering + comp = compare + + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go right + go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go $ Two mid k2 v2 right -- | Look up a value for the least key greater than the specified key lookupGT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupGT k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMin right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> findMin $ Two mid k2 v2 right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right + comp :: k -> k -> Ordering + comp = compare + + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> findMin right + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go right + go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of + EQ -> findMin $ Two mid k2 v2 right + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go $ Two mid k2 v2 right -- | Returns the pair with the greatest key findMax :: forall k v. Map k v -> Maybe { key :: k, value :: v } findMax = go Nothing where - go acc Leaf = acc - go _ (Two _ k1 v1 right) = go (Just { key: k1, value: v1 }) right - go _ (Three _ _ _ _ k2 v2 right) = go (Just { key: k2, value: v2 }) right + go acc Leaf = acc + go _ (Two _ k1 v1 right) = go (Just { key: k1, value: v1 }) right + go _ (Three _ _ _ _ k2 v2 right) = go (Just { key: k2, value: v2 }) right -- | Returns the pair with the least key findMin :: forall k v. Map k v -> Maybe { key :: k, value :: v } findMin = go Nothing where - go acc Leaf = acc - go _ (Two left k1 v1 _) = go (Just { key: k1, value: v1 }) left - go _ (Three left k1 v1 _ _ _ _) = go (Just { key: k1, value: v1 }) left + go acc Leaf = acc + go _ (Two left k1 v1 _) = go (Just { key: k1, value: v1 }) left + go _ (Three left k1 v1 _ _ _ _) = go (Just { key: k1, value: v1 }) left -- | Fold over the entries of a given map where the key is between a lower and -- | an upper bound. Passing `Nothing` as either the lower or upper bound @@ -389,15 +401,15 @@ foldSubmapBy appendFn memptyValue kmin kmax f = Leaf -> memptyValue Two left k v right -> - (if tooSmall k then memptyValue else go left) - `appendFn` (if inBounds k then f k v else memptyValue) - `appendFn` (if tooLarge k then memptyValue else go right) + (if tooSmall k then memptyValue else go left) + `appendFn` (if inBounds k then f k v else memptyValue) + `appendFn` (if tooLarge k then memptyValue else go right) Three left k1 v1 mid k2 v2 right -> - (if tooSmall k1 then memptyValue else go left) - `appendFn` (if inBounds k1 then f k1 v1 else memptyValue) - `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) - `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) - `appendFn` (if tooLarge k2 then memptyValue else go right) + (if tooSmall k1 then memptyValue else go left) + `appendFn` (if inBounds k1 then f k1 v1 else memptyValue) + `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) + `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) + `appendFn` (if tooLarge k2 then memptyValue else go right) in go @@ -468,16 +480,16 @@ insert k v = down Nil case comp k k1 of EQ -> fromZipper ctx (Two left k v right) LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ -> down (Cons (TwoRight left k1 v1) ctx) right + _ -> down (Cons (TwoRight left k1 v1) ctx) right down ctx (Three left k1 v1 mid k2 v2 right) = case comp k k1 of EQ -> fromZipper ctx (Three left k v mid k2 v2 right) c1 -> case c1, comp k k2 of - _ , EQ -> fromZipper ctx (Three left k1 v1 mid k v right) - LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, EQ -> fromZipper ctx (Three left k1 v1 mid k v right) + LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> KickUp k v -> Map k v up Nil (KickUp left k' v' right) = Two left k' v' right @@ -514,25 +526,36 @@ pop k = down Nil Two left k1 v1 right -> case right, comp k k1 of Leaf, EQ -> Just (Tuple v1 (up ctx Leaf)) - _ , EQ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) - _ , LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ , _ -> down (Cons (TwoRight left k1 v1) ctx) right + _, EQ -> + let + max = maxNode left + in + Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) + _, LT -> down (Cons (TwoLeft k1 v1 right) ctx) left + _, _ -> down (Cons (TwoRight left k1 v1) ctx) right Three left k1 v1 mid k2 v2 right -> - let leaves = - case left, mid, right of - Leaf, Leaf, Leaf -> true - _ , _ , _ -> false - in case leaves, comp k k1, comp k k2 of - true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) - true, _ , EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) - _ , EQ, _ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) - _ , _ , EQ -> let max = maxNode mid - in Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) - _ , LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - _ , GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + let + leaves = + case left, mid, right of + Leaf, Leaf, Leaf -> true + _, _, _ -> false + in + case leaves, comp k k1, comp k k2 of + true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) + true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) + _, EQ, _ -> + let + max = maxNode left + in + Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) + _, _, EQ -> + let + max = maxNode mid + in + Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) + _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid + _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> Map k v -> Map k v up ctxs tree = @@ -567,7 +590,6 @@ pop k = down Nil Three _ _ _ _ _ _ right -> maxNode right _ -> unsafeCrashWith "The impossible happened in partial function `maxNode`." - removeMaxNode :: List (TreeContext k v) -> Map k v -> Map k v removeMaxNode ctx m = case m of @@ -577,7 +599,6 @@ pop k = down Nil Three left k1 v1 mid k2 v2 right -> removeMaxNode (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right _ -> unsafeCrashWith "The impossible happened in partial function `removeMaxNode`." - -- | Insert the value, delete a value, or update a value for a key in a map alter :: forall k v. Ord k => (Maybe v -> Maybe v) -> k -> Map k v -> Map k v alter f k m = case f (k `lookup` m) of @@ -596,7 +617,8 @@ fromFoldable = foldl (\m (Tuple k v) -> insert k v m) empty -- | Convert any foldable collection of key/value pairs to a map. -- | On key collision, the values are configurably combined. fromFoldableWith :: forall f k v. Ord k => Foldable f => (v -> v -> v) -> f (Tuple k v) -> Map k v -fromFoldableWith f = foldl (\m (Tuple k v) -> alter (combine v) k m) empty where +fromFoldableWith f = foldl (\m (Tuple k v) -> alter (combine v) k m) empty + where combine v (Just v') = Just $ f v v' combine v Nothing = Just v @@ -606,7 +628,8 @@ fromFoldableWithIndex = foldlWithIndex (\k m v -> insert k v m) empty -- | Convert a map to an unfoldable structure of key/value pairs where the keys are in ascending order toUnfoldable :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toUnfoldable m = unfoldr go (m : Nil) where +toUnfoldable m = unfoldr go (m : Nil) + where go Nil = Nothing go (hd : tl) = case hd of Leaf -> go tl @@ -627,7 +650,8 @@ toUnfoldable m = unfoldr go (m : Nil) where -- | -- | If you are unsure, use `toUnfoldable` toUnfoldableUnordered :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toUnfoldableUnordered m = unfoldr go (m : Nil) where +toUnfoldableUnordered m = unfoldr go (m : Nil) + where go Nil = Nothing go (hd : tl) = case hd of Leaf -> go tl @@ -673,9 +697,9 @@ intersectionWith f m1 m2 = go (toUnfoldable m1 :: List (Tuple k a)) (toUnfoldabl go _ Nil m = m go as@(Cons (Tuple k1 a) ass) bs@(Cons (Tuple k2 b) bss) m = case compare k1 k2 of - LT -> go ass bs m - EQ -> go ass bss (insert k1 (f a b) m) - GT -> go as bss m + LT -> go ass bs m + EQ -> go ass bss (insert k1 (f a b) m) + GT -> go as bss m -- | Compute the intersection of two maps, preferring values from the first map in the case -- | of duplicate keys. @@ -690,7 +714,8 @@ difference m1 m2 = foldl (flip delete) m1 (keys m2) -- | Test whether one map contains all of the keys and values contained in another map isSubmap :: forall k v. Ord k => Eq v => Map k v -> Map k v -> Boolean isSubmap m1 m2 = LL.all f $ (toUnfoldable m1 :: LL.List (Tuple k v)) - where f (Tuple k v) = lookup k m2 == Just v + where + f (Tuple k v) = lookup k m2 == Just v -- | Calculate the number of key/value pairs in a map size :: forall k v. Map k v -> Int @@ -717,7 +742,7 @@ filter predicate = filterWithKey $ const predicate -- | Applies a function to each key/value pair in a map, discarding entries -- | where the function returns `Nothing`. mapMaybeWithKey :: forall k a b. Ord k => (k -> a -> Maybe b) -> Map k a -> Map k b -mapMaybeWithKey f = foldrWithIndex (\k a acc → maybe acc (\b -> insert k b acc) (f k a)) empty +mapMaybeWithKey f = foldrWithIndex (\k a acc -> maybe acc (\b -> insert k b acc) (f k a)) empty -- | Applies a function to each value in a map, discarding entries where the -- | function returns `Nothing`. diff --git a/bench/src/Bench/Data/Mapf149d5.purs b/bench/src/Bench/Data/Mapf149d5.purs index 4700e90..144ee01 100644 --- a/bench/src/Bench/Data/Mapf149d5.purs +++ b/bench/src/Bench/Data/Mapf149d5.purs @@ -52,14 +52,14 @@ import Prelude import Control.Alt (class Alt) import Control.Plus (class Plus) import Data.Eq (class Eq1) -import Data.Foldable (foldl, foldMap, foldr, class Foldable) -import Data.FoldableWithIndex (class FoldableWithIndex, foldlWithIndex, foldrWithIndex, foldMapWithIndex) +import Data.Foldable (class Foldable, foldMap, foldl, foldr) +import Data.FoldableWithIndex (class FoldableWithIndex, foldMapWithIndex, foldlWithIndex, foldrWithIndex) import Data.FunctorWithIndex (class FunctorWithIndex, mapWithIndex) -import Data.List (List(..), (:), length, nub) +import Data.List (List(..), length, nub, (:)) import Data.List.Lazy as LL -import Data.Maybe (Maybe(..), maybe, isJust, fromMaybe) +import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) import Data.Ord (class Ord1) -import Data.Traversable (traverse, class Traversable) +import Data.Traversable (class Traversable, traverse) import Data.TraversableWithIndex (class TraversableWithIndex, traverseWithIndex) import Data.Tuple (Tuple(Tuple), snd, uncurry) import Data.Unfoldable (class Unfoldable, unfoldr) @@ -97,14 +97,16 @@ instance semigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Semigroup (Map k v) where + ) => + Semigroup (Map k v) where append l r = unionWith append l r instance monoidSemigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Monoid (Map k v) where + ) => + Monoid (Map k v) where mempty = empty instance altMap :: Ord k => Alt (Map k) where @@ -161,51 +163,62 @@ instance traversableMap :: Traversable (Map k) where traverse _ Leaf = pure Leaf traverse f (Two left k v right) = Two <$> traverse f left - <*> pure k - <*> f v - <*> traverse f right + <*> pure k + <*> f v + <*> traverse f right traverse f (Three left k1 v1 mid k2 v2 right) = Three <$> traverse f left - <*> pure k1 - <*> f v1 - <*> traverse f mid - <*> pure k2 - <*> f v2 - <*> traverse f right + <*> pure k1 + <*> f v1 + <*> traverse f mid + <*> pure k2 + <*> f v2 + <*> traverse f right sequence = traverse identity instance traversableWithIndexMap :: TraversableWithIndex k (Map k) where traverseWithIndex _ Leaf = pure Leaf traverseWithIndex f (Two left k v right) = Two <$> traverseWithIndex f left - <*> pure k - <*> f k v - <*> traverseWithIndex f right + <*> pure k + <*> f k v + <*> traverseWithIndex f right traverseWithIndex f (Three left k1 v1 mid k2 v2 right) = Three <$> traverseWithIndex f left - <*> pure k1 - <*> f k1 v1 - <*> traverseWithIndex f mid - <*> pure k2 - <*> f k2 v2 - <*> traverseWithIndex f right + <*> pure k1 + <*> f k1 v1 + <*> traverseWithIndex f mid + <*> pure k2 + <*> f k2 v2 + <*> traverseWithIndex f right -- | Render a `Map` as a `String` showTree :: forall k v. Show k => Show v => Map k v -> String showTree Leaf = "Leaf" showTree (Two left k v right) = - "Two (" <> showTree left <> - ") (" <> show k <> - ") (" <> show v <> - ") (" <> showTree right <> ")" + "Two (" <> showTree left + <> ") (" + <> show k + <> ") (" + <> show v + <> ") (" + <> showTree right + <> ")" showTree (Three left k1 v1 mid k2 v2 right) = - "Three (" <> showTree left <> - ") (" <> show k1 <> - ") (" <> show v1 <> - ") (" <> showTree mid <> - ") (" <> show k2 <> - ") (" <> show v2 <> - ") (" <> showTree right <> ")" + "Three (" <> showTree left + <> ") (" + <> show k1 + <> ") (" + <> show v1 + <> ") (" + <> showTree mid + <> ") (" + <> show k2 + <> ") (" + <> show v2 + <> ") (" + <> showTree right + <> ")" -- | An empty map empty :: forall k v. Map k v @@ -235,109 +248,108 @@ checkValid tree = length (nub (allHeights tree)) == one lookup :: forall k v. Ord k => k -> Map k v -> Maybe v lookup k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v right) = - case comp k k1 of - EQ -> Just v - LT -> go left - _ -> go right - go (Three left k1 v1 mid k2 v2 right) = - case comp k k1 of - EQ -> Just v1 - c1 -> - case c1, comp k k2 of - _ , EQ -> Just v2 - LT, _ -> go left - _ , GT -> go right - _ , _ -> go mid + comp :: k -> k -> Ordering + comp = compare + go Leaf = Nothing + go (Two left k1 v right) = + case comp k k1 of + EQ -> Just v + LT -> go left + _ -> go right + go (Three left k1 v1 mid k2 v2 right) = + case comp k k1 of + EQ -> Just v1 + c1 -> + case c1, comp k k2 of + _, EQ -> Just v2 + LT, _ -> go left + _, GT -> go right + _, _ -> go mid -- | Look up a value for the specified key, or the greatest one less than it lookupLE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupLE k = go where - comp :: k -> k -> Ordering - comp = compare + comp :: k -> k -> Ordering + comp = compare - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> Just { key: k2, value: v2 } - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right + LT -> go left + go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of + EQ -> Just { key: k2, value: v2 } + GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right + LT -> go $ Two left k1 v1 mid -- | Look up a value for the greatest key less than the specified key lookupLT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupLT k = go where - comp :: k -> k -> Ordering - comp = compare + comp :: k -> k -> Ordering + comp = compare - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMax left - GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right - LT -> go left - go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of - EQ -> findMax $ Two left k1 v1 mid - GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right - LT -> go $ Two left k1 v1 mid + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> findMax left + GT -> Just $ fromMaybe { key: k1, value: v1 } $ go right + LT -> go left + go (Three left k1 v1 mid k2 v2 right) = case comp k k2 of + EQ -> findMax $ Two left k1 v1 mid + GT -> Just $ fromMaybe { key: k2, value: v2 } $ go right + LT -> go $ Two left k1 v1 mid -- | Look up a value for the specified key, or the least one greater than it lookupGE :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupGE k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> Just { key: k1, value: v1 } - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right + comp :: k -> k -> Ordering + comp = compare + + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go right + go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of + EQ -> Just { key: k1, value: v1 } + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go $ Two mid k2 v2 right -- | Look up a value for the least key greater than the specified key lookupGT :: forall k v. Ord k => k -> Map k v -> Maybe { key :: k, value :: v } lookupGT k = go where - comp :: k -> k -> Ordering - comp = compare - - go Leaf = Nothing - go (Two left k1 v1 right) = case comp k k1 of - EQ -> findMin right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go right - go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of - EQ -> findMin $ Two mid k2 v2 right - LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left - GT -> go $ Two mid k2 v2 right + comp :: k -> k -> Ordering + comp = compare + + go Leaf = Nothing + go (Two left k1 v1 right) = case comp k k1 of + EQ -> findMin right + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go right + go (Three left k1 v1 mid k2 v2 right) = case comp k k1 of + EQ -> findMin $ Two mid k2 v2 right + LT -> Just $ fromMaybe { key: k1, value: v1 } $ go left + GT -> go $ Two mid k2 v2 right -- | Returns the pair with the greatest key findMax :: forall k v. Map k v -> Maybe { key :: k, value :: v } findMax = go Nothing where - go acc Leaf = acc - go _ (Two _ k1 v1 right) = go (Just { key: k1, value: v1 }) right - go _ (Three _ _ _ _ k2 v2 right) = go (Just { key: k2, value: v2 }) right + go acc Leaf = acc + go _ (Two _ k1 v1 right) = go (Just { key: k1, value: v1 }) right + go _ (Three _ _ _ _ k2 v2 right) = go (Just { key: k2, value: v2 }) right -- | Returns the pair with the least key findMin :: forall k v. Map k v -> Maybe { key :: k, value :: v } findMin = go Nothing where - go acc Leaf = acc - go _ (Two left k1 v1 _) = go (Just { key: k1, value: v1 }) left - go _ (Three left k1 v1 _ _ _ _) = go (Just { key: k1, value: v1 }) left + go acc Leaf = acc + go _ (Two left k1 v1 _) = go (Just { key: k1, value: v1 }) left + go _ (Three left k1 v1 _ _ _ _) = go (Just { key: k1, value: v1 }) left -- | Fold over the entries of a given map where the key is between a lower and -- | an upper bound. Passing `Nothing` as either the lower or upper bound @@ -404,15 +416,15 @@ foldSubmapBy appendFn memptyValue kmin kmax f = Leaf -> memptyValue Two left k v right -> - (if tooSmall k then memptyValue else go left) - `appendFn` (if inBounds k then f k v else memptyValue) - `appendFn` (if tooLarge k then memptyValue else go right) + (if tooSmall k then memptyValue else go left) + `appendFn` (if inBounds k then f k v else memptyValue) + `appendFn` (if tooLarge k then memptyValue else go right) Three left k1 v1 mid k2 v2 right -> - (if tooSmall k1 then memptyValue else go left) - `appendFn` (if inBounds k1 then f k1 v1 else memptyValue) - `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) - `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) - `appendFn` (if tooLarge k2 then memptyValue else go right) + (if tooSmall k1 then memptyValue else go left) + `appendFn` (if inBounds k1 then f k1 v1 else memptyValue) + `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) + `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) + `appendFn` (if tooLarge k2 then memptyValue else go right) in go @@ -483,16 +495,16 @@ insert k v = down Nil case comp k k1 of EQ -> fromZipper ctx (Two left k v right) LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ -> down (Cons (TwoRight left k1 v1) ctx) right + _ -> down (Cons (TwoRight left k1 v1) ctx) right down ctx (Three left k1 v1 mid k2 v2 right) = case comp k k1 of EQ -> fromZipper ctx (Three left k v mid k2 v2 right) c1 -> case c1, comp k k2 of - _ , EQ -> fromZipper ctx (Three left k1 v1 mid k v right) - LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, EQ -> fromZipper ctx (Three left k1 v1 mid k v right) + LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> KickUp k v -> Map k v up Nil (KickUp left k' v' right) = Two left k' v' right @@ -529,25 +541,36 @@ pop k = down Nil Two left k1 v1 right -> case right, comp k k1 of Leaf, EQ -> Just (Tuple v1 (up ctx Leaf)) - _ , EQ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) - _ , LT -> down (Cons (TwoLeft k1 v1 right) ctx) left - _ , _ -> down (Cons (TwoRight left k1 v1) ctx) right + _, EQ -> + let + max = maxNode left + in + Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) + _, LT -> down (Cons (TwoLeft k1 v1 right) ctx) left + _, _ -> down (Cons (TwoRight left k1 v1) ctx) right Three left k1 v1 mid k2 v2 right -> - let leaves = - case left, mid, right of - Leaf, Leaf, Leaf -> true - _ , _ , _ -> false - in case leaves, comp k k1, comp k k2 of - true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) - true, _ , EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) - _ , EQ, _ -> let max = maxNode left - in Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) - _ , _ , EQ -> let max = maxNode mid - in Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) - _ , LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - _ , GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _ , _ , _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + let + leaves = + case left, mid, right of + Leaf, Leaf, Leaf -> true + _, _, _ -> false + in + case leaves, comp k k1, comp k k2 of + true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) + true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) + _, EQ, _ -> + let + max = maxNode left + in + Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) + _, _, EQ -> + let + max = maxNode mid + in + Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) + _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid + _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> Map k v -> Map k v up ctxs tree = @@ -582,7 +605,6 @@ pop k = down Nil Three _ _ _ _ _ _ right -> maxNode right _ -> unsafeCrashWith "The impossible happened in partial function `maxNode`." - removeMaxNode :: List (TreeContext k v) -> Map k v -> Map k v removeMaxNode ctx m = case m of @@ -592,7 +614,6 @@ pop k = down Nil Three left k1 v1 mid k2 v2 right -> removeMaxNode (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right _ -> unsafeCrashWith "The impossible happened in partial function `removeMaxNode`." - -- | Insert the value, delete a value, or update a value for a key in a map alter :: forall k v. Ord k => (Maybe v -> Maybe v) -> k -> Map k v -> Map k v alter f k m = case f (k `lookup` m) of @@ -611,7 +632,8 @@ fromFoldable = foldl (\m (Tuple k v) -> insert k v m) empty -- | Convert any foldable collection of key/value pairs to a map. -- | On key collision, the values are configurably combined. fromFoldableWith :: forall f k v. Ord k => Foldable f => (v -> v -> v) -> f (Tuple k v) -> Map k v -fromFoldableWith f = foldl (\m (Tuple k v) -> alter (combine v) k m) empty where +fromFoldableWith f = foldl (\m (Tuple k v) -> alter (combine v) k m) empty + where combine v (Just v') = Just $ f v v' combine v Nothing = Just v @@ -621,7 +643,8 @@ fromFoldableWithIndex = foldlWithIndex (\k m v -> insert k v m) empty -- | Convert a map to an unfoldable structure of key/value pairs where the keys are in ascending order toUnfoldable :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toUnfoldable m = unfoldr go (m : Nil) where +toUnfoldable m = unfoldr go (m : Nil) + where go Nil = Nothing go (hd : tl) = case hd of Leaf -> go tl @@ -642,7 +665,8 @@ toUnfoldable m = unfoldr go (m : Nil) where -- | -- | If you are unsure, use `toUnfoldable` toUnfoldableUnordered :: forall f k v. Unfoldable f => Map k v -> f (Tuple k v) -toUnfoldableUnordered m = unfoldr go (m : Nil) where +toUnfoldableUnordered m = unfoldr go (m : Nil) + where go Nil = Nothing go (hd : tl) = case hd of Leaf -> go tl @@ -684,9 +708,9 @@ intersectionWith f m1 m2 = go (toUnfoldable m1 :: List (Tuple k a)) (toUnfoldabl go _ Nil m = m go as@(Cons (Tuple k1 a) ass) bs@(Cons (Tuple k2 b) bss) m = case compare k1 k2 of - LT -> go ass bs m - EQ -> go ass bss (insert k1 (f a b) m) - GT -> go as bss m + LT -> go ass bs m + EQ -> go ass bss (insert k1 (f a b) m) + GT -> go as bss m -- | Compute the intersection of two maps, preferring values from the first map in the case -- | of duplicate keys. @@ -701,7 +725,8 @@ difference m1 m2 = foldlWithIndex (\k m _ -> delete k m) m1 m2 -- | Test whether one map contains all of the keys and values contained in another map isSubmap :: forall k v. Ord k => Eq v => Map k v -> Map k v -> Boolean isSubmap m1 m2 = LL.all f $ (toUnfoldable m1 :: LL.List (Tuple k v)) - where f (Tuple k v) = lookup k m2 == Just v + where + f (Tuple k v) = lookup k m2 == Just v -- | Calculate the number of key/value pairs in a map size :: forall k v. Map k v -> Int @@ -728,7 +753,7 @@ filter predicate = filterWithKey $ const predicate -- | Applies a function to each key/value pair in a map, discarding entries -- | where the function returns `Nothing`. mapMaybeWithKey :: forall k a b. Ord k => (k -> a -> Maybe b) -> Map k a -> Map k b -mapMaybeWithKey f = foldrWithIndex (\k a acc → maybe acc (\b -> insert k b acc) (f k a)) empty +mapMaybeWithKey f = foldrWithIndex (\k a acc -> maybe acc (\b -> insert k b acc) (f k a)) empty -- | Applies a function to each value in a map, discarding entries where the -- | function returns `Nothing`. diff --git a/src/Data/Map.purs b/src/Data/Map.purs index 4aac96f..6af4d68 100644 --- a/src/Data/Map.purs +++ b/src/Data/Map.purs @@ -12,12 +12,12 @@ import Data.Eq (class Eq1) import Data.Foldable (class Foldable) import Data.FoldableWithIndex (class FoldableWithIndex) import Data.FunctorWithIndex (class FunctorWithIndex) -import Data.Map.Internal (Map, alter, catMaybes, checkValid, delete, empty, filter, filterKeys, filterWithKey, findMax, findMin, foldSubmap, fromFoldable, fromFoldableWith, fromFoldableWithIndex, insert, insertWith, isEmpty, isSubmap, lookup, lookupGE, lookupGT, lookupLE, lookupLT, member, pop, showTree, singleton, size, submap, toUnfoldable, toUnfoldableUnordered, union, unionWith, unions, intersection, intersectionWith, difference, update, values, mapMaybeWithKey, mapMaybe, any, anyWithKey) +import Data.Map.Internal (Map, alter, any, anyWithKey, catMaybes, checkValid, delete, difference, empty, filter, filterKeys, filterWithKey, findMax, findMin, foldSubmap, fromFoldable, fromFoldableWith, fromFoldableWithIndex, insert, insertWith, intersection, intersectionWith, isEmpty, isSubmap, lookup, lookupGE, lookupGT, lookupLE, lookupLT, mapMaybe, mapMaybeWithKey, member, pop, showTree, singleton, size, submap, toUnfoldable, toUnfoldableUnordered, union, unionWith, unions, update, values) import Data.Newtype (class Newtype) import Data.Ord (class Ord1) +import Data.Set (Set, fromMap) import Data.Traversable (class Traversable) import Data.TraversableWithIndex (class TraversableWithIndex) -import Data.Set (Set, fromMap) -- | The set of keys of the given map. -- | See also `Data.Set.fromMap`. diff --git a/src/Data/Map/Gen.purs b/src/Data/Map/Gen.purs index 6398a2d..1603cdf 100644 --- a/src/Data/Map/Gen.purs +++ b/src/Data/Map/Gen.purs @@ -4,14 +4,14 @@ import Prelude import Control.Monad.Gen (class MonadGen, chooseInt, resize, sized, unfoldable) import Control.Monad.Rec.Class (class MonadRec) +import Data.List (List) import Data.Map (Map, fromFoldable) import Data.Tuple (Tuple(..)) -import Data.List (List) -- | Generates a `Map` using the specified key and value generators. genMap :: forall m a b - . MonadRec m + . MonadRec m => MonadGen m => Ord a => m a diff --git a/src/Data/Map/Internal.purs b/src/Data/Map/Internal.purs index 2b50f1a..c5da604 100644 --- a/src/Data/Map/Internal.purs +++ b/src/Data/Map/Internal.purs @@ -76,7 +76,7 @@ import Data.FunctorWithIndex (class FunctorWithIndex) import Data.List (List(..), (:)) import Data.Maybe (Maybe(..)) import Data.Ord (class Ord1, abs) -import Data.Traversable (traverse, class Traversable) +import Data.Traversable (class Traversable, traverse) import Data.TraversableWithIndex (class TraversableWithIndex) import Data.Tuple (Tuple(Tuple)) import Data.Unfoldable (class Unfoldable, unfoldr) @@ -125,14 +125,16 @@ instance semigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Semigroup (Map k v) where + ) => + Semigroup (Map k v) where append = unionWith append instance monoidSemigroupMap :: ( Warn (Text "Data.Map's `Semigroup` instance is now unbiased and differs from the left-biased instance defined in PureScript releases <= 0.13.x.") , Ord k , Semigroup v - ) => Monoid (Map k v) where + ) => + Monoid (Map k v) where mempty = empty instance altMap :: Ord k => Alt (Map k) where @@ -233,7 +235,7 @@ showTree = go "" go ind = case _ of Leaf -> ind <> "Leaf" Node h _ k v l r -> - (ind <> "[" <> show h <> "] " <> show k <> " => " <> show v <> "\n") + (ind <> "[" <> show h <> "] " <> show k <> " => " <> show v <> "\n") <> (go (ind <> " ") l <> "\n") <> (go (ind <> " ") r) @@ -351,7 +353,7 @@ lookupGT k = go findMax :: forall k v. Map k v -> Maybe { key :: k, value :: v } findMax = case _ of Leaf -> Nothing - Node _ _ k v _ r -> + Node _ _ k v _ r -> case r of Leaf -> Just { key: k, value: v } _ -> findMax r @@ -413,9 +415,9 @@ foldSubmapBy appendFn memptyValue kmin kmax f = Leaf -> memptyValue Node _ _ k v left right -> - (if tooSmall k then memptyValue else go left) - `appendFn` (if inBounds k then f k v else memptyValue) - `appendFn` (if tooLarge k then memptyValue else go right) + (if tooSmall k then memptyValue else go left) + `appendFn` (if inBounds k then f k v else memptyValue) + `appendFn` (if tooLarge k then memptyValue else go right) in go @@ -681,7 +683,7 @@ any :: forall k v. (v -> Boolean) -> Map k v -> Boolean any predicate = go where go = case _ of - Leaf -> false + Leaf -> false Node _ _ _ mv ml mr -> predicate mv || go ml || go mr -- | Returns true if at least one map element satisfies the given predicate, @@ -691,7 +693,7 @@ anyWithKey :: forall k v. (k -> v -> Boolean) -> Map k v -> Boolean anyWithKey predicate = go where go = case _ of - Leaf -> false + Leaf -> false Node _ _ mk mv ml mr -> predicate mk mv || go ml || go mr -- | Low-level Node constructor which maintains the height and size invariants @@ -881,7 +883,7 @@ instance (Ord k, Ord v) => Ord (MapIter k v) where other other -> other - IterDone, b'-> + IterDone, b' -> case b' of IterDone -> EQ diff --git a/src/Data/Set/NonEmpty.purs b/src/Data/Set/NonEmpty.purs index a4c2dee..036d36d 100644 --- a/src/Data/Set/NonEmpty.purs +++ b/src/Data/Set/NonEmpty.purs @@ -34,7 +34,7 @@ import Data.List.NonEmpty (NonEmptyList) import Data.Map.Internal as Internal import Data.Maybe (Maybe(..), fromJust) import Data.Ord (class Ord1) -import Data.Semigroup.Foldable (class Foldable1, foldMap1, foldr1, foldl1) +import Data.Semigroup.Foldable (class Foldable1, foldMap1, foldl1, foldr1) import Data.Set (Set) import Data.Set as Set import Data.Tuple (Tuple(..)) diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index c7d86ef..df4aa9c 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -5,11 +5,11 @@ import Prelude import Control.Alt ((<|>)) import Data.Array as A import Data.Array.NonEmpty (cons') -import Data.Foldable (foldl, for_, all, and) +import Data.Foldable (all, and, foldl, for_) import Data.FoldableWithIndex (foldrWithIndex) import Data.Function (on) import Data.FunctorWithIndex (mapWithIndex) -import Data.List (List(..), groupBy, length, nubBy, singleton, sort, sortBy, (:), head, last) +import Data.List (List(..), groupBy, head, last, length, nubBy, singleton, sort, sortBy, (:)) import Data.List.NonEmpty as NEL import Data.Map as M import Data.Map.Gen (genMap) @@ -19,9 +19,9 @@ import Data.Semigroup.Last (Last(..)) import Data.Tuple (Tuple(..), fst, snd, uncurry) import Effect (Effect) import Effect.Console (log) -import Effect.Exception (throwException, error) +import Effect.Exception (error, throwException) import Partial.Unsafe (unsafePartial) -import Test.QuickCheck ((), (<=?), (===), quickCheck, quickCheck') +import Test.QuickCheck (quickCheck, quickCheck', (<=?), (), (===)) import Test.QuickCheck.Arbitrary (class Arbitrary, arbitrary) import Test.QuickCheck.Gen (elements, oneOf) @@ -31,6 +31,7 @@ instance arbTestMap :: (Eq k, Ord k, Arbitrary k, Arbitrary v) => Arbitrary (Tes arbitrary = TestMap <$> genMap arbitrary arbitrary data SmallKey = A | B | C | D | E | F | G | H | I | J + derive instance eqSmallKey :: Eq SmallKey derive instance ordSmallKey :: Ord SmallKey @@ -47,7 +48,7 @@ instance showSmallKey :: Show SmallKey where show J = "J" instance arbSmallKey :: Arbitrary SmallKey where - arbitrary = elements $ cons' A [B, C, D, E, F, G, H, I, J] + arbitrary = elements $ cons' A [ B, C, D, E, F, G, H, I, J ] data Instruction k v = Insert k v | Delete k @@ -56,7 +57,7 @@ instance showInstruction :: (Show k, Show v) => Show (Instruction k v) where show (Delete k) = "Delete (" <> show k <> ")" instance arbInstruction :: (Arbitrary k, Arbitrary v) => Arbitrary (Instruction k v) where - arbitrary = oneOf $ cons' (Insert <$> arbitrary <*> arbitrary) [Delete <$> arbitrary] + arbitrary = oneOf $ cons' (Insert <$> arbitrary <*> arbitrary) [ Delete <$> arbitrary ] runInstructions :: forall k v. Ord k => List (Instruction k v) -> M.Map k v -> M.Map k v runInstructions instrs t0 = foldl step t0 instrs @@ -123,7 +124,8 @@ mapTests = do let tree :: M.Map SmallKey Int tree = runInstructions instrs M.empty - in M.checkValid tree ("Map not balanced:\n " <> show tree <> "\nGenerated by:\n " <> show instrs) + in + M.checkValid tree ("Map not balanced:\n " <> show tree <> "\nGenerated by:\n " <> show instrs) log "Lookup from empty" quickCheck $ \k -> M.lookup k (M.empty :: M.Map SmallKey Int) == Nothing @@ -136,68 +138,84 @@ mapTests = do let tree :: M.Map SmallKey Int tree = M.insert k v (runInstructions instrs M.empty) - in M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) + in + M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) log "Singleton to list" quickCheck $ \k v -> M.toUnfoldable (M.singleton k v :: M.Map SmallKey Int) == singleton (Tuple k v) log "fromFoldable [] = empty" - quickCheck (M.fromFoldable [] == (M.empty :: M.Map Unit Unit) - "was not empty") + quickCheck + ( M.fromFoldable [] == (M.empty :: M.Map Unit Unit) + "was not empty" + ) log "fromFoldable & key collision" do - let nums = M.fromFoldable [Tuple 0 "zero", Tuple 1 "what", Tuple 1 "one"] + let nums = M.fromFoldable [ Tuple 0 "zero", Tuple 1 "what", Tuple 1 "one" ] quickCheck (M.lookup 0 nums == Just "zero" "invalid lookup - 0") - quickCheck (M.lookup 1 nums == Just "one" "invalid lookup - 1") - quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") + quickCheck (M.lookup 1 nums == Just "one" "invalid lookup - 1") + quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") log "fromFoldableWith const [] = empty" - quickCheck (M.fromFoldableWith const [] == (M.empty :: M.Map Unit Unit) - "was not empty") + quickCheck + ( M.fromFoldableWith const [] == (M.empty :: M.Map Unit Unit) + "was not empty" + ) log "fromFoldableWith (+) & key collision" do - let nums = M.fromFoldableWith (+) [Tuple 0 1, Tuple 1 1, Tuple 1 1] - quickCheck (M.lookup 0 nums == Just 1 "invalid lookup - 0") - quickCheck (M.lookup 1 nums == Just 2 "invalid lookup - 1") + let nums = M.fromFoldableWith (+) [ Tuple 0 1, Tuple 1 1, Tuple 1 1 ] + quickCheck (M.lookup 0 nums == Just 1 "invalid lookup - 0") + quickCheck (M.lookup 1 nums == Just 2 "invalid lookup - 1") quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") log "sort . toUnfoldable . fromFoldable = sort (on lists without key-duplicates)" quickCheck $ \(list :: List (Tuple SmallKey Int)) -> - let nubbedList = nubBy (compare `on` fst) list - f x = M.toUnfoldable (M.fromFoldable x) - in sort (f nubbedList) == sort nubbedList show nubbedList + let + nubbedList = nubBy (compare `on` fst) list + f x = M.toUnfoldable (M.fromFoldable x) + in + sort (f nubbedList) == sort nubbedList show nubbedList log "fromFoldable . toUnfoldable = id" quickCheck $ \(TestMap (m :: M.Map SmallKey Int)) -> - let f m' = M.fromFoldable (M.toUnfoldable m' :: List (Tuple SmallKey Int)) - in f m == m show m + let + f m' = M.fromFoldable (M.toUnfoldable m' :: List (Tuple SmallKey Int)) + in + f m == m show m log "fromFoldableWith const = fromFoldable" quickCheck $ \arr -> M.fromFoldableWith const arr == - M.fromFoldable (arr :: List (Tuple SmallKey Int)) show arr + M.fromFoldable (arr :: List (Tuple SmallKey Int)) show arr log "fromFoldableWith (<>) = fromFoldable . collapse with (<>) . group on fst" quickCheck $ \arr -> - let combine (Tuple s a) (Tuple _ b) = (Tuple s $ b <> a) - foldl1 g = unsafePartial \(Cons x xs) -> foldl g x xs - f = M.fromFoldable <<< map (foldl1 combine <<< NEL.toList) <<< - groupBy ((==) `on` fst) <<< sortBy (compare `on` fst) in - M.fromFoldableWith (<>) arr === f (arr :: List (Tuple String String)) + let + combine (Tuple s a) (Tuple _ b) = (Tuple s $ b <> a) + foldl1 g = unsafePartial \(Cons x xs) -> foldl g x xs + f = M.fromFoldable <<< map (foldl1 combine <<< NEL.toList) + <<< groupBy ((==) `on` fst) + <<< sortBy (compare `on` fst) + in + M.fromFoldableWith (<>) arr === f (arr :: List (Tuple String String)) log "toUnfoldable is sorted" quickCheck $ \(TestMap m) -> - let list = M.toUnfoldable (m :: M.Map SmallKey Int) - ascList = M.toUnfoldable m - in ascList === sortBy (compare `on` fst) list + let + list = M.toUnfoldable (m :: M.Map SmallKey Int) + ascList = M.toUnfoldable m + in + ascList === sortBy (compare `on` fst) list log "Lookup from union" quickCheck $ \(TestMap m1) (TestMap m2) k -> - M.lookup (smallKey k) (M.union m1 m2) == (case M.lookup k m1 of - Nothing -> M.lookup k m2 - Just v -> Just (number v)) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", union: " <> show (M.union m1 m2)) + M.lookup (smallKey k) (M.union m1 m2) == + ( case M.lookup k m1 of + Nothing -> M.lookup k m2 + Just v -> Just (number v) + ) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", union: " <> show (M.union m1 m2)) log "Union is idempotent" quickCheck $ \(TestMap m1) (TestMap m2) -> (m1 `M.union` m2) == ((m1 `M.union` m2) `M.union` (m2 :: M.Map SmallKey Int)) @@ -206,103 +224,124 @@ mapTests = do quickCheck $ \(TestMap m1) (TestMap m2) k -> M.lookup k (M.union m1 (m2 :: M.Map SmallKey Int)) == (M.lookup k m1 <|> M.lookup k m2) log "unionWith" - for_ [Tuple (+) 0, Tuple (*) 1] $ \(Tuple op ident) -> + for_ [ Tuple (+) 0, Tuple (*) 1 ] $ \(Tuple op ident) -> quickCheck $ \(TestMap m1) (TestMap m2) k -> - let u = M.unionWith op m1 m2 :: M.Map SmallKey Int - in case M.lookup k u of - Nothing -> not (M.member k m1 || M.member k m2) - Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) + let + u = M.unionWith op m1 m2 :: M.Map SmallKey Int + in + case M.lookup k u of + Nothing -> not (M.member k m1 || M.member k m2) + Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) log "unionWith argument order" quickCheck $ \(TestMap m1) (TestMap m2) k -> - let u = M.unionWith (-) m1 m2 :: M.Map SmallKey Int - in1 = M.member k m1 - v1 = M.lookup k m1 - in2 = M.member k m2 - v2 = M.lookup k m2 - in case M.lookup k u of - Just v | in1 && in2 -> Just v == ((-) <$> v1 <*> v2) - Just v | in1 -> Just v == v1 - Just v -> Just v == v2 - Nothing -> not (in1 || in2) + let + u = M.unionWith (-) m1 m2 :: M.Map SmallKey Int + in1 = M.member k m1 + v1 = M.lookup k m1 + in2 = M.member k m2 + v2 = M.lookup k m2 + in + case M.lookup k u of + Just v | in1 && in2 -> Just v == ((-) <$> v1 <*> v2) + Just v | in1 -> Just v == v1 + Just v -> Just v == v2 + Nothing -> not (in1 || in2) log "Lookup from intersection" quickCheck $ \(TestMap m1) (TestMap m2) k -> - M.lookup (smallKey k) (M.intersection (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey Int)) == (case M.lookup k m2 of - Nothing -> Nothing - Just _ -> M.lookup k m1) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", intersection: " <> show (M.intersection m1 m2)) + M.lookup (smallKey k) (M.intersection (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey Int)) == + ( case M.lookup k m2 of + Nothing -> Nothing + Just _ -> M.lookup k m1 + ) ("m1: " <> show m1 <> ", m2: " <> show m2 <> ", k: " <> show k <> ", v1: " <> show (M.lookup k m1) <> ", v2: " <> show (M.lookup k m2) <> ", intersection: " <> show (M.intersection m1 m2)) log "Intersection is idempotent" quickCheck $ \(TestMap m1) (TestMap m2) -> ((m1 :: M.Map SmallKey Int) `M.intersection` m2) == ((m1 `M.intersection` m2) `M.intersection` (m2 :: M.Map SmallKey Int)) log "intersectionWith" - for_ [(+), (*)] $ \op -> + for_ [ (+), (*) ] $ \op -> quickCheck $ \(TestMap m1) (TestMap m2) k -> - let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int - in case M.lookup k u of - Nothing -> not (M.member k m1 && M.member k m2) - Just v -> Just v == (op <$> M.lookup k m1 <*> M.lookup k m2) + let + u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int + in + case M.lookup k u of + Nothing -> not (M.member k m1 && M.member k m2) + Just v -> Just v == (op <$> M.lookup k m1 <*> M.lookup k m2) log "map-apply is equivalent to intersectionWith" - for_ [(+), (*)] $ \op -> + for_ [ (+), (*) ] $ \op -> quickCheck $ \(TestMap m1) (TestMap m2) -> - let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int - in u == (op <$> m1 <*> m2) + let + u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int + in + u == (op <$> m1 <*> m2) log "difference" quickCheck $ \(TestMap m1) (TestMap m2) -> - let d = M.difference (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey String) + let + d = M.difference (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey String) in if M.size d == 0 then and (map (\k -> M.member k m2) (A.fromFoldable $ M.keys m1)) else and (map (\k -> M.member k m1) (A.fromFoldable $ M.keys d)) && - and (map (\k -> not $ M.member k d) (A.fromFoldable $ M.keys m2)) + and (map (\k -> not $ M.member k d) (A.fromFoldable $ M.keys m2)) log "size" quickCheck $ \xs -> - let xs' = nubBy (compare `on` fst) xs - in M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int)) + let + xs' = nubBy (compare `on` fst) xs + in + M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int)) log "lookupLE result is correct" quickCheck $ \k (TestMap m) -> case M.lookupLE k (smallKeyToNumberMap m) of Nothing -> all (_ > k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k1 < k2 && k2 < k - isLTwhenEQexists = k1 < k && M.member k m - in k1 <= k - && all (not <<< isCloserKey) (M.keys m) - && not isLTwhenEQexists - && M.lookup k1 m == Just v + Just { key: k1, value: v } -> + let + isCloserKey k2 = k1 < k2 && k2 < k + isLTwhenEQexists = k1 < k && M.member k m + in + k1 <= k + && all (not <<< isCloserKey) (M.keys m) + && not isLTwhenEQexists + && M.lookup k1 m == Just v log "lookupGE result is correct" quickCheck $ \k (TestMap m) -> case M.lookupGE k (smallKeyToNumberMap m) of Nothing -> all (_ < k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k < k2 && k2 < k1 - isGTwhenEQexists = k < k1 && M.member k m - in k1 >= k - && all (not <<< isCloserKey) (M.keys m) - && not isGTwhenEQexists - && M.lookup k1 m == Just v + Just { key: k1, value: v } -> + let + isCloserKey k2 = k < k2 && k2 < k1 + isGTwhenEQexists = k < k1 && M.member k m + in + k1 >= k + && all (not <<< isCloserKey) (M.keys m) + && not isGTwhenEQexists + && M.lookup k1 m == Just v log "lookupLT result is correct" quickCheck $ \k (TestMap m) -> case M.lookupLT k (smallKeyToNumberMap m) of Nothing -> all (_ >= k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k1 < k2 && k2 < k - in k1 < k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v + Just { key: k1, value: v } -> + let + isCloserKey k2 = k1 < k2 && k2 < k + in + k1 < k + && all (not <<< isCloserKey) (M.keys m) + && M.lookup k1 m == Just v log "lookupGT result is correct" quickCheck $ \k (TestMap m) -> case M.lookupGT k (smallKeyToNumberMap m) of Nothing -> all (_ <= k) $ M.keys m - Just { key: k1, value: v } -> let - isCloserKey k2 = k < k2 && k2 < k1 - in k1 > k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v + Just { key: k1, value: v } -> + let + isCloserKey k2 = k < k2 && k2 < k1 + in + k1 > k + && all (not <<< isCloserKey) (M.keys m) + && M.lookup k1 m == Just v log "findMin result is correct" quickCheck $ \(TestMap m) -> case M.findMin (smallKeyToNumberMap m) of @@ -315,36 +354,38 @@ mapTests = do Just { key: k, value: v } -> M.lookup k m == Just v && all (_ <= k) (M.keys m) log "mapWithKey is correct" - quickCheck $ \(TestMap m :: TestMap String Int) -> let - f k v = k <> show v - resultViaMapWithKey = m # mapWithIndex f - toList = M.toUnfoldable :: forall k v. M.Map k v -> List (Tuple k v) - resultViaLists = m # toList # map (\(Tuple k v) → Tuple k (f k v)) # M.fromFoldable - in resultViaMapWithKey === resultViaLists + quickCheck $ \(TestMap m :: TestMap String Int) -> + let + f k v = k <> show v + resultViaMapWithKey = m # mapWithIndex f + toList = M.toUnfoldable :: forall k v. M.Map k v -> List (Tuple k v) + resultViaLists = m # toList # map (\(Tuple k v) -> Tuple k (f k v)) # M.fromFoldable + in + resultViaMapWithKey === resultViaLists log "filterWithKey gives submap" quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filterWithKey p s) s + M.isSubmap (M.filterWithKey p s) s log "filterWithKey keeps those keys for which predicate is true" quickCheck $ \(TestMap s :: TestMap String Int) p -> - all (uncurry p) (M.toUnfoldable (M.filterWithKey p s) :: Array (Tuple String Int)) + all (uncurry p) (M.toUnfoldable (M.filterWithKey p s) :: Array (Tuple String Int)) log "filterKeys gives submap" quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filterKeys p s) s + M.isSubmap (M.filterKeys p s) s log "filterKeys keeps those keys for which predicate is true" quickCheck $ \(TestMap s :: TestMap String Int) p -> - all p (M.keys (M.filterKeys p s)) + all p (M.keys (M.filterKeys p s)) log "filter gives submap" quickCheck $ \(TestMap s :: TestMap String Int) p -> - M.isSubmap (M.filter p s) s + M.isSubmap (M.filter p s) s log "filter keeps those values for which predicate is true" quickCheck $ \(TestMap s :: TestMap String Int) p -> - all p (M.values (M.filter p s)) + all p (M.values (M.filter p s)) log "submap with no bounds = id" quickCheck \(TestMap m :: TestMap SmallKey Int) -> @@ -352,39 +393,46 @@ mapTests = do log "submap with lower bound" quickCheck' 1 $ - M.submap (Just B) Nothing (M.fromFoldable [Tuple A 0, Tuple B 0]) - == M.fromFoldable [Tuple B 0] + M.submap (Just B) Nothing (M.fromFoldable [ Tuple A 0, Tuple B 0 ]) + == M.fromFoldable [ Tuple B 0 ] log "submap with upper bound" quickCheck' 1 $ - M.submap Nothing (Just A) (M.fromFoldable [Tuple A 0, Tuple B 0]) - == M.fromFoldable [Tuple A 0] + M.submap Nothing (Just A) (M.fromFoldable [ Tuple A 0, Tuple B 0 ]) + == M.fromFoldable [ Tuple A 0 ] log "submap with lower & upper bound" quickCheck' 1 $ - M.submap (Just B) (Just B) (M.fromFoldable [Tuple A 0, Tuple B 0, Tuple C 0]) - == M.fromFoldable [Tuple B 0] + M.submap (Just B) (Just B) (M.fromFoldable [ Tuple A 0, Tuple B 0, Tuple C 0 ]) + == M.fromFoldable [ Tuple B 0 ] log "submap" quickCheck' 1000 \(TestMap m :: TestMap SmallKey Int) mmin mmax key -> let m' = M.submap mmin mmax m in - (if (maybe true (\min -> min <= key) mmin && - maybe true (\max -> max >= key) mmax) - then + ( if + ( maybe true (\min -> min <= key) mmin && + maybe true (\max -> max >= key) mmax + ) then M.lookup key m == M.lookup key m' else - (not (M.member key m'))) - "m: " <> show m - <> ", mmin: " <> show mmin - <> ", mmax: " <> show mmax - <> ", key: " <> show key + (not (M.member key m')) + ) + "m: " <> show m + <> ", mmin: " + <> show mmin + <> ", mmax: " + <> show mmax + <> ", key: " + <> show key log "foldrWithIndex maintains order" quickCheck \(TestMap m :: TestMap Int Int) -> - let outList = foldrWithIndex (\i a b -> (Tuple i a) : b) Nil m - in outList == sort outList + let + outList = foldrWithIndex (\i a b -> (Tuple i a) : b) Nil m + in + outList == sort outList log "bind" quickCheck $ \(TestMap m1) (TestMap m2 :: TestMap SmallKey Int) (TestMap m3) k -> @@ -392,10 +440,11 @@ mapTests = do u = do v <- m1 if v then m2 else m3 - in case M.lookup k m1 of - Just true -> M.lookup k m2 == M.lookup k u - Just false -> M.lookup k m3 == M.lookup k u - Nothing -> not $ M.member k u + in + case M.lookup k m1 of + Just true -> M.lookup k m2 == M.lookup k u + Just false -> M.lookup k m3 == M.lookup k u + Nothing -> not $ M.member k u log "catMaybes creates a new map of size less than or equal to the original" quickCheck \(TestMap m :: TestMap Int (Maybe Int)) -> do @@ -453,28 +502,31 @@ mapTests = do log "any" quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> - let list = M.toUnfoldable m - in case head list of + let + list = M.toUnfoldable m + in + case head list of + Nothing -> true + Just h -> case last list of Nothing -> true - Just h -> case last list of - Nothing -> true - Just l -> M.any (\x -> x == snd h) m && M.any (\x -> x == snd l) m + Just l -> M.any (\x -> x == snd h) m && M.any (\x -> x == snd l) m log "any with empty map" when (M.any (\_ -> true) (M.empty :: M.Map SmallKey Int)) $ throwException $ error "Test any with empty map failed" log "anyWithKey" quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> - let list = M.toUnfoldable m - in case head list of + let + list = M.toUnfoldable m + in + case head list of + Nothing -> true + Just h -> case last list of Nothing -> true - Just h -> case last list of - Nothing -> true - Just l -> M.anyWithKey (\k v -> k == fst h && v == snd h) m && M.anyWithKey (\k v -> k == fst l && v == snd l) m - + Just l -> M.anyWithKey (\k v -> k == fst h && v == snd h) m && M.anyWithKey (\k v -> k == fst l && v == snd l) m + log "anyWithKey with empty map" when (M.anyWithKey (\_ _ -> true) (M.empty :: M.Map SmallKey Int)) $ throwException $ error "Test anyWithKey with empty map failed" - smSingleton :: forall key value. key -> value -> M.SemigroupMap key value smSingleton k v = M.SemigroupMap (M.singleton k v) diff --git a/test/Test/Data/Set.purs b/test/Test/Data/Set.purs index 2128b7a..90479a0 100644 --- a/test/Test/Data/Set.purs +++ b/test/Test/Data/Set.purs @@ -15,30 +15,37 @@ setTests = do assert $ S.fromFoldable [] == (S.empty :: Set Unit) log "fromFoldable - non empty" - do let set = S.fromFoldable [0, 1, 1, 2] - assert $ S.size set == 3 - assert $ S.member 0 set - assert $ S.member 1 set - assert $ S.member 2 set + do + let set = S.fromFoldable [ 0, 1, 1, 2 ] + assert $ S.size set == 3 + assert $ S.member 0 set + assert $ S.member 1 set + assert $ S.member 2 set log "intersection" - do let s1 = S.fromFoldable [1,2,3,4,5] - s2 = S.fromFoldable [2,4,6,8,10] - s3 = S.fromFoldable [2,4] - assert $ S.intersection s1 s2 == s3 + do + let + s1 = S.fromFoldable [ 1, 2, 3, 4, 5 ] + s2 = S.fromFoldable [ 2, 4, 6, 8, 10 ] + s3 = S.fromFoldable [ 2, 4 ] + assert $ S.intersection s1 s2 == s3 log "catMaybes - drops Nothing values" - do let s1 = S.fromFoldable [Just 1,Just 2,Just 3,Nothing] - s2 = S.fromFoldable [1,2,3] - assert $ S.catMaybes s1 == s2 + do + let + s1 = S.fromFoldable [ Just 1, Just 2, Just 3, Nothing ] + s2 = S.fromFoldable [ 1, 2, 3 ] + assert $ S.catMaybes s1 == s2 log "toggle - inserts item" - assert $ S.toggle 1 S.empty == S.fromFoldable [1] + assert $ S.toggle 1 S.empty == S.fromFoldable [ 1 ] log "toggle - deletes item" - assert $ S.toggle 1 (S.fromFoldable [1]) == S.empty + assert $ S.toggle 1 (S.fromFoldable [ 1 ]) == S.empty log "range" - do let s1 = S.fromFoldable [1,2,3,4,5] - s2 = S.fromFoldable [2,3] - assert $ S.range (Just 2) (Just 3) s1 == s2 + do + let + s1 = S.fromFoldable [ 1, 2, 3, 4, 5 ] + s2 = S.fromFoldable [ 2, 3 ] + assert $ S.range (Just 2) (Just 3) s1 == s2 From 364fd776ed136f4205abf801eeb31a831099dc55 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 12:11:57 +0000 Subject: [PATCH 5/8] Credit migration pull request in changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cf3601..c97a871 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ New features: Other improvements: -- Migrate from Bower/Pulp to Spago and update CI. +- Migrate from Bower/Pulp to Spago and update CI (#76 by @thomashoneyman). - Improve documentation for Map functions with bounds parameters (#74 by @aranchelk). ## [v3.2.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.2.0) - 2024-03-19 From 73dcd4001fc193d2747e1ca4a1711f621ac65e70 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Thu, 15 Oct 2026 21:26:11 +0000 Subject: [PATCH 6/8] Restore Unreleased changelog template and date proposed release --- CHANGELOG.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c97a871..f9a338a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,17 @@ Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [v3.3.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.3.0) +## [Unreleased] + +Breaking changes: + +New features: + +Bugfixes: + +Other improvements: + +## [v3.3.0](https://github.com/purescript/purescript-ordered-collections/releases/tag/v3.3.0) - 2026-10-17 New features: From 3590d156e38f49370b9955886e3ab82a84586535 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Sat, 17 Oct 2026 13:27:34 +0000 Subject: [PATCH 7/8] Refine PureScript layout without changing generated code --- bench/src/Bench/Data/Map2a0bff.purs | 65 ++++---- bench/src/Bench/Data/Mapf149d5.purs | 65 ++++---- src/Data/Map/Internal.purs | 27 ++-- test/Test/Data/Map.purs | 230 ++++++++++++---------------- 4 files changed, 178 insertions(+), 209 deletions(-) diff --git a/bench/src/Bench/Data/Map2a0bff.purs b/bench/src/Bench/Data/Map2a0bff.purs index 34a2879..a0daffe 100644 --- a/bench/src/Bench/Data/Map2a0bff.purs +++ b/bench/src/Bench/Data/Map2a0bff.purs @@ -211,8 +211,9 @@ empty = Leaf -- | Test if a map is empty isEmpty :: forall k v. Map k v -> Boolean -isEmpty Leaf = true -isEmpty _ = false +isEmpty = case _ of + Leaf -> true + _ -> false -- | Create a map with one key/value pair singleton :: forall k v. k -> v -> Map k v @@ -354,7 +355,7 @@ foldSubmap :: forall k v m. Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v - foldSubmap = foldSubmapBy (<>) mempty foldSubmapBy :: forall k v m. Ord k => (m -> m -> m) -> m -> Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m -foldSubmapBy appendFn memptyValue kmin kmax f = +foldSubmapBy appendFn memptyValue kmin kmax f = do let tooSmall = case kmin of @@ -410,8 +411,7 @@ foldSubmapBy appendFn memptyValue kmin kmax f = `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) `appendFn` (if tooLarge k2 then memptyValue else go right) - in - go + go -- | Returns a new map containing all entries of the given map which lie -- | between a given lower and upper bound, treating `Nothing` as no bound i.e. @@ -433,13 +433,16 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | -- | ```purescript -- | Given any m :: Map k v, mmin :: Maybe k, mmax :: Maybe k, key :: k, --- | let m' = submap mmin mmax m in --- | if (maybe true (\min -> min <= key) mmin && --- | maybe true (\max -> max >= key) mmax) --- | then --- | lookup key m == lookup key m' --- | else --- | not (member key m') +-- | do +-- | let +-- | m' = submap mmin mmax m +-- | inBounds = +-- | maybe true (\min -> min <= key) mmin && +-- | maybe true (\max -> max >= key) mmax +-- | if inBounds then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton @@ -526,36 +529,32 @@ pop k = down Nil Two left k1 v1 right -> case right, comp k k1 of Leaf, EQ -> Just (Tuple v1 (up ctx Leaf)) - _, EQ -> + _, EQ -> do let max = maxNode left - in - Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) + Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) _, LT -> down (Cons (TwoLeft k1 v1 right) ctx) left _, _ -> down (Cons (TwoRight left k1 v1) ctx) right - Three left k1 v1 mid k2 v2 right -> + Three left k1 v1 mid k2 v2 right -> do let leaves = case left, mid, right of Leaf, Leaf, Leaf -> true _, _, _ -> false - in - case leaves, comp k k1, comp k k2 of - true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) - true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) - _, EQ, _ -> - let - max = maxNode left - in - Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) - _, _, EQ -> - let - max = maxNode mid - in - Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) - _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + case leaves, comp k k1, comp k k2 of + true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) + true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) + _, EQ, _ -> do + let + max = maxNode left + Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) + _, _, EQ -> do + let + max = maxNode mid + Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) + _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid + _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> Map k v -> Map k v up ctxs tree = diff --git a/bench/src/Bench/Data/Mapf149d5.purs b/bench/src/Bench/Data/Mapf149d5.purs index 144ee01..8cda61d 100644 --- a/bench/src/Bench/Data/Mapf149d5.purs +++ b/bench/src/Bench/Data/Mapf149d5.purs @@ -226,8 +226,9 @@ empty = Leaf -- | Test if a map is empty isEmpty :: forall k v. Map k v -> Boolean -isEmpty Leaf = true -isEmpty _ = false +isEmpty = case _ of + Leaf -> true + _ -> false -- | Create a map with one key/value pair singleton :: forall k v. k -> v -> Map k v @@ -369,7 +370,7 @@ foldSubmap :: forall k v m. Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v - foldSubmap = foldSubmapBy (<>) mempty foldSubmapBy :: forall k v m. Ord k => (m -> m -> m) -> m -> Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m -foldSubmapBy appendFn memptyValue kmin kmax f = +foldSubmapBy appendFn memptyValue kmin kmax f = do let tooSmall = case kmin of @@ -425,8 +426,7 @@ foldSubmapBy appendFn memptyValue kmin kmax f = `appendFn` (if tooSmall k2 || tooLarge k1 then memptyValue else go mid) `appendFn` (if inBounds k2 then f k2 v2 else memptyValue) `appendFn` (if tooLarge k2 then memptyValue else go right) - in - go + go -- | Returns a new map containing all entries of the given map which lie -- | between a given lower and upper bound, treating `Nothing` as no bound i.e. @@ -448,13 +448,16 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | -- | ```purescript -- | Given any m :: Map k v, mmin :: Maybe k, mmax :: Maybe k, key :: k, --- | let m' = submap mmin mmax m in --- | if (maybe true (\min -> min <= key) mmin && --- | maybe true (\max -> max >= key) mmax) --- | then --- | lookup key m == lookup key m' --- | else --- | not (member key m') +-- | do +-- | let +-- | m' = submap mmin mmax m +-- | inBounds = +-- | maybe true (\min -> min <= key) mmin && +-- | maybe true (\max -> max >= key) mmax +-- | if inBounds then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton @@ -541,36 +544,32 @@ pop k = down Nil Two left k1 v1 right -> case right, comp k k1 of Leaf, EQ -> Just (Tuple v1 (up ctx Leaf)) - _, EQ -> + _, EQ -> do let max = maxNode left - in - Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) + Just (Tuple v1 (removeMaxNode (Cons (TwoLeft max.key max.value right) ctx) left)) _, LT -> down (Cons (TwoLeft k1 v1 right) ctx) left _, _ -> down (Cons (TwoRight left k1 v1) ctx) right - Three left k1 v1 mid k2 v2 right -> + Three left k1 v1 mid k2 v2 right -> do let leaves = case left, mid, right of Leaf, Leaf, Leaf -> true _, _, _ -> false - in - case leaves, comp k k1, comp k k2 of - true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) - true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) - _, EQ, _ -> - let - max = maxNode left - in - Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) - _, _, EQ -> - let - max = maxNode mid - in - Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) - _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left - _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid - _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right + case leaves, comp k k1, comp k k2 of + true, EQ, _ -> Just (Tuple v1 (fromZipper ctx (Two Leaf k2 v2 Leaf))) + true, _, EQ -> Just (Tuple v2 (fromZipper ctx (Two Leaf k1 v1 Leaf))) + _, EQ, _ -> do + let + max = maxNode left + Just (Tuple v1 (removeMaxNode (Cons (ThreeLeft max.key max.value mid k2 v2 right) ctx) left)) + _, _, EQ -> do + let + max = maxNode mid + Just (Tuple v2 (removeMaxNode (Cons (ThreeMiddle left k1 v1 max.key max.value right) ctx) mid)) + _, LT, _ -> down (Cons (ThreeLeft k1 v1 mid k2 v2 right) ctx) left + _, GT, LT -> down (Cons (ThreeMiddle left k1 v1 k2 v2 right) ctx) mid + _, _, _ -> down (Cons (ThreeRight left k1 v1 mid k2 v2) ctx) right up :: List (TreeContext k v) -> Map k v -> Map k v up ctxs tree = diff --git a/src/Data/Map/Internal.purs b/src/Data/Map/Internal.purs index c5da604..4dc3da6 100644 --- a/src/Data/Map/Internal.purs +++ b/src/Data/Map/Internal.purs @@ -245,8 +245,9 @@ empty = Leaf -- | Test if a map is empty isEmpty :: forall k v. Map k v -> Boolean -isEmpty Leaf = true -isEmpty _ = false +isEmpty = case _ of + Leaf -> true + _ -> false -- | Create a map with one key/value pair singleton :: forall k v. k -> v -> Map k v @@ -384,7 +385,7 @@ foldSubmap :: forall k v m. Ord k => Monoid m => Maybe k -> Maybe k -> (k -> v - foldSubmap = foldSubmapBy (<>) mempty foldSubmapBy :: forall k v m. Ord k => (m -> m -> m) -> m -> Maybe k -> Maybe k -> (k -> v -> m) -> Map k v -> m -foldSubmapBy appendFn memptyValue kmin kmax f = +foldSubmapBy appendFn memptyValue kmin kmax f = do let tooSmall = case kmin of @@ -418,8 +419,7 @@ foldSubmapBy appendFn memptyValue kmin kmax f = (if tooSmall k then memptyValue else go left) `appendFn` (if inBounds k then f k v else memptyValue) `appendFn` (if tooLarge k then memptyValue else go right) - in - go + go -- | Returns a new map containing all entries with keys that fall within a -- | specified lower and upper bound (including keys equal to those bounds). @@ -440,13 +440,16 @@ foldSubmapBy appendFn memptyValue kmin kmax f = -- | -- | ```purescript -- | Given any m :: Map k v, mmin :: Maybe k, mmax :: Maybe k, key :: k, --- | let m' = submap mmin mmax m in --- | if (maybe true (\min -> min <= key) mmin && --- | maybe true (\max -> max >= key) mmax) --- | then --- | lookup key m == lookup key m' --- | else --- | not (member key m') +-- | do +-- | let +-- | m' = submap mmin mmax m +-- | inBounds = +-- | maybe true (\min -> min <= key) mmin && +-- | maybe true (\max -> max >= key) mmax +-- | if inBounds then +-- | lookup key m == lookup key m' +-- | else +-- | not (member key m') -- | ``` submap :: forall k v. Ord k => Maybe k -> Maybe k -> Map k v -> Map k v submap kmin kmax = foldSubmapBy union empty kmin kmax singleton diff --git a/test/Test/Data/Map.purs b/test/Test/Data/Map.purs index df4aa9c..8e5d790 100644 --- a/test/Test/Data/Map.purs +++ b/test/Test/Data/Map.purs @@ -36,16 +36,17 @@ derive instance eqSmallKey :: Eq SmallKey derive instance ordSmallKey :: Ord SmallKey instance showSmallKey :: Show SmallKey where - show A = "A" - show B = "B" - show C = "C" - show D = "D" - show E = "E" - show F = "F" - show G = "G" - show H = "H" - show I = "I" - show J = "J" + show = case _ of + A -> "A" + B -> "B" + C -> "C" + D -> "D" + E -> "E" + F -> "F" + G -> "G" + H -> "H" + I -> "I" + J -> "J" instance arbSmallKey :: Arbitrary SmallKey where arbitrary = elements $ cons' A [ B, C, D, E, F, G, H, I, J ] @@ -53,8 +54,9 @@ instance arbSmallKey :: Arbitrary SmallKey where data Instruction k v = Insert k v | Delete k instance showInstruction :: (Show k, Show v) => Show (Instruction k v) where - show (Insert k v) = "Insert (" <> show k <> ") (" <> show v <> ")" - show (Delete k) = "Delete (" <> show k <> ")" + show = case _ of + Insert k v -> "Insert (" <> show k <> ") (" <> show v <> ")" + Delete k -> "Delete (" <> show k <> ")" instance arbInstruction :: (Arbitrary k, Arbitrary v) => Arbitrary (Instruction k v) where arbitrary = oneOf $ cons' (Insert <$> arbitrary <*> arbitrary) [ Delete <$> arbitrary ] @@ -120,12 +122,11 @@ mapTests = do ("k1: " <> show k1 <> ", v1: " <> show v1 <> ", k2: " <> show k2 <> ", v2: " <> show v2) log "Check balance property" - quickCheck' 1000 $ \instrs -> + quickCheck' 1000 $ \instrs -> do let tree :: M.Map SmallKey Int tree = runInstructions instrs M.empty - in - M.checkValid tree ("Map not balanced:\n " <> show tree <> "\nGenerated by:\n " <> show instrs) + M.checkValid tree ("Map not balanced:\n " <> show tree <> "\nGenerated by:\n " <> show instrs) log "Lookup from empty" quickCheck $ \k -> M.lookup k (M.empty :: M.Map SmallKey Int) == Nothing @@ -134,21 +135,17 @@ mapTests = do quickCheck $ \k v -> M.lookup (k :: SmallKey) (M.singleton k (v :: Int)) == Just v log "Random lookup" - quickCheck' 1000 $ \instrs k v -> + quickCheck' 1000 $ \instrs k v -> do let tree :: M.Map SmallKey Int tree = M.insert k v (runInstructions instrs M.empty) - in - M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) + M.lookup k tree == Just v ("instrs:\n " <> show instrs <> "\nk:\n " <> show k <> "\nv:\n " <> show v) log "Singleton to list" quickCheck $ \k v -> M.toUnfoldable (M.singleton k v :: M.Map SmallKey Int) == singleton (Tuple k v) log "fromFoldable [] = empty" - quickCheck - ( M.fromFoldable [] == (M.empty :: M.Map Unit Unit) - "was not empty" - ) + quickCheck $ M.fromFoldable [] == (M.empty :: M.Map Unit Unit) "was not empty" log "fromFoldable & key collision" do @@ -158,10 +155,7 @@ mapTests = do quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") log "fromFoldableWith const [] = empty" - quickCheck - ( M.fromFoldableWith const [] == (M.empty :: M.Map Unit Unit) - "was not empty" - ) + quickCheck $ M.fromFoldableWith const [] == (M.empty :: M.Map Unit Unit) "was not empty" log "fromFoldableWith (+) & key collision" do @@ -171,19 +165,17 @@ mapTests = do quickCheck (M.lookup 2 nums == Nothing "invalid lookup - 2") log "sort . toUnfoldable . fromFoldable = sort (on lists without key-duplicates)" - quickCheck $ \(list :: List (Tuple SmallKey Int)) -> + quickCheck $ \(list :: List (Tuple SmallKey Int)) -> do let nubbedList = nubBy (compare `on` fst) list f x = M.toUnfoldable (M.fromFoldable x) - in - sort (f nubbedList) == sort nubbedList show nubbedList + sort (f nubbedList) == sort nubbedList show nubbedList log "fromFoldable . toUnfoldable = id" - quickCheck $ \(TestMap (m :: M.Map SmallKey Int)) -> + quickCheck $ \(TestMap (m :: M.Map SmallKey Int)) -> do let f m' = M.fromFoldable (M.toUnfoldable m' :: List (Tuple SmallKey Int)) - in - f m == m show m + f m == m show m log "fromFoldableWith const = fromFoldable" quickCheck $ \arr -> @@ -191,23 +183,21 @@ mapTests = do M.fromFoldable (arr :: List (Tuple SmallKey Int)) show arr log "fromFoldableWith (<>) = fromFoldable . collapse with (<>) . group on fst" - quickCheck $ \arr -> + quickCheck $ \arr -> do let combine (Tuple s a) (Tuple _ b) = (Tuple s $ b <> a) foldl1 g = unsafePartial \(Cons x xs) -> foldl g x xs f = M.fromFoldable <<< map (foldl1 combine <<< NEL.toList) <<< groupBy ((==) `on` fst) <<< sortBy (compare `on` fst) - in - M.fromFoldableWith (<>) arr === f (arr :: List (Tuple String String)) + M.fromFoldableWith (<>) arr === f (arr :: List (Tuple String String)) log "toUnfoldable is sorted" - quickCheck $ \(TestMap m) -> + quickCheck $ \(TestMap m) -> do let list = M.toUnfoldable (m :: M.Map SmallKey Int) ascList = M.toUnfoldable m - in - ascList === sortBy (compare `on` fst) list + ascList === sortBy (compare `on` fst) list log "Lookup from union" quickCheck $ \(TestMap m1) (TestMap m2) k -> @@ -225,28 +215,26 @@ mapTests = do log "unionWith" for_ [ Tuple (+) 0, Tuple (*) 1 ] $ \(Tuple op ident) -> - quickCheck $ \(TestMap m1) (TestMap m2) k -> + quickCheck $ \(TestMap m1) (TestMap m2) k -> do let u = M.unionWith op m1 m2 :: M.Map SmallKey Int - in - case M.lookup k u of - Nothing -> not (M.member k m1 || M.member k m2) - Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) + case M.lookup k u of + Nothing -> not (M.member k m1 || M.member k m2) + Just v -> v == op (fromMaybe ident (M.lookup k m1)) (fromMaybe ident (M.lookup k m2)) log "unionWith argument order" - quickCheck $ \(TestMap m1) (TestMap m2) k -> + quickCheck $ \(TestMap m1) (TestMap m2) k -> do let u = M.unionWith (-) m1 m2 :: M.Map SmallKey Int in1 = M.member k m1 v1 = M.lookup k m1 in2 = M.member k m2 v2 = M.lookup k m2 - in - case M.lookup k u of - Just v | in1 && in2 -> Just v == ((-) <$> v1 <*> v2) - Just v | in1 -> Just v == v1 - Just v -> Just v == v2 - Nothing -> not (in1 || in2) + case M.lookup k u of + Just v | in1 && in2 -> Just v == ((-) <$> v1 <*> v2) + Just v | in1 -> Just v == v1 + Just v -> Just v == v2 + Nothing -> not (in1 || in2) log "Lookup from intersection" quickCheck $ \(TestMap m1) (TestMap m2) k -> @@ -261,87 +249,79 @@ mapTests = do log "intersectionWith" for_ [ (+), (*) ] $ \op -> - quickCheck $ \(TestMap m1) (TestMap m2) k -> + quickCheck $ \(TestMap m1) (TestMap m2) k -> do let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int - in - case M.lookup k u of - Nothing -> not (M.member k m1 && M.member k m2) - Just v -> Just v == (op <$> M.lookup k m1 <*> M.lookup k m2) + case M.lookup k u of + Nothing -> not (M.member k m1 && M.member k m2) + Just v -> Just v == (op <$> M.lookup k m1 <*> M.lookup k m2) log "map-apply is equivalent to intersectionWith" for_ [ (+), (*) ] $ \op -> - quickCheck $ \(TestMap m1) (TestMap m2) -> + quickCheck $ \(TestMap m1) (TestMap m2) -> do let u = M.intersectionWith op m1 m2 :: M.Map SmallKey Int - in - u == (op <$> m1 <*> m2) + u == (op <$> m1 <*> m2) log "difference" - quickCheck $ \(TestMap m1) (TestMap m2) -> + quickCheck $ \(TestMap m1) (TestMap m2) -> do let d = M.difference (m1 :: M.Map SmallKey Int) (m2 :: M.Map SmallKey String) - in - if M.size d == 0 then - and (map (\k -> M.member k m2) (A.fromFoldable $ M.keys m1)) - else - and (map (\k -> M.member k m1) (A.fromFoldable $ M.keys d)) && - and (map (\k -> not $ M.member k d) (A.fromFoldable $ M.keys m2)) + if M.size d == 0 then + and (map (\k -> M.member k m2) (A.fromFoldable $ M.keys m1)) + else + and (map (\k -> M.member k m1) (A.fromFoldable $ M.keys d)) && + and (map (\k -> not $ M.member k d) (A.fromFoldable $ M.keys m2)) log "size" - quickCheck $ \xs -> + quickCheck $ \xs -> do let xs' = nubBy (compare `on` fst) xs - in - M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int)) + M.size (M.fromFoldable xs') == length (xs' :: List (Tuple SmallKey Int)) log "lookupLE result is correct" quickCheck $ \k (TestMap m) -> case M.lookupLE k (smallKeyToNumberMap m) of Nothing -> all (_ > k) $ M.keys m - Just { key: k1, value: v } -> + Just { key: k1, value: v } -> do let isCloserKey k2 = k1 < k2 && k2 < k isLTwhenEQexists = k1 < k && M.member k m - in - k1 <= k - && all (not <<< isCloserKey) (M.keys m) - && not isLTwhenEQexists - && M.lookup k1 m == Just v + k1 <= k + && all (not <<< isCloserKey) (M.keys m) + && not isLTwhenEQexists + && M.lookup k1 m == Just v log "lookupGE result is correct" quickCheck $ \k (TestMap m) -> case M.lookupGE k (smallKeyToNumberMap m) of Nothing -> all (_ < k) $ M.keys m - Just { key: k1, value: v } -> + Just { key: k1, value: v } -> do let isCloserKey k2 = k < k2 && k2 < k1 isGTwhenEQexists = k < k1 && M.member k m - in - k1 >= k - && all (not <<< isCloserKey) (M.keys m) - && not isGTwhenEQexists - && M.lookup k1 m == Just v + k1 >= k + && all (not <<< isCloserKey) (M.keys m) + && not isGTwhenEQexists + && M.lookup k1 m == Just v log "lookupLT result is correct" quickCheck $ \k (TestMap m) -> case M.lookupLT k (smallKeyToNumberMap m) of Nothing -> all (_ >= k) $ M.keys m - Just { key: k1, value: v } -> + Just { key: k1, value: v } -> do let isCloserKey k2 = k1 < k2 && k2 < k - in - k1 < k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v + k1 < k + && all (not <<< isCloserKey) (M.keys m) + && M.lookup k1 m == Just v log "lookupGT result is correct" quickCheck $ \k (TestMap m) -> case M.lookupGT k (smallKeyToNumberMap m) of Nothing -> all (_ <= k) $ M.keys m - Just { key: k1, value: v } -> + Just { key: k1, value: v } -> do let isCloserKey k2 = k < k2 && k2 < k1 - in - k1 > k - && all (not <<< isCloserKey) (M.keys m) - && M.lookup k1 m == Just v + k1 > k + && all (not <<< isCloserKey) (M.keys m) + && M.lookup k1 m == Just v log "findMin result is correct" quickCheck $ \(TestMap m) -> case M.findMin (smallKeyToNumberMap m) of @@ -354,14 +334,13 @@ mapTests = do Just { key: k, value: v } -> M.lookup k m == Just v && all (_ <= k) (M.keys m) log "mapWithKey is correct" - quickCheck $ \(TestMap m :: TestMap String Int) -> + quickCheck $ \(TestMap m :: TestMap String Int) -> do let f k v = k <> show v resultViaMapWithKey = m # mapWithIndex f toList = M.toUnfoldable :: forall k v. M.Map k v -> List (Tuple k v) resultViaLists = m # toList # map (\(Tuple k v) -> Tuple k (f k v)) # M.fromFoldable - in - resultViaMapWithKey === resultViaLists + resultViaMapWithKey === resultViaLists log "filterWithKey gives submap" quickCheck $ \(TestMap s :: TestMap String Int) p -> @@ -407,44 +386,35 @@ mapTests = do == M.fromFoldable [ Tuple B 0 ] log "submap" - quickCheck' 1000 \(TestMap m :: TestMap SmallKey Int) mmin mmax key -> + quickCheck' 1000 \(TestMap m :: TestMap SmallKey Int) mmin mmax key -> do let m' = M.submap mmin mmax m - in - ( if - ( maybe true (\min -> min <= key) mmin && - maybe true (\max -> max >= key) mmax - ) then - M.lookup key m == M.lookup key m' - else - (not (M.member key m')) - ) - "m: " <> show m - <> ", mmin: " - <> show mmin - <> ", mmax: " - <> show mmax - <> ", key: " - <> show key + ( if + ( maybe true (\min -> min <= key) mmin && + maybe true (\max -> max >= key) mmax + ) then + M.lookup key m == M.lookup key m' + else + (not (M.member key m')) + ) + "m: " <> show m <> ", mmin: " <> show mmin <> ", mmax: " <> show mmax <> ", key: " <> show key log "foldrWithIndex maintains order" - quickCheck \(TestMap m :: TestMap Int Int) -> + quickCheck \(TestMap m :: TestMap Int Int) -> do let outList = foldrWithIndex (\i a b -> (Tuple i a) : b) Nil m - in - outList == sort outList + outList == sort outList log "bind" - quickCheck $ \(TestMap m1) (TestMap m2 :: TestMap SmallKey Int) (TestMap m3) k -> + quickCheck $ \(TestMap m1) (TestMap m2 :: TestMap SmallKey Int) (TestMap m3) k -> do let u = do v <- m1 if v then m2 else m3 - in - case M.lookup k m1 of - Just true -> M.lookup k m2 == M.lookup k u - Just false -> M.lookup k m3 == M.lookup k u - Nothing -> not $ M.member k u + case M.lookup k m1 of + Just true -> M.lookup k m2 == M.lookup k u + Just false -> M.lookup k m3 == M.lookup k u + Nothing -> not $ M.member k u log "catMaybes creates a new map of size less than or equal to the original" quickCheck \(TestMap m :: TestMap Int (Maybe Int)) -> do @@ -501,29 +471,27 @@ mapTests = do result == right log "any" - quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> + quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> do let list = M.toUnfoldable m - in - case head list of + case head list of + Nothing -> true + Just h -> case last list of Nothing -> true - Just h -> case last list of - Nothing -> true - Just l -> M.any (\x -> x == snd h) m && M.any (\x -> x == snd l) m + Just l -> M.any (\x -> x == snd h) m && M.any (\x -> x == snd l) m log "any with empty map" when (M.any (\_ -> true) (M.empty :: M.Map SmallKey Int)) $ throwException $ error "Test any with empty map failed" log "anyWithKey" - quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> + quickCheck $ \(TestMap m :: TestMap SmallKey Int) -> do let list = M.toUnfoldable m - in - case head list of + case head list of + Nothing -> true + Just h -> case last list of Nothing -> true - Just h -> case last list of - Nothing -> true - Just l -> M.anyWithKey (\k v -> k == fst h && v == snd h) m && M.anyWithKey (\k v -> k == fst l && v == snd l) m + Just l -> M.anyWithKey (\k v -> k == fst h && v == snd h) m && M.anyWithKey (\k v -> k == fst l && v == snd l) m log "anyWithKey with empty map" when (M.anyWithKey (\_ _ -> true) (M.empty :: M.Map SmallKey Int)) $ throwException $ error "Test anyWithKey with empty map failed" From 73a9f4543871ef8dabba95022d400186ac68d2f2 Mon Sep 17 00:00:00 2001 From: Thomas Honeyman Date: Sat, 17 Oct 2026 16:30:53 +0000 Subject: [PATCH 8/8] Link Unreleased changelog section --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9a338a..1dd632c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Notable changes to this project are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] +## [Unreleased](https://github.com/purescript/purescript-ordered-collections/compare/v3.3.0...HEAD) Breaking changes: