on a515601 help sign in
.github/workflows/haskell.ymldeleted−47
since splitsnapshots
unifiedsplit
@@ -1,47 +0,0 @@
1−name: github-action
2−
3−on: [push, pull_request]
4−
5−jobs:
6− build:
7− strategy:
8− matrix:
9− ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2']
10− os: ['ubuntu-latest', 'macos-latest']
11− exclude:
12− # There are some linker warnings in 802 on darwin that
13− # cause compilation to fail
14− # See https://github.com/NixOS/nixpkgs/issues/25139
15− - ghc: '8.0.2'
16− os: 'macos-latest'
17− runs-on: ${{ matrix.os }}
18−
19− name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
20− steps:
21− - uses: actions/checkout@v2
22− - uses: actions/setup-haskell@v1
23− with:
24− ghc-version: ${{ matrix.ghc }}
25− - name: Cache
26− uses: actions/cache@v1
27− env:
28− cache-name: cache-cabal
29− with:
30− path: ~/.cabal
31− key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
32− restore-keys: |
33− ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
34− ${{ runner.os }}-${{ matrix.ghc }}-build-
35− ${{ runner.os }}-${{ matrix.ghc }}-
36− ${{ runner.os }}
37−
38− - name: Install dependencies
39− run: |
40− cabal update
41− cabal build --only-dependencies --enable-tests --enable-benchmarks
42− - name: Build
43− run: cabal build --enable-tests --enable-benchmarks all
44− - name: Run tests
45− run: cabal test --enable-tests all
46− - name: Build Docs
47− run: cabal haddock
.travis.ymlnew file+171
@@ -0,0 +1,171 @@
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.3.20190814
8+#
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
15+cache:
16+ directories:
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
28+matrix:
29+ include:
30+ - compiler: ghcjs-8.4
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32+ - compiler: ghc-8.8.1
33+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
34+ - compiler: ghc-8.6.5
35+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
36+ - compiler: ghc-8.4.4
37+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
38+ - compiler: ghc-8.2.2
39+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
40+ - compiler: ghc-8.0.2
41+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
42+before_install:
43+ - |
44+ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
45+ sudo add-apt-repository -y ppa:hvr/ghcjs;
46+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
47+ sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
48+ sudo apt-get update;
49+ sudo apt-get install $CC cabal-install-3.0 nodejs;
50+ fi
51+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
52+ - WITHCOMPILER="-w $HC"
53+ - |
54+ if echo $CC | grep -q ghcjs; then
55+ GHCJS=true
56+ HC=${HC}js
57+ WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
58+ else
59+ GHCJS=false;
60+ fi
61+ - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
62+ - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
63+ - HCPKG="$HC-pkg"
64+ - unset CC
65+ - CABAL=/opt/ghc/bin/cabal
66+ - CABALHOME=$HOME/.cabal
67+ - export PATH="$CABALHOME/bin:$PATH"
68+ - TOP=$(pwd)
69+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
70+ - echo $HCNUMVER
71+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
72+ - set -o pipefail
73+ - |
74+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
75+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
76+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
77+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
78+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
79+ echo ' if (state == "cabal") {' >> .colorful.awk
80+ echo ' print blue($0)' >> .colorful.awk
81+ echo ' } else {' >> .colorful.awk
82+ echo ' print $0' >> .colorful.awk
83+ echo ' }' >> .colorful.awk
84+ echo '}' >> .colorful.awk
85+ - cat .colorful.awk
86+ - |
87+ color_cabal_output () {
88+ awk -f $TOP/.colorful.awk
89+ }
90+ - echo text | color_cabal_output
91+install:
92+ - ${CABAL} --version
93+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
94+ - node --version
95+ - echo $GHCJS
96+ - TEST=--enable-tests
97+ - BENCH=--enable-benchmarks
98+ - BENCH=--disable-benchmarks
99+ - HEADHACKAGE=false
100+ - rm -f $CABALHOME/config
101+ - |
102+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
103+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
104+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
105+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
106+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
107+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
108+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
109+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
110+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
111+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
112+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
113+ echo "install-dirs user" >> $CABALHOME/config
114+ echo " prefix: $CABALHOME" >> $CABALHOME/config
115+ echo "repository hackage.haskell.org" >> $CABALHOME/config
116+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
117+ - cat $CABALHOME/config
118+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
119+ - travis_retry ${CABAL} v2-update -v
120+ - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
121+ # Generate cabal.project
122+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
123+ - touch cabal.project
124+ - |
125+ echo "packages: ." >> cabal.project
126+ - |
127+ - cat cabal.project || true
128+ - cat cabal.project.local || true
129+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
130+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
131+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
132+ - rm cabal.project.freeze
133+script:
134+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
135+ # Packaging...
136+ - ${CABAL} v2-sdist all | color_cabal_output
137+ # Unpacking...
138+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
139+ - cd ${DISTDIR} || false
140+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
141+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
142+ - PKGDIR_patch="$(find . -maxdepth 1 -type d -regex '.*/patch-[0-9.]*')"
143+ # Generate cabal.project
144+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
145+ - touch cabal.project
146+ - |
147+ echo "packages: ${PKGDIR_patch}" >> cabal.project
148+ - |
149+ - cat cabal.project || true
150+ - cat cabal.project.local || true
151+ # Building...
152+ # this builds all libraries and executables (without tests/benchmarks)
153+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
154+ # Building with tests and benchmarks...
155+ # build & run tests, build benchmarks
156+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
157+ # Testing...
158+ - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
159+ # cabal check...
160+ - (cd ${PKGDIR_patch} && ${CABAL} -vnormal check)
161+ # haddock...
162+ - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
163+ # Constraint sets
164+ - rm -rf cabal.project.local
165+ # Constraint set old-these
166+ - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi
167+ # Constraint set old-witherable
168+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
169+
170+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
171+# EOF
CONTRIBUTING.md+1−1
@@ -16,7 +16,7 @@ Before opening an issue, please check whether your issue has already been report
16* Describe the issue you're encountering or the suggestion you're making
16* Describe the issue you're encountering or the suggestion you're making
17* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
17* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
18* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
18* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
19−* Describe how you're building patch (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
19+* Describe how you're building patch (i.e., via patch-platform, cabal install, stack, obelisk, etc.). If you're using patch-platform or obelisk, provide the git hash of your checkout.
20
20
21## Submitting Changes
21## Submitting Changes
22
22
ChangeLog.md+2−48
@@ -2,60 +2,14 @@
2
2
3## Unreleased
3## Unreleased
4
4
5−* New dep of `data-orphans` for old GHC to get instances honestly instead of
6− via `monoidal-containers`.
7−
8−## 0.0.5.0 - 2021-12-17
9−
10−* `Additive` now lives in `Data.Semigroup.Additive`, but is still reexported
11− from `Data.Patch` for compatability.
12−
13−* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
14− Care is taken to make this not a breaking change.
15− In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
16− There are complete constructor and field patterns too, and everything is
17− exported under the newtype as real constructors and fields would be.
18−
19−## 0.0.4.0 - 2021-04-20
20−
21−* Enable PolyKinds
22−
23−## 0.0.3.2 - 2020-11-06
24−
25−* Update version bounds
26−
27−## 0.0.3.1 - 2020-02-05
28−
29−* Replace `fromJust` with something easier to debug.
30−
31−## 0.0.3.0 - 2020-02-05
32−
33−* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
34−
35−* Create `DecidablyEmpty` subclass of `Monoid`.
36−
37−## 0.0.2.0 - 2020-01-17
38−
39−* Consistently provide:
40−
41− - `Wrapped` instances
42−
43− - `*WithIndex` instances
44−
45− - `un*` newtype unwrappers
46−
47− for `PatchMap`, `PatchIntMap`, and `PatchMapWithMove`.
48−
49−## 0.0.1.0 - 2020-01-09
50−
51* Support older GHCs with `split-these` flag.
5* Support older GHCs with `split-these` flag.
52
6
53* Additional instances for the `Group` class for basic types.
7* Additional instances for the `Group` class for basic types.
54
8
55−## 0.0.0.1 - 2020-01-08
9+## 0.0.0.1
56
10
57* Remove unneeded dependencies
11* Remove unneeded dependencies
58
12
59−## 0.0.0.0 - 2020-01-08
13+## 0.0.0.0
60
14
61* Extract patching functionality from Reflex.
15* Extract patching functionality from Reflex.
README.md+2−51
@@ -1,52 +1,3 @@
1−# patch
1+## Patch
2
2
3−[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/patch.svg)](https://hackage.haskell.org/package/patch) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/patch/badge)](https://matrix.hackage.haskell.org/#/package/patch) [![Travis CI](https://api.travis-ci.org/reflex-frp/patch.svg?branch=develop)](https://travis-ci.org/reflex-frp/patch) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/patch/LICENSE)
3+Infrastructure for writing patches which act on other types.
4−
5−Data structures for describing changes to other data structures.
6−
7−A `Patch` type represents a kind of change made to a data structure.
8−
9−```haskell
10−class Patch p where
11− type PatchTarget p :: *
12− -- | Apply the patch p a to the value a. If no change is needed, return
13− -- 'Nothing'.
14− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
15−```
16−
17−## Patching Maps
18−For example, `Data.Patch.Map` defines the `PatchMap` type which can be used to patch `Map`s. A `PatchMap` represents updates to a `Map` that can insert, remove, or replace items in the `Map`. In this example, the `Map` is the `PatchTarget` and the `PatchMap` is the `Patch`. Keep in mind that there are many other possible `Patch`es that can be applied to a `Map` (i.e., `Map` can be the `PatchTarget` for many different `Patch` instances).
19−
20−`PatchMap` is defined as:
21−
22−```haskell
23−newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
24−```
25−
26−The `Maybe` around the value is used to represent insertion/updates or deletion of the element at a given key.
27−
28−Its `Patch` instance begins with:
29−
30−```haskell
31−instance Ord k => Patch (PatchMap k v) where
32− type PatchTarget (PatchMap k v) = Map k v
33− ...
34−```
35−
36−When a `PatchMap` is applied to its `PatchTarget`, the following changes can occur:
37−
38−- If the key is present in the `Patch` and the `PatchTarget`...
39−
40− - And the `Patch` value at that key is `Nothing`: delete that key from the `PatchTarget`.
41−
42− - And the `Patch` value at that key is `Just x`: update the value at that key in the `PatchTarget` to `x`.
43−
44−- If the key is present in the `Patch` and not present in the `PatchTarget`...
45−
46− - And the `Patch` value at that key is `Nothing`: do nothing because we're trying to delete a key that doesn't exist in the target in the first place.
47−
48− - And the `Patch` value at that key is `Just x`: insert the key and the value `x` into the `PatchTarget`
49−
50−- If the key is *not* present in the `Patch` but present in the `PatchTarget`: do nothing.
51−
52−There are, of course, more complicated ways of patching maps involving, for example, moving values from one key to another. You can find the code for that in `Data.Patch.PatchMapWithMove`. Note that the `PatchTarget` type associated with the `PatchMapWithMove` patch instance is still `Map k v`!
cabal.haskell-cinew file+14
@@ -0,0 +1,14 @@
1+distribution: xenial
2+benchmarks: False
3+unconstrained: False
4+installed: -all
5+
6+-- https://github.com/haskell/cabal/issues/6106
7+install-dependencies: False
8+
9+constraint-set old-these
10+ ghc: <8.8
11+ constraints: these <1
12+
13+constraint-set old-witherable
14+ constraints: witherable <0.3.2
dep/reflex-platform/default.nix+6−7
@@ -1,8 +1,7 @@
1# DO NOT HAND-EDIT THIS FILE
1# DO NOT HAND-EDIT THIS FILE
2−let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
3− if !fetchSubmodules && !private then builtins.fetchTarball {
4− url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
5− } else (import <nixpkgs> {}).fetchFromGitHub {
2+import ((import <nixpkgs> {}).fetchFromGitHub (
6− inherit owner repo rev sha256 fetchSubmodules private;
7− };
8−in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))
3+ let json = builtins.fromJSON (builtins.readFile ./github.json);
4+ in { inherit (json) owner repo rev sha256;
5+ private = json.private or false;
6+ }
7+))
dep/reflex-platform/github.json+2−3
@@ -2,7 +2,6 @@
2 "owner": "reflex-frp",
2 "owner": "reflex-frp",
3 "repo": "reflex-platform",
3 "repo": "reflex-platform",
4 "branch": "master",
4 "branch": "master",
5− "private": false,
5+ "rev": "510b990d0b11f0626afbec5fe8575b5b2395391b",
6− "rev": "c9d11db1b98855fe8ab24a3ff6a5dbe0ad902ad9",
6+ "sha256": "09cmahsbxr0963wq171c7j139iyzz49hramr4v9nsf684wcwkngv"
7− "sha256": "0sfzkqdvyah5mwvmli0wq1nl0b8cvk2cmfgfy4rz57wv42x3099y"
8}
7}
patch.cabal+10−35
@@ -1,9 +1,7 @@
1Name: patch
1Name: patch
2−Version: 0.0.5.0
2+Version: 0.0.0.1
3−Synopsis: Data structures for describing changes to other data structures.
3+Synopsis: Infrastructure for writing patches which act on other types.
4Description:
4Description:
5− Data structures for describing changes to other data structures.
6− .
7 In this library, a patch is something which can be applied, analogous to a
5 In this library, a patch is something which can be applied, analogous to a
8 function, and which distinguishes returning the argument it was provided from
6 function, and which distinguishes returning the argument it was provided from
9 returning something else.
7 returning something else.
@@ -14,7 +12,7 @@ Maintainer: maintainer@obsidian.systems
14Stability: Experimental
12Stability: Experimental
15Category: FRP
13Category: FRP
16Build-type: Simple
14Build-type: Simple
17−Cabal-version: >=1.10
15+Cabal-version: >=1.9.2
18homepage: https://obsidian.systems
16homepage: https://obsidian.systems
19bug-reports: https://github.com/reflex-frp/patch/issues
17bug-reports: https://github.com/reflex-frp/patch/issues
20extra-source-files:
18extra-source-files:
@@ -22,7 +20,7 @@ extra-source-files:
22 ChangeLog.md
20 ChangeLog.md
23
21
24tested-with:
22tested-with:
25− GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
23+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
26 GHCJS ==8.4
24 GHCJS ==8.4
27
25
28flag split-these
26flag split-these
@@ -32,22 +30,16 @@ flag split-these
32
30
33library
31library
34 hs-source-dirs: src
32 hs-source-dirs: src
35− default-language: Haskell2010
36− build-depends: base >= 4.9 && < 4.15
33+ build-depends: base >= 4.9 && < 4.14
37 , constraints-extras >= 0.3 && < 0.4
34 , constraints-extras >= 0.3 && < 0.4
38 , containers >= 0.6 && < 0.7
35 , containers >= 0.6 && < 0.7
39− , dependent-map >= 0.3 && < 0.5
36+ , dependent-map >= 0.3 && < 0.4
40− , dependent-sum >= 0.6 && < 0.8
37+ , dependent-sum >= 0.6 && < 0.7
41− , lens >= 4.7 && < 5
42 , semigroupoids >= 4.0 && < 6
38 , semigroupoids >= 4.0 && < 6
43 , transformers >= 0.5.6.0 && < 0.6
39 , transformers >= 0.5.6.0 && < 0.6
44− , witherable >= 0.3 && < 0.5
40+ , witherable >= 0.3 && < 0.3.2
45−
46− if impl(ghc < 8.6) -- really, if base < 8.12
47− build-depends: base-orphans >= 0.8 && < 0.9
48
41
49 exposed-modules: Data.Functor.Misc
42 exposed-modules: Data.Functor.Misc
50− , Data.Monoid.DecidablyEmpty
51 , Data.Patch
43 , Data.Patch
52 , Data.Patch.Class
44 , Data.Patch.Class
53 , Data.Patch.DMap
45 , Data.Patch.DMap
@@ -55,35 +47,18 @@ library
55 , Data.Patch.IntMap
47 , Data.Patch.IntMap
56 , Data.Patch.Map
48 , Data.Patch.Map
57 , Data.Patch.MapWithMove
49 , Data.Patch.MapWithMove
58− , Data.Patch.MapWithPatchingMove
59− , Data.Semigroup.Additive
60
50
61 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
51 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
62− default-extensions: PolyKinds
63
52
64 if flag(split-these)
53 if flag(split-these)
65− build-depends: these >= 1 && <1.2
54+ build-depends: these >= 1 && <1.1
66 , semialign >=1 && <1.2
55 , semialign >=1 && <1.2
67 , monoidal-containers >= 0.6 && < 0.7
56 , monoidal-containers >= 0.6 && < 0.7
68 else
57 else
69 build-depends: these >= 0.4 && <0.9
58 build-depends: these >= 0.4 && <0.9
70 , monoidal-containers == 0.4.0.0
59 , monoidal-containers == 0.4.0.0
71
60
72−test-suite tests
73− default-language: Haskell2010
74− type: exitcode-stdio-1.0
75− main-is: tests.hs
76− hs-source-dirs: test
77− build-depends: base
78− , patch
79− , containers
80− , hedgehog
81− , HUnit
82− if impl(ghcjs)
83− buildable: False
84−
85test-suite hlint
61test-suite hlint
86− default-language: Haskell2010
87 type: exitcode-stdio-1.0
62 type: exitcode-stdio-1.0
88 main-is: hlint.hs
63 main-is: hlint.hs
89 hs-source-dirs: test
64 hs-source-dirs: test
@@ -91,7 +66,7 @@ test-suite hlint
91 , directory
66 , directory
92 , filepath
67 , filepath
93 , filemanip
68 , filemanip
94− , hlint (< 2.1 || >= 2.2.2) && < 3.3
69+ , hlint < 2.1 || >= 2.2.2
95 if impl(ghcjs)
70 if impl(ghcjs)
96 buildable: False
71 buildable: False
97
72
release.nix+10−20
@@ -18,26 +18,16 @@ let
18 "ghcIosAarch64"
18 "ghcIosAarch64"
19 ];
19 ];
20 compilerPkgs = lib.genAttrs compilers (ghc: let
20 compilerPkgs = lib.genAttrs compilers (ghc: let
21− reflex-platform = reflex-platform-fun {
22− inherit system;
23− haskellOverlays = [
24− # Use this package's source for reflex
25− (self: super: {
26− _dep = super._dep // {
27− patch = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
21+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
28− "release.nix"
22+ "release.nix"
29− ".git"
23+ ".git"
30− "dist"
24+ "dist"
31− "dist-newstyle"
25+ "dist-newstyle"
32− "cabal.haskell-ci"
26+ "cabal.haskell-ci"
33− "cabal.project"
27+ "cabal.project"
34− ".travis.yml"
28+ ".travis.yml"
35− ])) ./.;
29+ ])) ./.;
36− };
37− })
38− ];
39− };
40− in reflex-platform.${ghc}.patch);
30+ in reflex-platform.${ghc}.callCabal2nix "patch" src {});
41 in compilerPkgs // {
31 in compilerPkgs // {
42 cache = reflex-platform.pinBuildInputs "patch-${system}"
32 cache = reflex-platform.pinBuildInputs "patch-${system}"
43 (builtins.attrValues compilerPkgs);
33 (builtins.attrValues compilerPkgs);
src/Data/Functor/Misc.hs+3−2
@@ -6,6 +6,7 @@
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
@@ -47,7 +48,7 @@ import Data.IntMap (IntMap)
47import qualified Data.IntMap as IntMap
48import qualified Data.IntMap as IntMap
48import Data.Map (Map)
49import Data.Map (Map)
49import qualified Data.Map as Map
50import qualified Data.Map as Map
50−import Data.Some (Some, mkSome)
51+import Data.Some (Some(Some))
51import Data.These
52import Data.These
52import Data.Type.Equality ((:~:)(Refl))
53import Data.Type.Equality ((:~:)(Refl))
53import Data.Typeable hiding (Refl)
54import Data.Typeable hiding (Refl)
@@ -116,7 +117,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
116-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
117-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
117-- the keys, using a function to remove the wrapping 'Functor'
118-- the keys, using a function to remove the wrapping 'Functor'
118weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
119weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
119−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
120+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
120
121
121--------------------------------------------------------------------------------
122--------------------------------------------------------------------------------
122-- WrapArg
123-- WrapArg
src/Data/Monoid/DecidablyEmpty.hsdeleted−115
@@ -1,115 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DefaultSignatures #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE StandaloneDeriving #-}
5−{-# LANGUAGE TypeOperators #-}
6−
7−-- TODO upstream somwhere else?
8−module Data.Monoid.DecidablyEmpty where
9−
10−import Data.Functor.Identity
11−import Data.Functor.Const
12−import Data.Monoid
13−import Data.Maybe (isNothing)
14−#if MIN_VERSION_base(4,11,0)
15−import Data.Ord
16−#endif
17−import Data.Proxy
18−import Data.Semigroup hiding (First, Last)
19−#if MIN_VERSION_base(4,12,0)
20−import GHC.Generics
21−#endif
22−
23−import qualified Data.IntSet as IntSet
24−import qualified Data.IntMap as IntMap
25−import qualified Data.Map as Map
26−import qualified Data.Sequence as Seq
27−import qualified Data.Set as Set
28−
29−import Data.GADT.Compare
30−import qualified Data.Dependent.Map as DMap
31−
32−-- | A 'DecidablyEmpty' is one where it can be computed whether or not an
33−-- arbitrary value is 'mempty'.
34−--
35−-- By using this class rather than 'Eq', we avoid unnecessary constraining the
36−-- contents of 'Functor's. This makes it possible to efficiently combine and/or
37−-- nest patch maps with 'Eq'-lacking values (e.g. functions) at the leaves.
38−class Monoid a => DecidablyEmpty a where
39− isEmpty :: a -> Bool
40− default isEmpty :: Eq a => a -> Bool
41− isEmpty = (==) mempty
42−
43−-- base
44−
45−instance DecidablyEmpty Ordering
46−instance DecidablyEmpty ()
47−instance DecidablyEmpty Any
48−instance DecidablyEmpty All
49−-- instance DecidablyEmpty Lifetime
50−-- instance DecidablyEmpty Event
51−instance DecidablyEmpty [a] where
52− isEmpty = null
53−instance
54−#if MIN_VERSION_base(4,11,0)
55− Semigroup a
56−#else
57− Monoid a
58−#endif
59− => DecidablyEmpty (Maybe a) where
60− isEmpty = isNothing
61−deriving instance (Num a, DecidablyEmpty a) => DecidablyEmpty (Product a)
62−deriving instance (DecidablyEmpty a, Num a) => DecidablyEmpty (Sum a)
63−deriving instance DecidablyEmpty a => DecidablyEmpty (Dual a)
64−instance DecidablyEmpty (First a) where
65− isEmpty (First a) = isNothing a
66−instance DecidablyEmpty (Last a) where
67− isEmpty (Last a) = isNothing a
68−deriving instance DecidablyEmpty a => DecidablyEmpty (Identity a)
69−instance Semigroup a => DecidablyEmpty (Option a) where
70− isEmpty (Option a) = isNothing a
71−deriving instance DecidablyEmpty m => DecidablyEmpty (WrappedMonoid m)
72−instance (Ord a, Bounded a) => DecidablyEmpty (Max a)
73−instance (Ord a, Bounded a) => DecidablyEmpty (Min a)
74−instance DecidablyEmpty (Proxy s)
75−deriving instance DecidablyEmpty a => DecidablyEmpty (Const a b)
76−#if MIN_VERSION_base(4,11,0)
77−deriving instance DecidablyEmpty a => DecidablyEmpty (Down a)
78−#endif
79−#if MIN_VERSION_base(4,12,0)
80−deriving instance DecidablyEmpty p => DecidablyEmpty (Par1 p)
81−instance DecidablyEmpty (U1 p)
82−deriving instance DecidablyEmpty (f p) => DecidablyEmpty (Rec1 f p)
83−deriving instance DecidablyEmpty (f p) => DecidablyEmpty (M1 i c f p)
84−deriving instance DecidablyEmpty c => DecidablyEmpty (K1 i c p)
85−instance (DecidablyEmpty (f p), DecidablyEmpty (g p)) => DecidablyEmpty ((f :*: g) p) where
86− isEmpty (x :*: y) = isEmpty x && isEmpty y
87−deriving instance DecidablyEmpty (f (g p)) => DecidablyEmpty ((f :.: g) p)
88−#endif
89−
90−instance (DecidablyEmpty a, DecidablyEmpty b) => DecidablyEmpty (a, b) where
91− isEmpty (a, b) = isEmpty a && isEmpty b
92−instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c) => DecidablyEmpty (a, b, c) where
93− isEmpty (a, b, c) = isEmpty a && isEmpty b && isEmpty c
94−instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d) => DecidablyEmpty (a, b, c, d) where
95− isEmpty (a, b, c, d) = isEmpty a && isEmpty b && isEmpty c && isEmpty d
96−instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d, DecidablyEmpty e) => DecidablyEmpty (a, b, c, d, e) where
97− isEmpty (a, b, c, d, e) = isEmpty a && isEmpty b && isEmpty c && isEmpty d && isEmpty e
98−
99−-- containers
100−
101−instance DecidablyEmpty IntSet.IntSet where
102− isEmpty = IntSet.null
103−instance DecidablyEmpty (IntMap.IntMap v) where
104− isEmpty = IntMap.null
105−instance Ord k => DecidablyEmpty (Map.Map k v) where
106− isEmpty = Map.null
107−instance DecidablyEmpty (Seq.Seq v) where
108− isEmpty = Seq.null
109−instance Ord k => DecidablyEmpty (Set.Set k) where
110− isEmpty = Set.null
111−
112−-- dependent-map
113−
114−instance GCompare k => DecidablyEmpty (DMap.DMap k v) where
115− isEmpty = DMap.null
src/Data/Patch.hs+28−25
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE StandaloneDeriving #-}
2{-# LANGUAGE StandaloneDeriving #-}
4{-# LANGUAGE TypeFamilies #-}
3{-# LANGUAGE TypeFamilies #-}
@@ -13,32 +12,24 @@ module Data.Patch
13 , module X
12 , module X
14 ) where
13 ) where
15
14
16−import Control.Applicative
17−import Data.Functor.Const (Const (..))
18−import Data.Functor.Identity
19−import Data.Map.Monoidal (MonoidalMap)
20−import Data.Proxy
21−#if !MIN_VERSION_base(4,11,0)
22−import Data.Semigroup (Semigroup (..))
23−#endif
24−import GHC.Generics
25−
26−import Data.Semigroup.Additive as X
27import Data.Patch.Class as X
15import Data.Patch.Class as X
28import Data.Patch.DMap as X hiding (getDeletions)
16import Data.Patch.DMap as X hiding (getDeletions)
29−import Data.Patch.DMapWithMove as X
30− ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
17+import Data.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove,
31− , patchDMapWithMoveToPatchMapWithMoveWith
18+ patchDMapWithMoveToPatchMapWithMoveWith,
32− , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
19+ traversePatchDMapWithMoveWithKey, unPatchDMapWithMove,
33− , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
20+ unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith)
34− )
35import Data.Patch.IntMap as X hiding (getDeletions)
21import Data.Patch.IntMap as X hiding (getDeletions)
36import Data.Patch.Map as X
22import Data.Patch.Map as X
37−import Data.Patch.MapWithMove as X
38− ( PatchMapWithMove, patchMapWithMoveNewElements
23+import Data.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
39− , patchMapWithMoveNewElementsMap, unPatchMapWithMove
24+ patchMapWithMoveNewElementsMap, unPatchMapWithMove,
40− , unsafePatchMapWithMove
25+ unsafePatchMapWithMove)
41− )
26+import Data.Map.Monoidal (MonoidalMap)
27+import Data.Semigroup (Semigroup (..), (<>))
28+import GHC.Generics
29+import Data.Functor.Identity
30+import Data.Functor.Const
31+import Data.Proxy
32+import Control.Applicative
42
33
43-- | A 'Group' is a 'Monoid' where every element has an inverse.
34-- | A 'Group' is a 'Monoid' where every element has an inverse.
44class (Semigroup q, Monoid q) => Group q where
35class (Semigroup q, Monoid q) => Group q where
@@ -46,6 +37,9 @@ class (Semigroup q, Monoid q) => Group q where
46 (~~) :: q -> q -> q
37 (~~) :: q -> q -> q
47 r ~~ s = r <> negateG s
38 r ~~ s = r <> negateG s
48
39
40+-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
41+class Semigroup q => Additive q where
42+
49-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
43-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
50newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
44newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
51
45
@@ -56,15 +50,19 @@ instance Additive p => Patch (AdditivePatch p) where
56instance (Ord k, Group q) => Group (MonoidalMap k q) where
50instance (Ord k, Group q) => Group (MonoidalMap k q) where
57 negateG = fmap negateG
51 negateG = fmap negateG
58
52
53+instance (Ord k, Additive q) => Additive (MonoidalMap k q)
54+
59-- | Trivial group.
55-- | Trivial group.
60instance Group () where
56instance Group () where
61 negateG _ = ()
57 negateG _ = ()
62 _ ~~ _ = ()
58 _ ~~ _ = ()
59+instance Additive ()
63
60
64-- | Product group. A Pair of groups gives rise to a group
61-- | Product group. A Pair of groups gives rise to a group
65instance (Group a, Group b) => Group (a, b) where
62instance (Group a, Group b) => Group (a, b) where
66 negateG (a, b) = (negateG a, negateG b)
63 negateG (a, b) = (negateG a, negateG b)
67 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
64 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
65+instance (Additive a, Additive b) => Additive (a, b)
68
66
69-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
67-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
70-- Base does not define Monoid (Compose f g a) so this is the best we can
68-- Base does not define Monoid (Compose f g a) so this is the best we can
@@ -72,24 +70,29 @@ instance (Group a, Group b) => Group (a, b) where
72instance Group (f (g a)) => Group ((f :.: g) a) where
70instance Group (f (g a)) => Group ((f :.: g) a) where
73 negateG (Comp1 xs) = Comp1 (negateG xs)
71 negateG (Comp1 xs) = Comp1 (negateG xs)
74 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
72 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
73+instance Additive (f (g a)) => Additive ((f :.: g) a)
75
74
76-- | Product of groups, Functor style.
75-- | Product of groups, Functor style.
77instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
76instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
78 negateG (a :*: b) = negateG a :*: negateG b
77 negateG (a :*: b) = negateG a :*: negateG b
79 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
78 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
79+instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
80
80
81-- | Trivial group, Functor style
81-- | Trivial group, Functor style
82instance Group (Proxy x) where
82instance Group (Proxy x) where
83 negateG _ = Proxy
83 negateG _ = Proxy
84 _ ~~ _ = Proxy
84 _ ~~ _ = Proxy
85+instance Additive (Proxy x)
85
86
86-- | Const lifts groups into a functor.
87-- | Const lifts groups into a functor.
87deriving instance Group a => Group (Const a x)
88deriving instance Group a => Group (Const a x)
88−
89+instance Additive a => Additive (Const a x)
89−-- | Identity lifts groups pointwise (at only one point)
90+-- | Ideitnty lifts groups pointwise (at only one point)
90deriving instance Group a => Group (Identity a)
91deriving instance Group a => Group (Identity a)
92+instance Additive a => Additive (Identity a)
91
93
92-- | Functions lift groups pointwise.
94-- | Functions lift groups pointwise.
93instance Group b => Group (a -> b) where
95instance Group b => Group (a -> b) where
94 negateG f = negateG . f
96 negateG f = negateG . f
95 (~~) = liftA2 (~~)
97 (~~) = liftA2 (~~)
98+instance Additive b => Additive (a -> b)
src/Data/Patch/Class.hs+1−12
@@ -1,23 +1,17 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE TypeFamilies #-}
1{-# LANGUAGE TypeFamilies #-}
4-- | The interface for types which represent changes made to other types
2-- | The interface for types which represent changes made to other types
5module Data.Patch.Class where
3module Data.Patch.Class where
6
4
7import Data.Functor.Identity
5import Data.Functor.Identity
8−import Data.Kind (Type)
9import Data.Maybe
6import Data.Maybe
10−#if !MIN_VERSION_base(4,11,0)
11import Data.Semigroup (Semigroup(..))
7import Data.Semigroup (Semigroup(..))
12−#endif
13−import Data.Proxy
14
8
15-- | A 'Patch' type represents a kind of change made to a datastructure.
9-- | A 'Patch' type represents a kind of change made to a datastructure.
16--
10--
17-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
11-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
18-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
12-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
19class Patch p where
13class Patch p where
20− type PatchTarget p :: Type
14+ type PatchTarget p :: *
21 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
15 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
22 -- 'Nothing'.
16 -- 'Nothing'.
23 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
17 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -31,11 +25,6 @@ instance Patch (Identity a) where
31 type PatchTarget (Identity a) = a
25 type PatchTarget (Identity a) = a
32 apply (Identity a) _ = Just a
26 apply (Identity a) _ = Just a
33
27
34−-- | 'Proxy' can be used as a 'Patch' that does nothing.
35−instance forall (a :: Type). Patch (Proxy a) where
36− type PatchTarget (Proxy a) = a
37− apply ~Proxy _ = Nothing
38−
39-- | Like '(.)', but composes functions that return patches rather than
28-- | Like '(.)', but composes functions that return patches rather than
40-- functions that return new values. The Semigroup instance for patches must
29-- functions that return new values. The Semigroup instance for patches must
41-- apply patches right-to-left, like '(.)'.
30-- apply patches right-to-left, like '(.)'.
src/Data/Patch/DMap.hs+1−12
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE PolyKinds #-}
3{-# LANGUAGE PolyKinds #-}
@@ -6,7 +5,6 @@
6{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE StandaloneDeriving #-}
6{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
9−
10-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
8-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
11module Data.Patch.DMap where
9module Data.Patch.DMap where
12
10
@@ -14,18 +12,13 @@ import Data.Patch.Class
14import Data.Patch.IntMap
12import Data.Patch.IntMap
15import Data.Patch.Map
13import Data.Patch.Map
16
14
17−import Data.Dependent.Map (DMap)
15+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
18−import Data.Dependent.Sum (DSum (..))
19−import Data.GADT.Compare (GCompare (..))
20import qualified Data.Dependent.Map as DMap
16import qualified Data.Dependent.Map as DMap
21import Data.Functor.Constant
17import Data.Functor.Constant
22import Data.Functor.Misc
18import Data.Functor.Misc
23import qualified Data.IntMap as IntMap
19import qualified Data.IntMap as IntMap
24import qualified Data.Map as Map
20import qualified Data.Map as Map
25−import Data.Monoid.DecidablyEmpty
26−#if !MIN_VERSION_base(4,11,0)
27import Data.Semigroup (Semigroup (..))
21import Data.Semigroup (Semigroup (..))
28−#endif
29import Data.Some (Some)
22import Data.Some (Some)
30
23
31-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
24-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
@@ -37,10 +30,6 @@ deriving instance GCompare k => Semigroup (PatchDMap k v)
37
30
38deriving instance GCompare k => Monoid (PatchDMap k v)
31deriving instance GCompare k => Monoid (PatchDMap k v)
39
32
40−-- It won't let me derive for some reason
41−instance GCompare k => DecidablyEmpty (PatchDMap k v) where
42− isEmpty (PatchDMap m) = DMap.null m
43−
44-- | Apply the insertions or deletions to a given 'DMap'.
33-- | Apply the insertions or deletions to a given 'DMap'.
45instance GCompare k => Patch (PatchDMap k v) where
34instance GCompare k => Patch (PatchDMap k v) where
46 type PatchTarget (PatchDMap k v) = DMap k v
35 type PatchTarget (PatchDMap k v) = DMap k v
src/Data/Patch/DMapWithMove.hs+8−16
@@ -20,21 +20,17 @@ import Data.Patch.MapWithMove (PatchMapWithMove (..))
20import qualified Data.Patch.MapWithMove as MapWithMove
20import qualified Data.Patch.MapWithMove as MapWithMove
21
21
22import Data.Constraint.Extras
22import Data.Constraint.Extras
23−import Data.Dependent.Map (DMap)
23+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
24−import Data.Dependent.Sum (DSum (..))
25import qualified Data.Dependent.Map as DMap
24import qualified Data.Dependent.Map as DMap
26import Data.Functor.Constant
25import Data.Functor.Constant
27import Data.Functor.Misc
26import Data.Functor.Misc
28import Data.Functor.Product
27import Data.Functor.Product
29−import Data.GADT.Compare (GEq (..), GCompare (..))
28+import Data.GADT.Compare (GEq (..))
30import Data.GADT.Show (GShow, gshow)
29import Data.GADT.Show (GShow, gshow)
31import qualified Data.Map as Map
30import qualified Data.Map as Map
32import Data.Maybe
31import Data.Maybe
33−import Data.Monoid.DecidablyEmpty
34−#if !MIN_VERSION_base(4,11,0)
35−import Data.Semigroup (Semigroup (..))
32+import Data.Semigroup (Semigroup (..), (<>))
36−#endif
37−import Data.Some (Some, mkSome)
33+import Data.Some (Some(Some))
38import Data.These
34import Data.These
39
35
40-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
36-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
@@ -46,10 +42,6 @@ import Data.These
46-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
42-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
47newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
43newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
48
44
49−-- It won't let me derive for some reason
50−instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
51− isEmpty (PatchDMapWithMove m) = DMap.null m
52−
53-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
45-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
54-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
46-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
55data NodeInfo k v a = NodeInfo
47data NodeInfo k v a = NodeInfo
@@ -220,8 +212,8 @@ moveDMapKey src dst = case src `geq` dst of
220-- @
212-- @
221-- let aMay = DMap.lookup a dmap
213-- let aMay = DMap.lookup a dmap
222-- bMay = DMap.lookup b dmap
214-- bMay = DMap.lookup b dmap
223−-- in maybe id (DMap.insert a) (bMay <> aMay)
215+-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
224−-- . maybe id (DMap.insert b) (aMay <> bMay)
216+-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
225-- . DMap.delete a . DMap.delete b $ dmap
217-- . DMap.delete a . DMap.delete b $ dmap
226-- @
218-- @
227swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
219swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
@@ -318,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
318 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
319 From_Insert v -> MapWithMove.From_Insert $ f v
311 From_Insert v -> MapWithMove.From_Insert $ f v
320 From_Delete -> MapWithMove.From_Delete
312 From_Delete -> MapWithMove.From_Delete
321− From_Move k -> MapWithMove.From_Move $ mkSome k
313+ From_Move k -> MapWithMove.From_Move $ Some k
322− , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
323 }
315 }
324
316
325-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
317-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
src/Data/Patch/IntMap.hs+12−29
@@ -1,41 +1,22 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE DeriveFoldable #-}
2+{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE DeriveTraversable #-}
3−{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE StandaloneDeriving #-}
7−{-# LANGUAGE TemplateHaskell #-}
8{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
9−
10-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
6-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
11-- insert/update or delete of associations.
7-- insert/update or delete of associations.
12module Data.Patch.IntMap where
8module Data.Patch.IntMap where
13
9
14−import Control.Lens
15import Data.IntMap.Strict (IntMap)
10import Data.IntMap.Strict (IntMap)
16import qualified Data.IntMap.Strict as IntMap
11import qualified Data.IntMap.Strict as IntMap
17import Data.Maybe
12import Data.Maybe
18−import Data.Monoid.DecidablyEmpty
13+import Data.Semigroup
19−#if !MIN_VERSION_base(4,11,0)
20−import Data.Semigroup (Semigroup (..))
21−#endif
22import Data.Patch.Class
14import Data.Patch.Class
23
15
24-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
16-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
25-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
17-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
26-- and @Nothing@ means delete.
18-- and @Nothing@ means delete.
27−newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
19+newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
28− deriving ( Show, Read, Eq, Ord
29− , Functor, Foldable, Traversable
30− , Monoid, DecidablyEmpty
31− )
32−
33−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
34−-- If the same key is modified by both patches, the one on the left will take
35−-- precedence.
36−deriving instance Semigroup (PatchIntMap v)
37−
38−makeWrapped ''PatchIntMap
39
20
40-- | Apply the insertions or deletions to a given 'IntMap'.
21-- | Apply the insertions or deletions to a given 'IntMap'.
41instance Patch (PatchIntMap a) where
22instance Patch (PatchIntMap a) where
@@ -45,11 +26,13 @@ instance Patch (PatchIntMap a) where
45 adds = IntMap.mapMaybe id p
26 adds = IntMap.mapMaybe id p
46 in IntMap.union adds $ v `IntMap.difference` removes
27 in IntMap.union adds $ v `IntMap.difference` removes
47
28
48−instance FunctorWithIndex Int PatchIntMap
29+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
49−instance FoldableWithIndex Int PatchIntMap
30+-- If the same key is modified by both patches, the one on the left will take
31+-- precedence.
50−instance TraversableWithIndex Int PatchIntMap where
32+instance Semigroup (PatchIntMap v) where
51− itraverse = itraversed . Indexed
33+ PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
52− itraversed = _Wrapped .> itraversed <. traversed
34+ -- PatchMap is idempotent, so stimes n is id for every n
35+ stimes = stimesIdempotentMonoid
53
36
54-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
37-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
55-- (that is, all inserts/updates), producing a @PatchIntMap b@.
38-- (that is, all inserts/updates), producing a @PatchIntMap b@.
@@ -59,7 +42,7 @@ mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k m
59-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
42-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
60-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
43-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
61traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
44traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
62−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
45+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
63
46
64-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
47-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
65patchIntMapNewElements :: PatchIntMap a -> [a]
48patchIntMapNewElements :: PatchIntMap a -> [a]
src/Data/Patch/Map.hs+19−36
@@ -1,48 +1,21 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveTraversable #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE StandaloneDeriving #-}
8−{-# LANGUAGE TemplateHaskell #-}
9{-# LANGUAGE TypeFamilies #-}
2{-# LANGUAGE TypeFamilies #-}
10−
11-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
3-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
12-- and deletions
4-- and deletions
13module Data.Patch.Map where
5module Data.Patch.Map where
14
6
15import Data.Patch.Class
7import Data.Patch.Class
16
8
17−import Control.Lens
18import Data.Map (Map)
9import Data.Map (Map)
19import qualified Data.Map as Map
10import qualified Data.Map as Map
20import Data.Maybe
11import Data.Maybe
21−import Data.Monoid.DecidablyEmpty
12+import Data.Semigroup
22−import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
23
13
24-- | A set of changes to a 'Map'. Any element may be inserted/updated or
14-- | A set of changes to a 'Map'. Any element may be inserted/updated or
25-- deleted. Insertions are represented as values wrapped in 'Just', while
15-- deleted. Insertions are represented as values wrapped in 'Just', while
26-- deletions are represented as 'Nothing's
16-- deletions are represented as 'Nothing's
27newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
17newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
28− deriving ( Show, Read, Eq, Ord
18+ deriving (Show, Read, Eq, Ord)
29− , Foldable, Traversable
30− , DecidablyEmpty
31− )
32−
33−-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
34−-- Deletions are unaffected.
35−deriving instance Functor (PatchMap k)
36−-- | The empty 'PatchMap' contains no insertions or deletions
37−deriving instance Ord k => Monoid (PatchMap k v)
38−
39−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
40−-- If the same key is modified by both patches, the one on the left will take
41−-- precedence.
42−instance Ord k => Semigroup (PatchMap k v) where
43− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
44− -- PatchMap is idempotent, so stimes n is id for every n
45− stimes = stimesIdempotentMonoid
46
19
47-- | Apply the insertions or deletions to a given 'Map'.
20-- | Apply the insertions or deletions to a given 'Map'.
48instance Ord k => Patch (PatchMap k v) where
21instance Ord k => Patch (PatchMap k v) where
@@ -55,11 +28,23 @@ instance Ord k => Patch (PatchMap k v) where
55 Nothing -> Just ()
28 Nothing -> Just ()
56 Just _ -> Nothing
29 Just _ -> Nothing
57
30
31+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
32+-- If the same key is modified by both patches, the one on the left will take
33+-- precedence.
58−instance FunctorWithIndex k (PatchMap k)
34+instance Ord k => Semigroup (PatchMap k v) where
59−instance FoldableWithIndex k (PatchMap k)
35+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
36+ -- PatchMap is idempotent, so stimes n is id for every n
37+ stimes = stimesIdempotentMonoid
38+
39+-- | The empty 'PatchMap' contains no insertions or deletions
60−instance TraversableWithIndex k (PatchMap k) where
40+instance Ord k => Monoid (PatchMap k v) where
61− itraverse = itraversed . Indexed
41+ mempty = PatchMap mempty
62− itraversed = _Wrapped .> itraversed <. traversed
42+ mappend = (<>)
43+
44+-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
45+-- Deletions are unaffected.
46+instance Functor (PatchMap k) where
47+ fmap f = PatchMap . fmap (fmap f) . unPatchMap
63
48
64-- | Returns all the new elements that will be added to the 'Map'
49-- | Returns all the new elements that will be added to the 'Map'
65patchMapNewElements :: PatchMap k v -> [v]
50patchMapNewElements :: PatchMap k v -> [v]
@@ -68,5 +53,3 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
68-- | Returns all the new elements that will be added to the 'Map'
53-- | Returns all the new elements that will be added to the 'Map'
69patchMapNewElementsMap :: PatchMap k v -> Map k v
54patchMapNewElementsMap :: PatchMap k v -> Map k v
70patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
55patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71−
72−makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+182−224
@@ -1,151 +1,82 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE DeriveFoldable #-}
2+{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE PatternGuards #-}
6{-# LANGUAGE PatternGuards #-}
9−{-# LANGUAGE PatternSynonyms #-}
10{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE StandaloneDeriving #-}
12−{-# LANGUAGE TemplateHaskell #-}
13−{-# LANGUAGE TypeApplications #-}
14{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE ViewPatterns #-}
16−
17-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
9-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
18-- another
10-- another
19−module Data.Patch.MapWithMove
11+module Data.Patch.MapWithMove where
20− ( PatchMapWithMove
21− ( PatchMapWithMove
22− , unPatchMapWithMove
23− , ..
24− )
25− , patchMapWithMove
26− , patchMapWithMoveInsertAll
27− , insertMapKey
28− , moveMapKey
29− , swapMapKey
30− , deleteMapKey
31− , unsafePatchMapWithMove
32− , patchMapWithMoveNewElements
33− , patchMapWithMoveNewElementsMap
34− , patchThatSortsMapWith
35− , patchThatChangesAndSortsMapWith
36− , patchThatChangesMap
37−
38− -- * Node Info
39− , NodeInfo
40− ( NodeInfo
41− , _nodeInfo_to
42− , _nodeInfo_from
43− , ..
44− )
45− , bitraverseNodeInfo
46− , nodeInfoMapFrom
47− , nodeInfoMapMFrom
48− , nodeInfoSetTo
49−
50− -- * From
51− , From
52− ( From_Insert
53− , From_Delete
54− , From_Move
55− , ..
56− )
57− , bitraverseFrom
58
12
59− -- * To
60− , To
61− ) where
62−
63−import Data.Coerce
64−import Data.Kind (Type)
65import Data.Patch.Class
13import Data.Patch.Class
66−import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67−import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
68
14
69−import Control.Lens
15+import Control.Arrow
16+import Control.Monad.Trans.State
17+import Data.Foldable
18+import Data.Function
70import Data.List
19import Data.List
71import Data.Map (Map)
20import Data.Map (Map)
72import qualified Data.Map as Map
21import qualified Data.Map as Map
73−import Data.Proxy
22+import Data.Maybe
74−#if !MIN_VERSION_base(4,11,0)
75−import Data.Semigroup (Semigroup (..))
23+import Data.Semigroup (Semigroup (..), (<>))
76−#endif
24+import qualified Data.Set as Set
77−import Data.Traversable (foldMapDefault)
25+import Data.These (These(..))
26+import Data.Tuple
78
27
79−-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
28+-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
80-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
29-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
81-- and vice versa. There should never be any unpaired From/To keys.
30-- and vice versa. There should never be any unpaired From/To keys.
82−newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
31+newtype PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
83− { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
84− unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
85− }
86− deriving ( Show, Read, Eq, Ord
87−-- Haddock cannot handle documentation here before GHC 8.6
88− ,
89−#if __GLASGOW_HASKELL__ >= 806
90− -- | Compose patches having the same effect as applying the
91− -- patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p .
92− -- 'applyAlways' q@
93−#endif
94− Semigroup
95− , Monoid
96− )
97−
98−pattern Coerce :: Coercible a b => a -> b
99−pattern Coerce x <- (coerce -> x)
100− where Coerce x = coerce x
101
32
102−{-# COMPLETE PatchMapWithMove #-}
33+-- | Holds the information about each key: where its new value should come from,
103−pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
34+-- and where its old value should go to
104−-- | Extract the representation of the 'PatchMapWithMove' as a map of
35+data NodeInfo k v = NodeInfo
105−-- 'NodeInfo'.
36+ { _nodeInfo_from :: !(From k v)
106−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
37+ -- ^ Where do we get the new value for this key?
107−pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
38+ , _nodeInfo_to :: !(To k)
108−
39+ -- ^ If the old value is being kept (i.e. moved rather than deleted or
109−_PatchMapWithMove
40+ -- replaced), where is it going?
110− :: Iso
41+ }
111− (PatchMapWithMove k0 v0)
42+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
112− (PatchMapWithMove k1 v1)
113− (Map k0 (NodeInfo k0 v0))
114− (Map k1 (NodeInfo k1 v1))
115−_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
116−
117−instance Functor (PatchMapWithMove k) where
118− fmap f = runIdentity . traverse (Identity . f)
119−
120−instance Foldable (PatchMapWithMove k) where
121− foldMap = foldMapDefault
122
43
123−instance Traversable (PatchMapWithMove k) where
44+-- | Describe how a key's new value should be produced
124− traverse =
45+data From k v
125− _PatchMapWithMove .
46+ = From_Insert v -- ^ Insert the given value here
126− traverse .
47+ | From_Delete -- ^ Delete the existing value, if any, from here
127− traverse
48+ | From_Move !k -- ^ Move the value here from the given key
49+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
128
50
129−instance FunctorWithIndex k (PatchMapWithMove k)
51+-- | Describe where a key's old value will go. If this is 'Just', that means
130−instance FoldableWithIndex k (PatchMapWithMove k)
52+-- the key's old value will be moved to the given other key; if it is 'Nothing',
131−instance TraversableWithIndex k (PatchMapWithMove k) where
53+-- that means it will be deleted.
132− itraverse = itraversed . Indexed
54+type To = Maybe
133− itraversed =
134− _PatchMapWithMove .>
135− itraversed <.
136− traverse
137
55
138-- | Create a 'PatchMapWithMove', validating it
56-- | Create a 'PatchMapWithMove', validating it
139patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
57patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
140−patchMapWithMove = fmap PatchMapWithMove' . PM.patchMapWithPatchingMove . coerce
58+patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
59+ where valid = forwardLinks == backwardLinks
60+ forwardLinks = Map.mapMaybe _nodeInfo_to m
61+ backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, v) ->
62+ case _nodeInfo_from v of
63+ From_Move from -> Just (from, to)
64+ _ -> Nothing
141
65
142-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
66-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
143patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
67patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
144−patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
68+patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
69+ { _nodeInfo_from = From_Insert v
70+ , _nodeInfo_to = Nothing
71+ }
72+
73+-- | Extract the internal representation of the 'PatchMapWithMove'
74+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
75+unPatchMapWithMove (PatchMapWithMove p) = p
145
76
146-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
77-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
147insertMapKey :: k -> v -> PatchMapWithMove k v
78insertMapKey :: k -> v -> PatchMapWithMove k v
148−insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
79+insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
149
80
150-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
81-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
151--
82--
@@ -153,53 +84,84 @@ insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
153-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
84-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
154-- @
85-- @
155moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
86moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
156−moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
87+moveMapKey src dst
88+ | src == dst = mempty
89+ | otherwise =
90+ PatchMapWithMove $ Map.fromList
91+ [ (dst, NodeInfo (From_Move src) Nothing)
92+ , (src, NodeInfo From_Delete (Just dst))
93+ ]
157
94
158-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
95-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
159--
96--
160-- @
97-- @
161-- let aMay = Map.lookup a map
98-- let aMay = Map.lookup a map
162-- bMay = Map.lookup b map
99-- bMay = Map.lookup b map
163−-- in maybe id (Map.insert a) (bMay <> aMay)
100+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
164−-- . maybe id (Map.insert b) (aMay <> bMay)
101+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
165-- . Map.delete a . Map.delete b $ map
102-- . Map.delete a . Map.delete b $ map
166-- @
103-- @
167swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
104swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
168−swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
105+swapMapKey src dst
106+ | src == dst = mempty
107+ | otherwise =
108+ PatchMapWithMove $ Map.fromList
109+ [ (dst, NodeInfo (From_Move src) (Just src))
110+ , (src, NodeInfo (From_Move dst) (Just dst))
111+ ]
169
112
170-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
113-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
171deleteMapKey :: k -> PatchMapWithMove k v
114deleteMapKey :: k -> PatchMapWithMove k v
172−deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
115+deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
173
116
174-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
117-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
175--
118--
176-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
119-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
177unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
120unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
178−unsafePatchMapWithMove = coerce PM.unsafePatchMapWithPatchingMove
121+unsafePatchMapWithMove = PatchMapWithMove
179
122
180-- | Apply the insertions, deletions, and moves to a given 'Map'
123-- | Apply the insertions, deletions, and moves to a given 'Map'
181instance Ord k => Patch (PatchMapWithMove k v) where
124instance Ord k => Patch (PatchMapWithMove k v) where
182 type PatchTarget (PatchMapWithMove k v) = Map k v
125 type PatchTarget (PatchMapWithMove k v) = Map k v
183− apply (PatchMapWithMove' p) = apply p
126+ apply (PatchMapWithMove p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
127+ where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
128+ From_Insert v -> Just v
129+ From_Move k -> Map.lookup k old
130+ From_Delete -> Nothing
131+ deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
132+ From_Delete -> Just ()
133+ _ -> Nothing
184
134
185-- | Returns all the new elements that will be added to the 'Map'.
135-- | Returns all the new elements that will be added to the 'Map'.
186patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
136patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
187−patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
137+patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
188
138
189-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
139-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
190patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
140patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
191−patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
141+patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
142+ where f ni = case _nodeInfo_from ni of
143+ From_Insert v -> Just v
144+ From_Move _ -> Nothing
145+ From_Delete -> Nothing
192
146
193-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
147-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
194-- its values using the given ordering function. The set keys of the 'Map' is
148-- its values using the given ordering function. The set keys of the 'Map' is
195-- not changed.
149-- not changed.
196patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
150patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
197−patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
151+patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
152+ where unsorted = Map.toList m
153+ sorted = sortBy (cmp `on` snd) unsorted
154+ f (to, _) (from, _) = if to == from then Nothing else
155+ Just (from, to)
156+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
157+ g (to, _) (from, _) = if to == from then Nothing else
158+ let Just movingTo = Map.lookup to reverseMapping
159+ in Just (to, NodeInfo (From_Move from) $ Just movingTo)
198
160
199-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
161-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
200-- will produce a 'Map' with the same values as the second 'Map' but with the
162-- will produce a 'Map' with the same values as the second 'Map' but with the
201-- values sorted with the given ordering function.
163-- values sorted with the given ordering function.
202−patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
164+patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
203patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
165patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
204 where newList = Map.toList newByIndexUnsorted
166 where newList = Map.toList newByIndexUnsorted
205 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
167 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
@@ -207,106 +169,102 @@ patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatCha
207-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
169-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
208-- will produce the second 'Map'.
170-- will produce the second 'Map'.
209patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
171patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
210−patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
172+patchThatChangesMap oldByIndex newByIndex = patch
211− PM.patchThatChangesMap oldByIndex newByIndex
173+ where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
212−
174+ (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
213−--
175+ let f k v = do
214−-- NodeInfo
176+ remainingValues <- get
215−--
177+ let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
216−
178+ then Map.delete v remainingValues
217−-- | Holds the information about each key: where its new value should come from,
179+ else Map.insert v remainingKeys remainingValues
218−-- and where its old value should go to
180+ case Map.lookup v remainingValues of
219−newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
181+ Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
220−
182+ Just fromKs ->
221−deriving instance (Show k, Show p) => Show (NodeInfo k p)
183+ if k `Set.member` fromKs
222−deriving instance (Read k, Read p) => Read (NodeInfo k p)
184+ then do
223−deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
185+ putRemainingKeys $ Set.delete k fromKs
224−deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
186+ return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
225−
187+ else do
226−{-# COMPLETE NodeInfo #-}
188+ (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
227−pattern NodeInfo :: To k -> From k v -> NodeInfo k v
189+ putRemainingKeys remainingKeys
228−_nodeInfo_to :: NodeInfo k v -> To k
190+ return $ NodeInfo (From_Move fromK) $ Just undefined
229−_nodeInfo_from :: NodeInfo k v -> From k v
191+ Map.traverseWithKey f newByIndex
230−pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
192+ unusedOldKeys = fold unusedValuesByValue
231− PM.NodeInfo
193+ pointlessMove k = \case
232− { PM._nodeInfo_to = _nodeInfo_to
194+ From_Move k' | k == k' -> True
233− , PM._nodeInfo_from = Coerce _nodeInfo_from
195+ _ -> False
234− }
196+ keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
235−
197+ then Just undefined
236−_NodeInfo
198+ else Nothing
237− :: Iso
199+ patch = unsafePatchMapWithMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
238− (NodeInfo k0 v0)
239− (NodeInfo k1 v1)
240− (PM.NodeInfo k0 (Proxy v0))
241− (PM.NodeInfo k1 (Proxy v1))
242−_NodeInfo = iso unNodeInfo' NodeInfo'
243−
244−instance Functor (NodeInfo k) where
245− fmap f = runIdentity . traverse (Identity . f)
246−
247−instance Foldable (NodeInfo k) where
248− foldMap = foldMapDefault
249−
250−instance Traversable (NodeInfo k) where
251− traverse = bitraverseNodeInfo pure
252−
253−bitraverseNodeInfo
254− :: Applicative f
255− => (k0 -> f k1)
256− -> (v0 -> f v1)
257− -> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
258−bitraverseNodeInfo fk fv = fmap NodeInfo'
259− . PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
260− . coerce
261
200
262-- | Change the 'From' value of a 'NodeInfo'
201-- | Change the 'From' value of a 'NodeInfo'
263nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
202nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
264−nodeInfoMapFrom f = coerce $ PM.nodeInfoMapFrom (unFrom' . f . From')
203+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
265
204
266-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
205-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
267-- 'Applicative', 'Monad', etc.) action to get the new value
206-- 'Applicative', 'Monad', etc.) action to get the new value
268−nodeInfoMapMFrom
269− :: Functor f
270− => (From k v -> f (From k v))
271− -> NodeInfo k v -> f (NodeInfo k v)
207+nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
272−nodeInfoMapMFrom f = fmap NodeInfo'
208+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
273− . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
274− . coerce
275
209
276-- | Set the 'To' field of a 'NodeInfo'
210-- | Set the 'To' field of a 'NodeInfo'
277nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
211nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
278−nodeInfoSetTo = coerce . PM.nodeInfoSetTo
212+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
279−
213+
280−--
214+-- |Helper data structure used for composing patches using the monoid instance.
281−-- From
215+data Fixup k v
282−--
216+ = Fixup_Delete
283−
217+ | Fixup_Update (These (From k v) (To k))
284−-- | Describe how a key's new value should be produced
218+
285−newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
219+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
286−
220+instance Ord k => Semigroup (PatchMapWithMove k v) where
287−{-# COMPLETE From_Insert, From_Delete, From_Move #-}
221+ PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
288−
222+ where
289−-- | Insert the given value here
223+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
290−pattern From_Insert :: v -> From k v
224+ h :: (k, (Maybe k, From k v)) -> [(k, Fixup k v)]
291−pattern From_Insert v = From' (PM.From_Insert v)
225+ h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
292−
226+ (Just toAfter, From_Move fromBefore)
293−-- | Delete the existing value, if any, from here
227+ | fromBefore == toAfter
294−pattern From_Delete :: From k v
228+ -> [(toAfter, Fixup_Delete)]
295−pattern From_Delete = From' PM.From_Delete
229+ | otherwise
296−
230+ -> [ (toAfter, Fixup_Update (This editBefore))
297−-- | Move the value here from the given key
231+ , (fromBefore, Fixup_Update (That mToAfter))
298−pattern From_Move :: k -> From k v
232+ ]
299−pattern From_Move k = From' (PM.From_Move k Proxy)
233+ (Nothing, From_Move fromBefore) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
300−
234+ (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
301−bitraverseFrom
235+ (Nothing, _) -> []
302− :: Applicative f
236+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
303− => (k0 -> f k1)
237+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
304− -> (v0 -> f v1)
238+ | This x <- a, That y <- b
305− -> From k0 v0 -> f (From k1 v1)
239+ = Fixup_Update $ These x y
306−bitraverseFrom fk fv = fmap From'
240+ | That y <- a, This x <- b
307− . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
241+ = Fixup_Update $ These x y
308− . coerce
242+ mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
309−
243+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
310−makeWrapped ''PatchMapWithMove
244+ combineNodeInfos _ nia nib = NodeInfo
311−makeWrapped ''NodeInfo
245+ { _nodeInfo_from = _nodeInfo_from nia
312−makeWrapped ''From
246+ , _nodeInfo_to = _nodeInfo_to nib
247+ }
248+ applyFixup _ ni = \case
249+ Fixup_Delete -> Nothing
250+ Fixup_Update u -> Just $ NodeInfo
251+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
252+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
253+ }
254+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
255+ getHere :: These a b -> Maybe a
256+ getHere = \case
257+ This a -> Just a
258+ These a _ -> Just a
259+ That _ -> Nothing
260+ getThere :: These a b -> Maybe b
261+ getThere = \case
262+ This _ -> Nothing
263+ These _ b -> Just b
264+ That b -> Just b
265+
266+--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
267+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
268+instance Ord k => Monoid (PatchMapWithMove k v) where
269+ mempty = PatchMapWithMove mempty
270+ mappend = (<>)
src/Data/Patch/MapWithPatchingMove.hsdeleted−432
@@ -1,432 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PatternGuards #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE StandaloneDeriving #-}
10−{-# LANGUAGE TemplateHaskell #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−
14−-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15−-- another
16−module Data.Patch.MapWithPatchingMove
17− ( PatchMapWithPatchingMove (..)
18− , patchMapWithPatchingMove
19− , patchMapWithPatchingMoveInsertAll
20− , insertMapKey
21− , moveMapKey
22− , swapMapKey
23− , deleteMapKey
24− , unsafePatchMapWithPatchingMove
25− , patchMapWithPatchingMoveNewElements
26− , patchMapWithPatchingMoveNewElementsMap
27− , patchThatSortsMapWith
28− , patchThatChangesAndSortsMapWith
29− , patchThatChangesMap
30−
31− -- * Node Info
32− , NodeInfo (..)
33− , bitraverseNodeInfo
34− , nodeInfoMapFrom
35− , nodeInfoMapMFrom
36− , nodeInfoSetTo
37−
38− -- * From
39− , From(..)
40− , bitraverseFrom
41−
42− -- * To
43− , To
44−
45− -- TODO internals module
46− , Fixup (..)
47− ) where
48−
49−import Data.Patch.Class
50−
51−import Control.Lens hiding (from, to)
52−import Control.Lens.TH (makeWrapped)
53−import Data.Align (align)
54−import Data.Foldable (toList)
55−import Data.Function
56−import Data.List
57−import Data.Map (Map)
58−import qualified Data.Map as Map
59−import Data.Maybe
60−#if !MIN_VERSION_base(4,11,0)
61−import Data.Semigroup (Semigroup (..))
62−#endif
63−import Data.Monoid.DecidablyEmpty
64−import Data.Set (Set)
65−import qualified Data.Set as Set
66−import Data.These (These (..))
67−
68−-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
69−-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
70−-- and vice versa. There should never be any unpaired From/To keys.
71−newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
72− { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
73− unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
74− }
75−
76−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
77−deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
78−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
79−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
80−
81−deriving instance ( Ord k
82−#if !MIN_VERSION_base(4,11,0)
83− , Semigroup p
84−#endif
85− , DecidablyEmpty p
86− , Patch p
87− ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
88−
89−-- | Create a 'PatchMapWithPatchingMove', validating it
90−patchMapWithPatchingMove
91− :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
92−patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
93− where valid = forwardLinks == backwardLinks
94− forwardLinks = Map.mapMaybe _nodeInfo_to m
95− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
96− case _nodeInfo_from p of
97− From_Move from _ -> Just (from, to)
98− _ -> Nothing
99−
100−-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
101−patchMapWithPatchingMoveInsertAll
102− :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
103−patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
104− { _nodeInfo_from = From_Insert v
105− , _nodeInfo_to = Nothing
106− }
107−
108−-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109−insertMapKey
110− :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
111−insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
112−
113−-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
114−--
115−-- @
116−-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
117−-- @
118−moveMapKey
119− :: ( DecidablyEmpty p
120−#if !MIN_VERSION_base(4,11,0)
121− , Semigroup p
122−#endif
123− , Patch p
124− )
125− => Ord k => k -> k -> PatchMapWithPatchingMove k p
126−moveMapKey src dst
127− | src == dst = mempty
128− | otherwise =
129− PatchMapWithPatchingMove $ Map.fromList
130− [ (dst, NodeInfo (From_Move src mempty) Nothing)
131− , (src, NodeInfo From_Delete (Just dst))
132− ]
133−
134−-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
135−--
136−-- @
137−-- let aMay = Map.lookup a map
138−-- bMay = Map.lookup b map
139−-- in maybe id (Map.insert a) (bMay <> aMay)
140−-- . maybe id (Map.insert b) (aMay <> bMay)
141−-- . Map.delete a . Map.delete b $ map
142−-- @
143−swapMapKey
144− :: ( DecidablyEmpty p
145−#if !MIN_VERSION_base(4,11,0)
146− , Semigroup p
147−#endif
148− , Patch p
149− )
150− => Ord k => k -> k -> PatchMapWithPatchingMove k p
151−swapMapKey src dst
152− | src == dst = mempty
153− | otherwise =
154− PatchMapWithPatchingMove $ Map.fromList
155− [ (dst, NodeInfo (From_Move src mempty) (Just src))
156− , (src, NodeInfo (From_Move dst mempty) (Just dst))
157− ]
158−
159−-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
160−-- the mapping, equivalent to 'Map.delete'.
161−deleteMapKey
162− :: k -> PatchMapWithPatchingMove k v
163−deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
164−
165−-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
166−--
167−-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
168−unsafePatchMapWithPatchingMove
169− :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
170−unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
171−
172−-- | Apply the insertions, deletions, and moves to a given 'Map'
173−instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
174− type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
175− -- TODO: return Nothing sometimes
176− -- Note: the strict application here is critical to ensuring that incremental
177− -- merges don't hold onto all their prerequisite events forever; can we make
178− -- this more robust?
179− apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
180− where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
181− From_Insert v -> Just v
182− From_Move k p -> applyAlways p <$> Map.lookup k old
183− From_Delete -> Nothing
184− deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
185− From_Delete -> Just ()
186− _ -> Nothing
187−
188−-- | Returns all the new elements that will be added to the 'Map'
189−patchMapWithPatchingMoveNewElements
190− :: PatchMapWithPatchingMove k p -> [PatchTarget p]
191−patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
192−
193−-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
194−patchMapWithPatchingMoveNewElementsMap
195− :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
196−patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
197− where f ni = case _nodeInfo_from ni of
198− From_Insert v -> Just v
199− From_Move _ _ -> Nothing
200− From_Delete -> Nothing
201−
202−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
203−-- its values using the given ordering function. The set keys of the 'Map' is
204−-- not changed.
205−patchThatSortsMapWith
206− :: (Ord k, Monoid p)
207− => (PatchTarget p -> PatchTarget p -> Ordering)
208− -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
209−patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
210− where unsorted = Map.toList m
211− sorted = sortBy (cmp `on` snd) unsorted
212− f (to, _) (from, _) = if to == from then Nothing else
213− Just (from, to)
214− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
215− g (to, _) (from, _) = if to == from then Nothing else
216− let Just movingTo = Map.lookup from reverseMapping
217− in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
218−
219−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
220−-- will produce a 'Map' with the same values as the second 'Map' but with the
221−-- values sorted with the given ordering function.
222−patchThatChangesAndSortsMapWith
223− :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
224− => (PatchTarget p -> PatchTarget p -> Ordering)
225− -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
226−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
227− where newList = Map.toList newByIndexUnsorted
228− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
229−
230−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
231−-- will produce the second 'Map'.
232−-- Note: this will never produce a patch on a value.
233−patchThatChangesMap
234− :: forall k p
235− . (Ord k, Ord (PatchTarget p), Monoid p)
236− => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
237−patchThatChangesMap oldByIndex newByIndex = patch
238− where invert :: Map k (PatchTarget p) -> Map (PatchTarget p) (Set k)
239− invert = Map.fromListWith (<>) . fmap (\(k, v) -> (v, Set.singleton k)) . Map.toList
240− -- In the places where we use unionDistinct, a non-distinct key indicates a bug in this function
241− unionDistinct :: forall k' v'. Ord k' => Map k' v' -> Map k' v' -> Map k' v'
242− unionDistinct = Map.unionWith (error "patchThatChangesMap: non-distinct keys")
243− unionPairDistinct :: (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k))
244− unionPairDistinct (oldFroms, oldTos) (newFroms, newTos) = (unionDistinct oldFroms newFroms, unionDistinct oldTos newTos)
245− -- Generate patch info for a single value
246− -- Keys that are found in both the old and new sets will not be patched
247− -- Keys that are found in only the old set will be moved to a new position if any are available; otherwise they will be deleted
248− -- Keys that are found in only the new set will be populated by moving an old key if any are available; otherwise they will be inserted
249− patchSingleValue :: PatchTarget p -> Set k -> Set k -> (Map k (From k p), Map k (To k))
250− patchSingleValue v oldKeys newKeys = foldl' unionPairDistinct mempty $ align (toList $ oldKeys `Set.difference` newKeys) (toList $ newKeys `Set.difference` oldKeys) <&> \case
251− This oldK -> (mempty, Map.singleton oldK Nothing) -- There's nowhere for this value to go, so we know we are deleting it
252− That newK -> (Map.singleton newK $ From_Insert v, mempty) -- There's nowhere fo this value to come from, so we know we are inserting it
253− These oldK newK -> (Map.singleton newK $ From_Move oldK mempty, Map.singleton oldK $ Just newK)
254− -- Run patchSingleValue on a These. Missing old or new sets are considered empty
255− patchSingleValueThese :: PatchTarget p -> These (Set k) (Set k) -> (Map k (From k p), Map k (To k))
256− patchSingleValueThese v = \case
257− This oldKeys -> patchSingleValue v oldKeys mempty
258− That newKeys -> patchSingleValue v mempty newKeys
259− These oldKeys newKeys -> patchSingleValue v oldKeys newKeys
260− -- Generate froms and tos for all values, then merge them together
261− (froms, tos) = foldl' unionPairDistinct mempty $ Map.mapWithKey patchSingleValueThese $ align (invert oldByIndex) (invert newByIndex)
262− patch = unsafePatchMapWithPatchingMove $ align froms tos <&> \case
263− This from -> NodeInfo from Nothing -- Since we don't have a 'to' record for this key, that must mean it isn't being moved anywhere, so it should be deleted.
264− That to -> NodeInfo From_Delete to -- Since we don't have a 'from' record for this key, it must be getting deleted
265− These from to -> NodeInfo from to
266−
267−--
268−-- NodeInfo
269−--
270−
271−-- | Holds the information about each key: where its new value should come from,
272−-- and where its old value should go to
273−data NodeInfo k p = NodeInfo
274− { _nodeInfo_from :: !(From k p)
275− -- ^ Where do we get the new value for this key?
276− , _nodeInfo_to :: !(To k)
277− -- ^ If the old value is being kept (i.e. moved rather than deleted or
278− -- replaced), where is it going?
279− }
280−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
281−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
282−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
283−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
284−
285−-- | Traverse the 'NodeInfo' over the key, patch, and patch target. Because of
286−-- the type families here, this doesn't it any bi- or tri-traversal class.
287−bitraverseNodeInfo
288− :: Applicative f
289− => (k0 -> f k1)
290− -> (p0 -> f p1)
291− -> (PatchTarget p0 -> f (PatchTarget p1))
292− -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
293−bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
294− <$> bitraverseFrom fk fp fpt from
295− <*> traverse fk to
296−
297−-- | Change the 'From' value of a 'NodeInfo'
298−nodeInfoMapFrom
299− :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
300−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
301−
302−-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
303−-- 'Applicative', 'Monad', etc.) action to get the new value
304−nodeInfoMapMFrom
305− :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
306−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
307−
308−-- | Set the 'To' field of a 'NodeInfo'
309−nodeInfoSetTo
310− :: To k -> NodeInfo k v -> NodeInfo k v
311−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
312−
313−--
314−-- From
315−--
316−
317−-- | Describe how a key's new value should be produced
318−data From k p
319− = From_Insert (PatchTarget p) -- ^ Insert the given value here
320− | From_Delete -- ^ Delete the existing value, if any, from here
321− | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
322−
323−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
324−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
325−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
326−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
327−
328−-- | Traverse the 'From' over the key, patch, and patch target. Because of
329−-- the type families here, this doesn't it any bi- or tri-traversal class.
330−bitraverseFrom
331− :: Applicative f
332− => (k0 -> f k1)
333− -> (p0 -> f p1)
334− -> (PatchTarget p0 -> f (PatchTarget p1))
335− -> From k0 p0 -> f (From k1 p1)
336−bitraverseFrom fk fp fpt = \case
337− From_Insert pt -> From_Insert <$> fpt pt
338− From_Delete -> pure From_Delete
339− From_Move k p -> From_Move <$> fk k <*> fp p
340−
341−--
342−-- To
343−--
344−
345−-- | Describe where a key's old value will go. If this is 'Just', that means
346−-- the key's old value will be moved to the given other key; if it is 'Nothing',
347−-- that means it will be deleted.
348−type To = Maybe
349−
350−--
351−-- Fixup
352−--
353−
354−-- | Helper data structure used for composing patches using the monoid instance.
355−data Fixup k v
356− = Fixup_Delete
357− | Fixup_Update (These (From k v) (To k))
358−
359−-- | Compose patches having the same effect as applying the patches in turn:
360−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
361−instance ( Ord k
362−#if !MIN_VERSION_base(4,11,0)
363− , Semigroup p
364−#endif
365− , DecidablyEmpty p
366− , Patch p
367− ) => Semigroup (PatchMapWithPatchingMove k p) where
368− PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
369− where
370− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
371− h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
372− h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
373− (Just toAfter, From_Move fromBefore p)
374− | fromBefore == toAfter && isEmpty p
375− -> [(toAfter, Fixup_Delete)]
376− | otherwise
377− -> [ (toAfter, Fixup_Update (This editBefore))
378− , (fromBefore, Fixup_Update (That mToAfter))
379− ]
380− (Nothing, From_Move fromBefore _) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
381− (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
382− (Nothing, _) -> []
383− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
384− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
385− | This x <- a, That y <- b
386− = Fixup_Update $ These x y
387− | That y <- a, This x <- b
388− = Fixup_Update $ These x y
389− mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
390− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
391− combineNodeInfos _ nia nib = NodeInfo
392− { _nodeInfo_from = _nodeInfo_from nia
393− , _nodeInfo_to = _nodeInfo_to nib
394− }
395− applyFixup _ ni = \case
396− Fixup_Delete -> Nothing
397− Fixup_Update u -> Just $ NodeInfo
398− { _nodeInfo_from = case _nodeInfo_from ni of
399− f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
400− Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
401− Just (From_Insert v) -> From_Insert $ applyAlways p' v
402− Just From_Delete -> From_Delete
403− Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
404− _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
405− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
406− }
407− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
408− getHere :: These a b -> Maybe a
409− getHere = \case
410− This a -> Just a
411− These a _ -> Just a
412− That _ -> Nothing
413− getThere :: These a b -> Maybe b
414− getThere = \case
415− This _ -> Nothing
416− These _ b -> Just b
417− That b -> Just b
418−
419−--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
420−-- | Compose patches having the same effect as applying the patches in turn:
421−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
422−instance ( Ord k
423−#if !MIN_VERSION_base(4,11,0)
424− , Semigroup p
425−#endif
426− , DecidablyEmpty p
427− , Patch p
428− ) => Monoid (PatchMapWithPatchingMove k p) where
429− mempty = PatchMapWithPatchingMove mempty
430− mappend = (<>)
431−
432−makeWrapped ''PatchMapWithPatchingMove
src/Data/Semigroup/Additive.hsdeleted−54
@@ -1,54 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE TypeOperators #-}
3−{-# LANGUAGE TypeFamilies #-}
4−-- |
5−-- Module:
6−-- Data.Semigroup.Additive
7−-- Description:
8−-- This module defines a class for commutative semigroups, until it is moved
9−-- to another library.
10−module Data.Semigroup.Additive
11− ( Additive
12− ) where
13−
14−import Data.Functor.Const (Const (..))
15−import Data.Functor.Identity
16−import Data.Proxy
17−#if !MIN_VERSION_base(4,12,0)
18−-- for :*: and :.: semigroup instances
19−import Data.Orphans ()
20−#endif
21−#if !MIN_VERSION_base(4,11,0)
22−import Data.Semigroup (Semigroup (..))
23−#endif
24−import GHC.Generics
25−
26−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
27−class Semigroup q => Additive q where
28−
29−-- | Trivial additive semigroup.
30−instance Additive ()
31−
32−-- | Product additive semigroup.
33−-- A Pair of additive semigroups gives rise to a additive semigroup
34−instance (Additive a, Additive b) => Additive (a, b)
35−
36−-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
37−-- Base does not define Monoid (Compose f g a) so this is the best we can
38−-- really do for functor composition.
39−instance Additive (f (g a)) => Additive ((f :.: g) a)
40−
41−-- | Product of additive semigroups, Functor style.
42−instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
43−
44−-- | Trivial additive semigroup, Functor style
45−instance Additive (Proxy x)
46−
47−-- | Const lifts additive semigroups into a functor.
48−instance Additive a => Additive (Const a x)
49−
50−-- | Identity lifts additive semigroups pointwise (at only one point)
51−instance Additive a => Additive (Identity a)
52−
53−-- | Functions lift additive semigroups pointwise.
54−instance Additive b => Additive (a -> b)
test/hlint.hs+1−2
@@ -1,7 +1,7 @@
1module Main where
1module Main where
2
2
3import Control.Monad
3import Control.Monad
4−import Language.Haskell.HLint (hlint)
4+import Language.Haskell.HLint3 (hlint)
5import System.Directory
5import System.Directory
6import System.Exit (exitFailure, exitSuccess)
6import System.Exit (exitFailure, exitSuccess)
7import System.FilePath
7import System.FilePath
@@ -23,7 +23,6 @@ main = do
23 , "--ignore=Reduce duplication"
23 , "--ignore=Reduce duplication"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
25 , "--ignore=Use tuple-section"
25 , "--ignore=Use tuple-section"
26− , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
27 ]
26 ]
28 recurseInto = and <$> sequence
27 recurseInto = and <$> sequence
29 [ fileType ==? Directory
28 [ fileType ==? Directory
test/tests.hsdeleted−43
@@ -1,43 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−module Main where
3−
4−import Test.HUnit (runTestTT, (~:), assertEqual, errors, failures, test)
5−import Data.Patch ( Patch(apply) )
6−import Data.Patch.MapWithMove ( patchThatChangesMap )
7−import Data.Map as Map ( Map, fromList, singleton )
8−import Hedgehog (checkParallel, discover, Property, property, forAll, PropertyT, (===))
9−import Hedgehog.Gen as Gen ( int )
10−import Hedgehog.Range as Range ( linear )
11−import Control.Monad (replicateM)
12−import System.Exit (exitFailure, exitSuccess)
13−import Data.Sequence as Seq ( foldMapWithIndex, replicateM )
14−
15−main :: IO ()
16−main = do
17− counts <- runTestTT $ test [
18− "Simple Move" ~: (do
19− let mapBefore = Map.fromList [(0,1)]
20− mapAfter = Map.fromList [(0,0),(1,1)]
21− patch = patchThatChangesMap mapBefore mapAfter
22− afterPatch = apply patch mapBefore
23− assertEqual "Patch creates the same Map" (Just mapAfter) afterPatch),
24− "Property Checks" ~: propertyChecks
25− ]
26− if errors counts + failures counts == 0 then exitSuccess else exitFailure
27−
28−propertyChecks :: IO Bool
29−propertyChecks = checkParallel $$(discover)
30−
31−prop_patchThatChangesMap :: Property
32−prop_patchThatChangesMap = property $ do
33− mapBefore <- makeRandomIntMap
34− mapAfter <- makeRandomIntMap
35− let patch = patchThatChangesMap mapBefore mapAfter
36− Just mapAfter === apply patch mapBefore
37−
38−makeRandomIntMap :: Monad m => PropertyT m (Map Int Int)
39−makeRandomIntMap = do
40− let genNum = Gen.int (Range.linear 0 100)
41− length <- forAll genNum
42− listOfNumbers <- forAll $ Seq.replicateM length genNum
43− pure $ Seq.foldMapWithIndex Map.singleton listOfNumbers