on develop help sign in
.github/workflows/haskell.ymlnew file+47
since splitsnapshots
unifiedsplit
@@ -0,0 +1,47 @@
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.ymldeleted−171
@@ -1,171 +0,0 @@
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 patch-platform, cabal install, stack, obelisk, etc.). If you're using patch-platform or obelisk, provide the git hash of your checkout.
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.
20
20
21## Submitting Changes
21## Submitting Changes
22
22
ChangeLog.md+44−6
@@ -2,22 +2,60 @@
2
2
3## Unreleased
3## Unreleased
4
4
5−* `PatchMapWithMove` supports moves with a patch.
5+* New dep of `data-orphans` for old GHC to get instances honestly instead of
6+ via `monoidal-containers`.
6
7
7−* Add `PatchDMapWithReset`
8+## 0.0.5.0 - 2021-12-17
8
9
9−* Add `Patchable`
10+* `Additive` now lives in `Data.Semigroup.Additive`, but is still reexported
11+ from `Data.Patch` for compatability.
10
12
11−## 0.0.1.0
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
12
50
13* Support older GHCs with `split-these` flag.
51* Support older GHCs with `split-these` flag.
14
52
15* Additional instances for the `Group` class for basic types.
53* Additional instances for the `Group` class for basic types.
16
54
17−## 0.0.0.1
55+## 0.0.0.1 - 2020-01-08
18
56
19* Remove unneeded dependencies
57* Remove unneeded dependencies
20
58
21−## 0.0.0.0
59+## 0.0.0.0 - 2020-01-08
22
60
23* Extract patching functionality from Reflex.
61* Extract patching functionality from Reflex.
README.md+6−4
@@ -1,8 +1,10 @@
1−## Patch
1+# patch
2
2
3−Infrastructure for writing patches which act on other types.
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)
4
4
5+Data structures for describing changes to other data structures.
6+
5−A `Patch` type represents a kind of change made to a datastructure.
7+A `Patch` type represents a kind of change made to a data structure.
6
8
7```haskell
9```haskell
8class Patch p where
10class Patch p where
@@ -12,7 +14,7 @@ class Patch p where
12 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
14 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
13```
15```
14
16
15−### Patching Maps
17+## Patching Maps
16For 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).
18For 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).
17
19
18`PatchMap` is defined as:
20`PatchMap` is defined as:
cabal.haskell-cideleted−14
@@ -1,14 +0,0 @@
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+7−6
@@ -1,7 +1,8 @@
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;
2−import ((import <nixpkgs> {}).fetchFromGitHub (
5+ } else (import <nixpkgs> {}).fetchFromGitHub {
6+ inherit owner repo rev sha256 fetchSubmodules private;
7+ };
3− let json = builtins.fromJSON (builtins.readFile ./github.json);
8+in import (fetch (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+3−2
@@ -2,6 +2,7 @@
2 "owner": "reflex-frp",
2 "owner": "reflex-frp",
3 "repo": "reflex-platform",
3 "repo": "reflex-platform",
4 "branch": "master",
4 "branch": "master",
5− "rev": "510b990d0b11f0626afbec5fe8575b5b2395391b",
5+ "private": false,
6− "sha256": "09cmahsbxr0963wq171c7j139iyzz49hramr4v9nsf684wcwkngv"
6+ "rev": "c9d11db1b98855fe8ab24a3ff6a5dbe0ad902ad9",
7+ "sha256": "0sfzkqdvyah5mwvmli0wq1nl0b8cvk2cmfgfy4rz57wv42x3099y"
7}
8}
patch.cabal+35−13
@@ -1,7 +1,9 @@
1Name: patch
1Name: patch
2−Version: 0.0.1.0
2+Version: 0.0.5.0
3−Synopsis: Infrastructure for writing patches which act on other types.
3+Synopsis: Data structures for describing changes to other data structures.
4Description:
4Description:
5+ Data structures for describing changes to other data structures.
6+ .
5 In this library, a patch is something which can be applied, analogous to a
7 In this library, a patch is something which can be applied, analogous to a
6 function, and which distinguishes returning the argument it was provided from
8 function, and which distinguishes returning the argument it was provided from
7 returning something else.
9 returning something else.
@@ -12,7 +14,7 @@ Maintainer: maintainer@obsidian.systems
12Stability: Experimental
14Stability: Experimental
13Category: FRP
15Category: FRP
14Build-type: Simple
16Build-type: Simple
15−Cabal-version: >=1.9.2
17+Cabal-version: >=1.10
16homepage: https://obsidian.systems
18homepage: https://obsidian.systems
17bug-reports: https://github.com/reflex-frp/patch/issues
19bug-reports: https://github.com/reflex-frp/patch/issues
18extra-source-files:
20extra-source-files:
@@ -20,7 +22,7 @@ extra-source-files:
20 ChangeLog.md
22 ChangeLog.md
21
23
22tested-with:
24tested-with:
23− GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
25+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
24 GHCJS ==8.4
26 GHCJS ==8.4
25
27
26flag split-these
28flag split-these
@@ -30,38 +32,58 @@ flag split-these
30
32
31library
33library
32 hs-source-dirs: src
34 hs-source-dirs: src
35+ default-language: Haskell2010
33− build-depends: base >= 4.9 && < 4.14
36+ build-depends: base >= 4.9 && < 4.15
34 , constraints-extras >= 0.3 && < 0.4
37 , constraints-extras >= 0.3 && < 0.4
35 , containers >= 0.6 && < 0.7
38 , containers >= 0.6 && < 0.7
36− , dependent-map >= 0.3 && < 0.4
39+ , dependent-map >= 0.3 && < 0.5
37− , dependent-sum >= 0.6 && < 0.7
40+ , dependent-sum >= 0.6 && < 0.8
41+ , lens >= 4.7 && < 5
38 , semigroupoids >= 4.0 && < 6
42 , semigroupoids >= 4.0 && < 6
39 , transformers >= 0.5.6.0 && < 0.6
43 , transformers >= 0.5.6.0 && < 0.6
40− , witherable >= 0.3 && < 0.3.2
44+ , witherable >= 0.3 && < 0.5
45+
46+ if impl(ghc < 8.6) -- really, if base < 8.12
47+ build-depends: base-orphans >= 0.8 && < 0.9
41
48
42 exposed-modules: Data.Functor.Misc
49 exposed-modules: Data.Functor.Misc
50+ , Data.Monoid.DecidablyEmpty
43 , Data.Patch
51 , Data.Patch
44 , Data.Patch.Class
52 , Data.Patch.Class
45 , Data.Patch.DMap
53 , Data.Patch.DMap
46 , Data.Patch.DMapWithMove
54 , Data.Patch.DMapWithMove
47− , Data.Patch.DMapWithReset
48 , Data.Patch.IntMap
55 , Data.Patch.IntMap
49 , Data.Patch.Map
56 , Data.Patch.Map
50 , Data.Patch.MapWithMove
57 , Data.Patch.MapWithMove
51− , Data.Patch.Patchable
58+ , Data.Patch.MapWithPatchingMove
59+ , Data.Semigroup.Additive
52
60
53 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
61 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
62+ default-extensions: PolyKinds
54
63
55 if flag(split-these)
64 if flag(split-these)
56− build-depends: these >= 1 && <1.1
65+ build-depends: these >= 1 && <1.2
57− , these-lens >= 1 && <1.1
58 , semialign >=1 && <1.2
66 , semialign >=1 && <1.2
59 , monoidal-containers >= 0.6 && < 0.7
67 , monoidal-containers >= 0.6 && < 0.7
60 else
68 else
61 build-depends: these >= 0.4 && <0.9
69 build-depends: these >= 0.4 && <0.9
62 , monoidal-containers == 0.4.0.0
70 , monoidal-containers == 0.4.0.0
63
71
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+
64test-suite hlint
85test-suite hlint
86+ default-language: Haskell2010
65 type: exitcode-stdio-1.0
87 type: exitcode-stdio-1.0
66 main-is: hlint.hs
88 main-is: hlint.hs
67 hs-source-dirs: test
89 hs-source-dirs: test
@@ -69,7 +91,7 @@ test-suite hlint
69 , directory
91 , directory
70 , filepath
92 , filepath
71 , filemanip
93 , filemanip
72− , hlint < 2.1 || >= 2.2.2
94+ , hlint (< 2.1 || >= 2.2.2) && < 3.3
73 if impl(ghcjs)
95 if impl(ghcjs)
74 buildable: False
96 buildable: False
75
97
release.nix+20−10
@@ -18,16 +18,26 @@ 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 // {
21− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
27+ patch = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
22− "release.nix"
28+ "release.nix"
23− ".git"
29+ ".git"
24− "dist"
30+ "dist"
25− "dist-newstyle"
31+ "dist-newstyle"
26− "cabal.haskell-ci"
32+ "cabal.haskell-ci"
27− "cabal.project"
33+ "cabal.project"
28− ".travis.yml"
34+ ".travis.yml"
29− ])) ./.;
35+ ])) ./.;
36+ };
37+ })
38+ ];
39+ };
30− in reflex-platform.${ghc}.callCabal2nix "patch" src {});
40+ in reflex-platform.${ghc}.patch);
31 in compilerPkgs // {
41 in compilerPkgs // {
32 cache = reflex-platform.pinBuildInputs "patch-${system}"
42 cache = reflex-platform.pinBuildInputs "patch-${system}"
33 (builtins.attrValues compilerPkgs);
43 (builtins.attrValues compilerPkgs);
src/Data/Functor/Misc.hs+2−3
@@ -6,7 +6,6 @@
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RankNTypes #-}
12{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
@@ -48,7 +47,7 @@ import Data.IntMap (IntMap)
48import qualified Data.IntMap as IntMap
47import qualified Data.IntMap as IntMap
49import Data.Map (Map)
48import Data.Map (Map)
50import qualified Data.Map as Map
49import qualified Data.Map as Map
51−import Data.Some (Some(Some))
50+import Data.Some (Some, mkSome)
52import Data.These
51import Data.These
53import Data.Type.Equality ((:~:)(Refl))
52import Data.Type.Equality ((:~:)(Refl))
54import Data.Typeable hiding (Refl)
53import Data.Typeable hiding (Refl)
@@ -117,7 +116,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
117-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
116-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
118-- the keys, using a function to remove the wrapping 'Functor'
117-- the keys, using a function to remove the wrapping 'Functor'
119weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
118weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
120−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
119+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
121
120
122--------------------------------------------------------------------------------
121--------------------------------------------------------------------------------
123-- WrapArg
122-- WrapArg
src/Data/Monoid/DecidablyEmpty.hs+111−1
@@ -1,5 +1,115 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+{-# LANGUAGE TypeOperators #-}
6+
1-- TODO upstream somwhere else?
7-- TODO upstream somwhere else?
2module Data.Monoid.DecidablyEmpty where
8module Data.Monoid.DecidablyEmpty where
3
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.
4class Monoid a => DecidablyEmpty a where
38class Monoid a => DecidablyEmpty a where
5− isNull :: a -> Bool
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+10−23
@@ -18,11 +18,12 @@ import Data.Functor.Const (Const (..))
18import Data.Functor.Identity
18import Data.Functor.Identity
19import Data.Map.Monoidal (MonoidalMap)
19import Data.Map.Monoidal (MonoidalMap)
20import Data.Proxy
20import Data.Proxy
21−#if !MIN_VERSION_base(4,9,0)
21+#if !MIN_VERSION_base(4,11,0)
22−import Data.Semigroup (Semigroup (..), (<>))
22+import Data.Semigroup (Semigroup (..))
23#endif
23#endif
24import GHC.Generics
24import GHC.Generics
25
25
26+import Data.Semigroup.Additive as X
26import Data.Patch.Class as X
27import Data.Patch.Class as X
27import Data.Patch.DMap as X hiding (getDeletions)
28import Data.Patch.DMap as X hiding (getDeletions)
28import Data.Patch.DMapWithMove as X
29import Data.Patch.DMapWithMove as X
@@ -45,34 +46,25 @@ class (Semigroup q, Monoid q) => Group q where
45 (~~) :: q -> q -> q
46 (~~) :: q -> q -> q
46 r ~~ s = r <> negateG s
47 r ~~ s = r <> negateG s
47
48
48−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
49−class Semigroup q => Additive q where
50−
51-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
49-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
52newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
50newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
53
51
54−instance Additive p => PatchHet (AdditivePatch p) where
55− type PatchSource (AdditivePatch p) = p
56− type PatchTarget (AdditivePatch p) = p
57instance Additive p => Patch (AdditivePatch p) where
52instance Additive p => Patch (AdditivePatch p) where
53+ type PatchTarget (AdditivePatch p) = p
58 apply (AdditivePatch p) q = Just $ p <> q
54 apply (AdditivePatch p) q = Just $ p <> q
59
55
60instance (Ord k, Group q) => Group (MonoidalMap k q) where
56instance (Ord k, Group q) => Group (MonoidalMap k q) where
61 negateG = fmap negateG
57 negateG = fmap negateG
62
58
63−instance (Ord k, Additive q) => Additive (MonoidalMap k q)
64−
65-- | Trivial group.
59-- | Trivial group.
66instance Group () where
60instance Group () where
67− negateG ~() = ()
61+ negateG _ = ()
68− ~() ~~ ~() = ()
62+ _ ~~ _ = ()
69−instance Additive ()
70
63
71-- | Product group. A Pair of groups gives rise to a group
64-- | Product group. A Pair of groups gives rise to a group
72instance (Group a, Group b) => Group (a, b) where
65instance (Group a, Group b) => Group (a, b) where
73 negateG (a, b) = (negateG a, negateG b)
66 negateG (a, b) = (negateG a, negateG b)
74 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
67 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
75−instance (Additive a, Additive b) => Additive (a, b)
76
68
77-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
69-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
78-- Base does not define Monoid (Compose f g a) so this is the best we can
70-- Base does not define Monoid (Compose f g a) so this is the best we can
@@ -80,29 +72,24 @@ instance (Additive a, Additive b) => Additive (a, b)
80instance Group (f (g a)) => Group ((f :.: g) a) where
72instance Group (f (g a)) => Group ((f :.: g) a) where
81 negateG (Comp1 xs) = Comp1 (negateG xs)
73 negateG (Comp1 xs) = Comp1 (negateG xs)
82 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
74 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
83−instance Additive (f (g a)) => Additive ((f :.: g) a)
84
75
85-- | Product of groups, Functor style.
76-- | Product of groups, Functor style.
86instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
77instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
87 negateG (a :*: b) = negateG a :*: negateG b
78 negateG (a :*: b) = negateG a :*: negateG b
88 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
79 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
89−instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
90
80
91-- | Trivial group, Functor style
81-- | Trivial group, Functor style
92instance Group (Proxy x) where
82instance Group (Proxy x) where
93− negateG ~Proxy = Proxy
83+ negateG _ = Proxy
94− ~Proxy ~~ ~Proxy = Proxy
84+ _ ~~ _ = Proxy
95−instance Additive (Proxy x)
96
85
97-- | Const lifts groups into a functor.
86-- | Const lifts groups into a functor.
98deriving instance Group a => Group (Const a x)
87deriving instance Group a => Group (Const a x)
99−instance Additive a => Additive (Const a x)
88+
100−-- | Ideitnty lifts groups pointwise (at only one point)
89+-- | Identity lifts groups pointwise (at only one point)
101deriving instance Group a => Group (Identity a)
90deriving instance Group a => Group (Identity a)
102−instance Additive a => Additive (Identity a)
103
91
104-- | Functions lift groups pointwise.
92-- | Functions lift groups pointwise.
105instance Group b => Group (a -> b) where
93instance Group b => Group (a -> b) where
106 negateG f = negateG . f
94 negateG f = negateG . f
107 (~~) = liftA2 (~~)
95 (~~) = liftA2 (~~)
108−instance Additive b => Additive (a -> b)
src/Data/Patch/Class.hs+11−134
@@ -1,50 +1,23 @@
1−{-# LANGUAGE DefaultSignatures #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveTraversable #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE PolyKinds #-}
6{-# LANGUAGE TypeFamilies #-}
3{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE TypeOperators #-}
8-- | The interface for types which represent changes made to other types
4-- | The interface for types which represent changes made to other types
9module Data.Patch.Class where
5module Data.Patch.Class where
10
6
11import Data.Functor.Identity
7import Data.Functor.Identity
8+import Data.Kind (Type)
12import Data.Maybe
9import Data.Maybe
10+#if !MIN_VERSION_base(4,11,0)
13import Data.Semigroup (Semigroup(..))
11import Data.Semigroup (Semigroup(..))
12+#endif
14import Data.Proxy
13import Data.Proxy
15−import Data.Type.Equality ((:~:) (..))
16−
17−class PatchHet p where
18− type PatchSource p :: *
19− type PatchTarget p :: *
20− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
21− -- 'Nothing'.
22− applyHet
23− :: p
24− -> PatchSource p
25− -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
26− default applyHet
27− :: Patch p
28− => p
29− -> PatchSource p
30− -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
31− applyHet p a = case apply p a of
32− Nothing -> Left Refl
33− Just a' -> Right a'
34−
35−-- | Apply a 'PatchHet'; if it does nothing, return the original value
36−applyAlwaysHet :: PatchHet p => p -> PatchSource p -> PatchTarget p
37−applyAlwaysHet p t = case applyHet p t of
38− Left Refl -> t
39− Right t' -> t'
40
14
41-- | A 'Patch' type represents a kind of change made to a datastructure.
15-- | A 'Patch' type represents a kind of change made to a datastructure.
42--
16--
43-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
17-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
44-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
18-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
45−class ( PatchHet p
46− , PatchSource p ~ PatchTarget p
47− ) => Patch p where
19+class Patch p where
20+ type PatchTarget p :: Type
48 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
21 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
49 -- 'Nothing'.
22 -- 'Nothing'.
50 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
23 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -54,115 +27,19 @@ applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
54applyAlways p t = fromMaybe t $ apply p t
27applyAlways p t = fromMaybe t $ apply p t
55
28
56-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
29-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
57−instance PatchHet (Identity a) where
58− type PatchSource (Identity a) = a
59− type PatchTarget (Identity a) = a
60instance Patch (Identity a) where
30instance Patch (Identity a) where
31+ type PatchTarget (Identity a) = a
61 apply (Identity a) _ = Just a
32 apply (Identity a) _ = Just a
62
33
63−-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
34+-- | 'Proxy' can be used as a 'Patch' that does nothing.
64−instance PatchHet (Proxy (a :: *)) where
35+instance forall (a :: Type). Patch (Proxy a) where
65− type PatchSource (Proxy a) = a
66 type PatchTarget (Proxy a) = a
36 type PatchTarget (Proxy a) = a
67−instance Patch (Proxy (a :: *)) where
68 apply ~Proxy _ = Nothing
37 apply ~Proxy _ = Nothing
69
38
70-- | Like '(.)', but composes functions that return patches rather than
39-- | Like '(.)', but composes functions that return patches rather than
71-- functions that return new values. The Semigroup instance for patches must
40-- functions that return new values. The Semigroup instance for patches must
72-- apply patches right-to-left, like '(.)'.
41-- apply patches right-to-left, like '(.)'.
73−composePatchFunctions
42+composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
74− :: (Patch p, Semigroup p)
75− => (PatchTarget p -> p)
76− -> (PatchTarget p -> p)
77− -> PatchTarget p -> p
78composePatchFunctions g f a =
43composePatchFunctions g f a =
79 let fp = f a
44 let fp = f a
80 in g (applyAlways fp a) <> fp
45 in g (applyAlways fp a) <> fp
81−
82−
83−class PatchHet2Base (p :: k -> k -> *) where
84− type PatchSource1 p :: k -> *
85− type PatchTarget1 p :: k -> *
86−
87−class ( PatchHet2Base p
88− , PatchHet (p from to)
89− , PatchSource1 p from ~ PatchSource (p from to)
90− , PatchTarget1 p to ~ PatchTarget (p from to)
91− ) => PatchHet2Locally (p :: k -> k -> *) from to where
92−instance ( PatchHet2Base p
93− , PatchHet (p from to)
94− , PatchSource1 p from ~ PatchSource (p from to)
95− , PatchTarget1 p to ~ PatchTarget (p from to)
96− ) => PatchHet2Locally (p :: k -> k -> *) from to where
97−
98−applyHet2Locally
99− :: PatchHet2Locally p from to
100− => p from to
101− -> PatchSource1 p from
102− -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
103−applyHet2Locally = applyHet
104−
105−applyAlwaysHet2Locally
106− :: PatchHet2Locally p from to
107− => p from to
108− -> PatchSource1 p from
109− -> PatchTarget1 p to
110−applyAlwaysHet2Locally = applyAlwaysHet
111−
112−-- TODO once we can use quantified constraints, perhaps combine PatchHet2Base and
113−-- PatchHet2Locally, or at least get rid of this.
114−class PatchHet2Base p => PatchHet2 (p :: k -> k -> *) where
115− applyHet2
116− :: p from to
117− -> PatchSource1 p from
118− -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
119−
120−applyAlwaysHet2
121− :: PatchHet2 p
122− => p from to
123− -> PatchSource1 p from
124− -> PatchTarget1 p to
125−applyAlwaysHet2 p t = case applyHet2 p t of
126− Left Refl -> t
127− Right t' -> t'
128−
129−-- | Connect the classes without quanitified constraints
130−newtype ProjectLocal p from to = ProjectLocal { unProjectLocal :: p from to }
131−
132−instance PatchHet2 p => PatchHet (ProjectLocal p from to) where
133− type PatchSource (ProjectLocal p from to) = PatchSource1 p from
134− type PatchTarget (ProjectLocal p from to) = PatchTarget1 p to
135− applyHet (ProjectLocal p) src = applyHet2 p src
136−
137−instance PatchHet2 p => PatchHet2Base (ProjectLocal p) where
138− type PatchSource1 (ProjectLocal p) = PatchSource1 p
139− type PatchTarget1 (ProjectLocal p) = PatchTarget1 p
140−
141−class ( PatchHet2Base p
142− , PatchSource1 p ~ PatchTarget1 p
143− ) => Patch2 p
144−instance ( PatchHet2Base p
145− , PatchSource1 p ~ PatchTarget1 p
146− ) => Patch2 p
147−
148−newtype Replace2 (t :: k -> *) (a :: k) (b :: k) = Replace2 (t b)
149− deriving ( Show, Read, Eq, Ord
150− , Functor, Foldable, Traversable
151− )
152−
153−data Proxy2 t a b = Proxy2
154− deriving ( Show, Read, Eq, Ord
155− , Functor, Foldable, Traversable
156− )
157−
158−-- | 'Replace2' can be used as a 'Patch' that always fully replaces the value
159−instance PatchHet (Replace2 (t :: k -> *) (from :: k) (to :: k)) where
160− type PatchSource (Replace2 t from to) = t from
161− type PatchTarget (Replace2 t from to) = t to
162− applyHet (Replace2 val) _ = Right val
163−
164−-- | 'Proxy2' can be used as a 'Patch' that always does nothing
165−instance PatchHet (Proxy2 (t :: k -> *) (a :: k) (a :: k)) where
166− type PatchSource (Proxy2 t a a) = t a
167− type PatchTarget (Proxy2 t a a) = t a
168− applyHet ~Proxy2 _ = Left Refl
src/Data/Patch/DMap.hs+13−4
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE PolyKinds #-}
4{-# LANGUAGE PolyKinds #-}
@@ -5,6 +6,7 @@
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
9+
8-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
10-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
9module Data.Patch.DMap where
11module Data.Patch.DMap where
10
12
@@ -12,13 +14,18 @@ import Data.Patch.Class
12import Data.Patch.IntMap
14import Data.Patch.IntMap
13import Data.Patch.Map
15import Data.Patch.Map
14
16
15−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
17+import Data.Dependent.Map (DMap)
18+import Data.Dependent.Sum (DSum (..))
19+import Data.GADT.Compare (GCompare (..))
16import qualified Data.Dependent.Map as DMap
20import qualified Data.Dependent.Map as DMap
17import Data.Functor.Constant
21import Data.Functor.Constant
18import Data.Functor.Misc
22import Data.Functor.Misc
19import qualified Data.IntMap as IntMap
23import qualified Data.IntMap as IntMap
20import qualified Data.Map as Map
24import qualified Data.Map as Map
25+import Data.Monoid.DecidablyEmpty
26+#if !MIN_VERSION_base(4,11,0)
21import Data.Semigroup (Semigroup (..))
27import Data.Semigroup (Semigroup (..))
28+#endif
22import Data.Some (Some)
29import Data.Some (Some)
23
30
24-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
31-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
@@ -30,11 +37,13 @@ deriving instance GCompare k => Semigroup (PatchDMap k v)
30
37
31deriving instance GCompare k => Monoid (PatchDMap k v)
38deriving instance GCompare k => Monoid (PatchDMap k v)
32
39
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+
33-- | Apply the insertions or deletions to a given 'DMap'.
44-- | Apply the insertions or deletions to a given 'DMap'.
34−instance GCompare k => PatchHet (PatchDMap k v) where
35− type PatchSource (PatchDMap k v) = DMap k v
36− type PatchTarget (PatchDMap k v) = DMap k v
37instance GCompare k => Patch (PatchDMap k v) where
45instance GCompare k => Patch (PatchDMap k v) where
46+ type PatchTarget (PatchDMap k v) = DMap k v
38 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.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?
47 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.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?
39 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
48 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
40 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
49 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
src/Data/Patch/DMapWithMove.hs+119−390
@@ -1,314 +1,108 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE PatternGuards #-}
7{-# LANGUAGE PatternGuards #-}
10{-# LANGUAGE PolyKinds #-}
8{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE Rank2Types #-}
9{-# LANGUAGE Rank2Types #-}
12{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14−{-# LANGUAGE TypeApplications #-}
15{-# LANGUAGE TypeFamilies #-}
11{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE UndecidableInstances #-}
12{-# LANGUAGE UndecidableInstances #-}
17−{-# LANGUAGE UndecidableSuperClasses #-}
18
13
19-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
14-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
20-- move values between keys.
15-- move values between keys.
21module Data.Patch.DMapWithMove where
16module Data.Patch.DMapWithMove where
22
17
23−import qualified Control.Category as Cat
18+import Data.Patch.Class
19+import Data.Patch.MapWithMove (PatchMapWithMove (..))
20+import qualified Data.Patch.MapWithMove as MapWithMove
21+
24import Data.Constraint.Extras
22import Data.Constraint.Extras
25−import Data.Constraint.Compose
26−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
23+import Data.Dependent.Map (DMap)
24+import Data.Dependent.Sum (DSum (..))
27import qualified Data.Dependent.Map as DMap
25import qualified Data.Dependent.Map as DMap
28import Data.Functor.Constant
26import Data.Functor.Constant
29import Data.Functor.Misc
27import Data.Functor.Misc
30import Data.Functor.Product
28import Data.Functor.Product
31−import Data.GADT.Compare (GEq (..), GCompare)
29+import Data.GADT.Compare (GEq (..), GCompare (..))
32−import Data.GADT.Show (GRead, GShow, gshow)
30+import Data.GADT.Show (GShow, gshow)
33import qualified Data.Map as Map
31import qualified Data.Map as Map
34import Data.Maybe
32import Data.Maybe
35import Data.Monoid.DecidablyEmpty
33import Data.Monoid.DecidablyEmpty
34+#if !MIN_VERSION_base(4,11,0)
36−import Data.Semigroup (Semigroup (..), (<>))
35+import Data.Semigroup (Semigroup (..))
37−import Data.Semigroupoid as Cat
36+#endif
38−import Data.Some (Some(Some))
37+import Data.Some (Some, mkSome)
39−import Data.Proxy
40import Data.These
38import Data.These
41−import Data.Type.Equality ((:~:) (..))
42−import Data.Kind (Constraint)
43−
44−import Data.Patch.Class
45−import Data.Patch.MapWithMove (PatchMapWithMove (..))
46−import qualified Data.Patch.MapWithMove as MapWithMove
47−
48−-- | Composition for constraints.
49−class p (f a a) => DupC (p :: k2 -> Constraint) (f :: k1 -> k1 -> k2) (a :: k1)
50−instance p (f a a) => DupC p f a
51−
52−type ConstraintsForZip f (c :: k -> Constraint) (g :: k' -> k' -> k) =
53− ConstraintsFor f (DupC c g)
54
39
55−type HasZip (c :: k -> Constraint) f (g :: k' -> k' -> k) =
56− (ArgDict (DupC c g) f, ConstraintsForZip f c g)
57−
58−-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
59−-- which describes how it will be changed by the patch and connects move sources
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
60−-- and destinations.
41+-- destinations.
61--
42--
62-- Invariants:
43-- Invariants:
63--
44--
64−-- * A key should not move to itself.
45+-- * A key should not move to itself.
65−--
66−-- * A move should always be represented with both the destination key (as a
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)@)
67−-- 'From_Move') and the source key (as a @'ComposeMaybe' ('Just'
68−-- destination)@)
69newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
47newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
70
48
71−deriving instance ( GShow k
49+-- It won't let me derive for some reason
72− , HasZip Show k p
73− , Has' Show k (PatchTarget1 p)
74− ) => Show (PatchDMapWithMove k p)
75−deriving instance ( GRead k
76− , HasZip Read k p
77− , Has' Read k (PatchTarget1 p)
78− ) => Read (PatchDMapWithMove k p)
79−deriving instance ( GEq k
80− , HasZip Eq k p
81− , Has' Eq k (PatchTarget1 p)
82− ) => Eq (PatchDMapWithMove k p)
83−deriving instance ( GCompare k
50+instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
84− , HasZip Ord k p
85− , Has' Ord k (PatchTarget1 p)
86− ) => Ord (PatchDMapWithMove k p)
51+ isEmpty (PatchDMapWithMove m) = DMap.null m
87−
52+
88−-- | Structure which represents what changes apply to a particular key.
89−-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
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
90−-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
54+-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
91−-- key the current key is moving to if involved in a move.
92−data NodeInfo k p a = NodeInfo
55+data NodeInfo k v a = NodeInfo
93− { _nodeInfo_from :: !(From k p a)
56+ { _nodeInfo_from :: !(From k v a)
94− -- ^ Change applying to the current key, be it an insert, move, or delete.
57+ -- ^Change applying to the current key, be it an insert, move, or delete.
95− , _nodeInfo_to :: !(To k p a)
58+ , _nodeInfo_to :: !(To k a)
96− -- ^ Where this key is moving to, if involved in a move. Should only be
59+ -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
97− -- @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
98 }
60 }
99−
61+ deriving (Show)
100−deriving instance ( Show (k a)
62+
101− , Show (p a a)
102− , Show (PatchTarget1 p a)
103− ) => Show (NodeInfo k p a)
104−deriving instance ( Read (k a)
105− , Read (p a a)
106− , Read (PatchTarget1 p a)
107− ) => Read (NodeInfo k p a)
108−deriving instance ( Eq (k a)
109− , Eq (p a a)
110− , Eq (PatchTarget1 p a)
111− ) => Eq (NodeInfo k p a)
112−deriving instance ( Ord (k a)
113− , Ord (p a a)
114− , Ord (PatchTarget1 p a)
115− ) => Ord (NodeInfo k p a)
116−
117−-- | Structure describing a particular change to a key, be it inserting a new
118−-- key (@By_Insert@), updating an existing key (@By_Insert@ again), deleting
119−-- a key (@By_Delete@), or moving a key (@By_Move@).
120−--
121−-- This type isn't used directly as the from field patch, but is instead wrapped
122−-- in an existential. However, it is nice to be able to reason about this in
123−-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
124−data By (k :: a -> *) (p :: a -> a -> *) :: a -> a -> * where
125− -- | Insert a new or update an existing key with the given value @PatchTarget1
126− -- p a@
127− By_Insert :: PatchTarget1 p to -> By k p from to
128− -- | Delete the existing key
129− By_Delete :: By k p from to
130− -- | Move the value from the given key @k a@ to this key. The source key
131− -- should also have an entry in the patch giving the current key as
132− -- @_nodeInfo_to@, usually but not necessarily with @By_Delete@.
133− By_Move :: !(k from) -> p from to -> By k p from to
134−
135−deriving instance ( Show (k to)
136− , Show (p from to)
137− , Show (PatchTarget1 p to)
138− ) => Show (By k p from to)
139−deriving instance ( Read (k to)
140− , Read (p from to)
141− , Read (PatchTarget1 p to)
142− ) => Read (By k p from to)
143−deriving instance ( Eq (k to)
144− , Eq (p from to)
145− , Eq (PatchTarget1 p to)
146− ) => Eq (By k p from to)
147−deriving instance ( Ord (k to)
148− , Ord (p from to)
149− , Ord (PatchTarget1 p to)
150− ) => Ord (By k p from to)
151−
152−mapByPatch
153− :: PatchTarget1 p0 ~ PatchTarget1 p1
154− => ((p0 from to) -> (p1 from to))
155− -> By k p0 from to
156− -> By k p1 from to
157−mapByPatch f = \case
158− By_Insert v -> By_Insert v
159− By_Delete -> By_Delete
160− By_Move k p -> By_Move k $ f p
161−
162−-- | Compose patches having the same effect as applying the patches in turn:
163−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
164−instance ( PatchSource1 p ~ PatchTarget1 p
165− , Cat.Semigroupoid p
166− , PatchHet2 p
167− ) => Cat.Semigroupoid (By k p) where
168− o p0 p1 = mapByPatch unProjectLocal $
169− oLocal (mapByPatch ProjectLocal p0) (mapByPatch ProjectLocal p1)
170−
171−oLocal
172− :: ( PatchSource1 p ~ PatchTarget1 p
173− , PatchHet2Locally p between after
174− )
175− => By k p between after
176− -> By k p before between
177− -> By k p before after
178−By_Insert new `oLocal` _ = By_Insert new
179−By_Delete `oLocal` _ = By_Delete
180−By_Move _ x `oLocal` By_Insert y = By_Insert $ applyAlwaysHet2 x y
181−By_Move _ x `oLocal` By_Move src y = By_Move src $ x `o` y
182−By_Move _ _ `oLocal` By_Delete = By_Delete
183−
184−newtype Flip p to from = Flip (p from to)
185−
186−-- | Structure describing a particular change to a key, be it inserting a new
63+-- |Structure describing a particular change to a key, be it inserting a new key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting a
187−-- key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting
188−-- a key (@From_Delete@), or moving a key (@From_Move@).
64+-- key (@From_Delete@), or moving a key (@From_Move@).
189−--
190−-- This type isn't used directly as the from field patch, but is instead wrapped
191−-- in an existential. However, it is nice to be able to reason about this in
192−-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
193−data From (k :: a -> *) (p :: a -> a -> *) :: a -> * where
65+data From (k :: a -> *) (v :: a -> *) :: a -> * where
194− -- | Insert a new or update an existing key with the given value @PatchTarget1
66+ -- |Insert a new or update an existing key with the given value @v a@
195− -- p a@
196− From_Insert :: PatchTarget1 p to -> From k p to
67+ From_Insert :: v a -> From k v a
197− -- | Delete the existing key
68+ -- |Delete the existing key
198− From_Delete :: From k p to
69+ From_Delete :: From k v a
199− -- | Move the value from the given key @k a@ to this key. The source key
200− -- should also have an entry in the patch giving the current key as
70+ -- |Move the value from the given key @k a@ to this key. The source key should also have an entry in the patch giving the current key as @_nodeInfo_to@,
201− -- @_nodeInfo_to@, usually but not necessarily with @From_Delete@.
71+ -- usually but not necessarily with @From_Delete@.
202− From_Move :: !(DSum k (Flip p to)) -> From k p to
72+ From_Move :: !(k a) -> From k v a
203−
73+ deriving (Show, Read, Eq, Ord)
204−deriving instance ( Show (k a)
74+
205− , Show (p a a)
75+-- |Type alias for the "to" part of a 'NodeInfo'. @'ComposeMaybe' ('Just' k)@ means the key is moving to another key, @ComposeMaybe Nothing@ for any other
206− , Show (PatchTarget1 p a)
76+-- operation.
207− ) => Show (From k p a)
77+type To = ComposeMaybe
208−deriving instance ( Read (k a)
209− , Read (p a a)
210− , Read (PatchTarget1 p a)
211− ) => Read (From k p a)
212−
213−deriving instance ( GEq k
214− , HasZip Eq k p
215− , Eq (PatchTarget1 p a)
216− ) => Eq (From k p a)
217−
218−deriving instance ( GCompare k
219− , HasZip Ord k p
220− , Ord (PatchTarget1 p a)
221− ) => Ord (From k p a)
222−
223−-- | The "to" part of a 'NodeInfo'. Rather than be built out of @From@ like @From@
224−-- is, we store just the information necessary to compose a @To@ and @From@ like
225−-- @oLocal@ composes two @From@s.
226−data To (k :: a -> *) (p :: a -> a -> *) :: a -> * where
227− -- | Delete or leave in place
228− To_NonMove :: To k p a0
229− -- | Move the value from the given key @k a@ to this key. The target key
230− -- should also have an entry in the patch giving the current key in
231− -- @_nodeInfo_from@, usually but not necessarily with @To_Delete@.
232− To_Move :: !(k a1) -> p a0 a1 -> To k p a0
233−
234−deriving instance ( Show (k a)
235− , Show (p a a)
236− , Show (PatchTarget1 p a)
237− ) => Show (To k p a)
238−deriving instance ( Read (k a)
239− , Read (p a a)
240− , Read (PatchTarget1 p a)
241− ) => Read (To k p a)
242−
243−instance ( GEq k
244− , HasZip Eq k p
245− , Eq (PatchTarget1 p a)
246− ) => Eq (To k p a) where
247− To_NonMove == To_NonMove = True
248− To_Move k0 p0 == To_Move k1 p1 = case geq k0 k1 of
249− Nothing -> False
250− Just Refl -> has @(DupC Eq p) k0 $ p0 == p1
251−
252−instance ( GCompare k
253− , HasZip Ord k p
254− , Ord (PatchTarget1 p a)
255− ) => Ord (To k p a) where
256− To_NonMove `compare` To_NonMove = EQ
257− To_Move k0 p0 `compare` To_Move k1 p1 = case gcompare k0 k1 of
258− DMap.GGT -> GT
259− DMap.GEQ -> has @(DupC Ord p) k0 $ p0 `compare` p1
260− DMap.GLT -> LT
261−
262−--oToFrom
263−-- :: ( PatchSource1 p ~ PatchTarget1 p
264−-- , PatchHet2 p
265−-- )
266−-- => To k p between
267−-- -> From k p between
268−-- -> DSum k (From k p)
269−--From_Insert new `oToFrom` _ = From_Insert new
270−--From_Delete `oToFrom` _ = From_Delete
271−--From_Move _ x `oToFrom` From_Insert y = From_Insert $ applyAlwaysHet2 x y
272−--From_Move _ x `oToFrom` From_Move src y = From_Move src $ x `o` y
273−--From_Move _ _ `oToFrom` From_Delete = From_Delete
274
78
275-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
79-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
276−validPatchDMapWithMove
80+validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
277− :: forall k v
278− . (GCompare k, GShow k)
279− => DMap k (NodeInfo k v)
280− -> Bool
281validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
81validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
282
82
283-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
83-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
284−validationErrorsForPatchDMapWithMove
84+validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
285− :: forall k v
286− . (GCompare k, GShow k)
287− => DMap k (NodeInfo k v)
288− -> [String]
289validationErrorsForPatchDMapWithMove m =
85validationErrorsForPatchDMapWithMove m =
290 noSelfMoves <> movesBalanced
86 noSelfMoves <> movesBalanced
291 where
87 where
292 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
88 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
293− selfMove (dst :=> NodeInfo (From_Move (src :=> _)) _)
294− | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
89+ selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
295− selfMove (src :=> NodeInfo _ (To_Move dst _))
296− | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
90+ selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
297 selfMove _ = Nothing
91 selfMove _ = Nothing
298 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
92 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
299− unbalancedMove (dst :=> NodeInfo (From_Move (src :=> _)) _) =
93+ unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
300 case DMap.lookup src m of
94 case DMap.lookup src m of
301 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
95 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
302− Just (NodeInfo _ (To_Move dst' _)) ->
96+ Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
303 if isNothing (dst' `geq` dst)
97 if isNothing (dst' `geq` dst)
304 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
98 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
305 else Nothing
99 else Nothing
306 _ ->
100 _ ->
307 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
101 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
308− unbalancedMove (src :=> NodeInfo _ (To_Move dst _)) =
102+ unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
309 case DMap.lookup dst m of
103 case DMap.lookup dst m of
310 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
104 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
311− Just (NodeInfo (From_Move (src' :=> _)) _) ->
105+ Just (NodeInfo (From_Move src') _) ->
312 if isNothing (src' `geq` src)
106 if isNothing (src' `geq` src)
313 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
107 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
314 else Nothing
108 else Nothing
@@ -317,41 +111,33 @@ validationErrorsForPatchDMapWithMove m =
317 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
111 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
318 unbalancedMove _ = Nothing
112 unbalancedMove _ = Nothing
319
113
114+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
115+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
116+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
117+
320-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
118-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
321data Pair1 f g a = Pair1 (f a) (g a)
119data Pair1 f g a = Pair1 (f a) (g a)
322
120
323-- |Helper data structure used for composing patches using the monoid instance.
121-- |Helper data structure used for composing patches using the monoid instance.
324−data Fixup k p a
122+data Fixup k v a
325 = Fixup_Delete
123 = Fixup_Delete
326− | Fixup_Update (These (From k p a) (To k p a))
124+ | Fixup_Update (These (From k v a) (To k a))
327−
125+
328−-- | Compose patches having the same effect as applying the patches in turn:
126+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
329−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
330−instance ( GCompare k
331− , Cat.Semigroupoid p
332− -- , DecidablyEmpty p
333− , PatchHet2 p
334− ) => Semigroup (PatchDMapWithMove k p) where
127+instance GCompare k => Semigroup (PatchDMapWithMove k v) where
335 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
128 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
336 where
129 where
337− connections :: [DSum k (Pair1 (To k p) (From k p))]
338− connections = DMap.toList $ DMap.intersectionWithKey
339− (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b))
130+ connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
340− ma
341− mb
342− h :: DSum k (Pair1 k (From k v)) -> [DSum k (Fixup k v)]
131+ h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
343− h (between :=> Pair1 editAfter editBefore) = case (editAfter, editBefore) of
132+ h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
344− (To_Move toAfter p1, From_Move (fromBefore :=> p0)) -> case toAfter `geq` fromBefore of
133+ (Just toAfter, From_Move fromBefore)
345− Just Refl | isNull p0 ->
134+ | isJust $ fromBefore `geq` toAfter
346− [ toAfter :=> Fixup_Delete ]
135+ -> [toAfter :=> Fixup_Delete]
347− _ ->
136+ | otherwise
348− [ toAfter :=> Fixup_Update (This editBefore)
137+ -> [ toAfter :=> Fixup_Update (This editBefore)
349− , fromBefore :=> Fixup_Update (That editAfter)
138+ , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
350− ]
139+ ]
351− (To_NonMove, From_Move (fromBefore :=> _)) ->
352− -- The item is destroyed in the second patch, so indicate that it is
140+ (Nothing, From_Move fromBefore) -> [fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
353− -- destroyed in the source map
354− [fromBefore :=> Fixup_Update (That (ComposeMaybe editAfter))]
355 (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
141 (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
356 (Nothing, _) -> []
142 (Nothing, _) -> []
357 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
143 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
@@ -369,13 +155,7 @@ instance ( GCompare k
369 applyFixup _ ni = \case
155 applyFixup _ ni = \case
370 Fixup_Delete -> Nothing
156 Fixup_Delete -> Nothing
371 Fixup_Update u -> Just $ NodeInfo
157 Fixup_Update u -> Just $ NodeInfo
372− { _nodeInfo_from = case _nodeInfo_from ni of
158+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
373− f@(From_Move (_ :=> p')) -> case _here u of -- The `from` fixup comes from the "old" patch
374− Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
375− Just (From_Insert v) -> From_Insert $ applyAlways p' v
376− Just From_Delete -> From_Delete
377− Just (From_Move (oldKey :=> p)) -> From_Move $ oldKey :=> (p' <> p)
378− _ -> error "DPatchMapWithMove: fixup for non-move From"
379 , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
159 , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
380 }
160 }
381 m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
161 m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
@@ -396,7 +176,7 @@ instance GCompare k => Monoid (PatchDMapWithMove k v) where
396 mappend = (<>)
176 mappend = (<>)
397
177
398{-
178{-
399−mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
179+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
400PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
180PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
401 where
181 where
402 getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
182 getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
@@ -417,8 +197,8 @@ PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWith
417 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
197 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
418-}
198-}
419
199
420−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @PatchTarget1 p a@ to the given key @k a@, like 'DMap.insert'.
200+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @v a@ to the given key @k a@, like 'DMap.insert'.
421−insertDMapKey :: k a -> PatchTarget1 p a -> PatchDMapWithMove k p
201+insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
422insertDMapKey k v =
202insertDMapKey k v =
423 PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
203 PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
424
204
@@ -430,7 +210,7 @@ insertDMapKey k v =
430moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
210moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
431moveDMapKey src dst = case src `geq` dst of
211moveDMapKey src dst = case src `geq` dst of
432 Nothing -> PatchDMapWithMove $ DMap.fromList
212 Nothing -> PatchDMapWithMove $ DMap.fromList
433− [ dst :=> NodeInfo (From_Move (src :=> Cat.id)) (ComposeMaybe Nothing)
213+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
434 , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
214 , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
435 ]
215 ]
436 Just _ -> mempty
216 Just _ -> mempty
@@ -440,15 +220,15 @@ moveDMapKey src dst = case src `geq` dst of
440-- @
220-- @
441-- let aMay = DMap.lookup a dmap
221-- let aMay = DMap.lookup a dmap
442-- bMay = DMap.lookup b dmap
222-- bMay = DMap.lookup b dmap
443−-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
223+-- in maybe id (DMap.insert a) (bMay <> aMay)
444−-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
224+-- . maybe id (DMap.insert b) (aMay <> bMay)
445-- . DMap.delete a . DMap.delete b $ dmap
225-- . DMap.delete a . DMap.delete b $ dmap
446-- @
226-- @
447swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
227swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
448swapDMapKey src dst = case src `geq` dst of
228swapDMapKey src dst = case src `geq` dst of
449 Nothing -> PatchDMapWithMove $ DMap.fromList
229 Nothing -> PatchDMapWithMove $ DMap.fromList
450− [ dst :=> NodeInfo (From_Move (src :=> Cat.id)) (ComposeMaybe $ Just src)
230+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
451− , src :=> NodeInfo (From_Move (dst :=> Cat.id)) (ComposeMaybe $ Just dst)
231+ , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
452 ]
232 ]
453 Just _ -> mempty
233 Just _ -> mempty
454
234
@@ -499,144 +279,93 @@ patchDMapWithMove dm =
499 errs -> Left errs
279 errs -> Left errs
500
280
501-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
281-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
502−mapPatchDMapWithMove
282+mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
503− :: forall k p p'
504− . (forall a. PatchTarget1 p a -> PatchTarget1 p' a)
505− -> (forall from to. p from to -> p' from to)
506− -> PatchDMapWithMove k p
507− -> PatchDMapWithMove k p'
508−mapPatchDMapWithMove f g (PatchDMapWithMove p) = PatchDMapWithMove $
283+mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
509 DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
284 DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
510− where g :: forall a. From k PatchTarget1 p a -> From k PatchTarget1 p' a
285+ where g :: forall a. From k v a -> From k v' a
511 g = \case
286 g = \case
512 From_Insert v -> From_Insert $ f v
287 From_Insert v -> From_Insert $ f v
513 From_Delete -> From_Delete
288 From_Delete -> From_Delete
514− From_Move k p -> From_Move k $ g p
289+ From_Move k -> From_Move k
515−
290+
516−-- | Traverse an effectful function @forall a. PatchTarget1 p a -> m (v ' a)@
517−-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
291+-- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
518−-- ('PatchDMapWithMove' k v')@.
519−traversePatchDMapWithMove
292+traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
520− :: forall m k p p'
521− . Applicative m
522− => (forall a. PatchTarget1 p a -> m (PatchTarget1 p' a))
523− -> PatchDMapWithMove k p
524− -> m (PatchDMapWithMove k p')
525traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
293traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
526
294
527−-- | Map an effectful function @forall a. k a -> PatchTarget1 p a -> m (v ' a)@
528−-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
295+-- |Map an effectful function @forall a. k a -> v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
529−-- ('PatchDMapWithMove' k v')@.
530−traversePatchDMapWithMoveWithKey
296+traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
531− :: forall m k p p'
532− . Applicative m
533− => (forall a. k a -> PatchTarget1 p a -> m (PatchTarget1 p' a))
534− -> PatchDMapWithMove k p
535− -> m (PatchDMapWithMove k p')
536−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) =
537− PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
297+traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
538− where g :: forall a. k a -> From k PatchTarget1 p a -> m (From k PatchTarget1 p' a)
298+ where g :: forall a. k a -> From k v a -> m (From k v' a)
539 g k = \case
299 g k = \case
540 From_Insert v -> From_Insert <$> f k v
300 From_Insert v -> From_Insert <$> f k v
541 From_Delete -> pure From_Delete
301 From_Delete -> pure From_Delete
542 From_Move fromKey -> pure $ From_Move fromKey
302 From_Move fromKey -> pure $ From_Move fromKey
543
303
544−-- | Map a function which transforms @'From k PatchTarget1 p a@ into a @'From k
304+-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
545−-- PatchTarget1 p' a@ over a @'NodeInfo' k PatchTarget1 p a@.
546−nodeInfoMapFrom
305+nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
547− :: (From k p a -> From k p' a)
548− -> NodeInfo k p a
549− -> NodeInfo k p' a
550nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
306nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
551
307
552−-- | Map an effectful function which transforms @'From k PatchTarget1 p a@ into
308+-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
553−-- a @f ('From k PatchTarget1 p' a)@ over a @'NodeInfo' k PatchTarget1 p a@.
554−nodeInfoMapFromM
309+nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
555− :: Functor f
556− => (From k p a -> f (From k p' a))
557− -> NodeInfo k p a
558− -> f (NodeInfo k p' a)
559nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
310nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
560
311
561−-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
312+-- |Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys from @k a@ to @'Some' k@ and applying a given weakening function @v a -> v'@ to
562−-- from @k a@ to @'Some' k@ and applying a given weakening function
563−-- @PatchTarget1 p a -> v'@ to values.
313+-- values.
564−weakenPatchDMapWithMoveWith
314+weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
565− :: forall k p v'
566− . (forall a. PatchTarget1 p a -> v')
567− -> PatchDMapWithMove k p
568− -> PatchMapWithMove (Some k) (Proxy v')
569weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
315weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
570− where g :: forall a. NodeInfo k PatchTarget1 p a -> MapWithMove.NodeInfo (Some k) (Proxy v')
316+ where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
571 g ni = MapWithMove.NodeInfo
317 g ni = MapWithMove.NodeInfo
572 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
318 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
573 From_Insert v -> MapWithMove.From_Insert $ f v
319 From_Insert v -> MapWithMove.From_Insert $ f v
574 From_Delete -> MapWithMove.From_Delete
320 From_Delete -> MapWithMove.From_Delete
575− From_Move k -> MapWithMove.From_Move (Some k) Proxy
321+ From_Move k -> MapWithMove.From_Move $ mkSome k
576− , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
322+ , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
577 }
323 }
578
324
579−-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
580−-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
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
581−-- dependency in the typing and all points are already @a@, hence the function
326+-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
582−-- to map each value to @v'@ is not higher rank.
583−patchDMapWithMoveToPatchMapWithMoveWith
327+patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v'
584− :: forall k p v' a. (PatchTarget1 p a -> v')
585− -> PatchDMapWithMove (Const2 k a) p
586− -> PatchMapWithMove k (Proxy v')
587patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
328patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
588− where g :: NodeInfo (Const2 k a) PatchTarget1 p a -> MapWithMove.NodeInfo k (Proxy v')
329+ where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
589 g ni = MapWithMove.NodeInfo
330 g ni = MapWithMove.NodeInfo
590 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
331 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
591 From_Insert v -> MapWithMove.From_Insert $ f v
332 From_Insert v -> MapWithMove.From_Insert $ f v
592 From_Delete -> MapWithMove.From_Delete
333 From_Delete -> MapWithMove.From_Delete
593− From_Move (Const2 k) -> MapWithMove.From_Move k Proxy
334+ From_Move (Const2 k) -> MapWithMove.From_Move k
594 , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
335 , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
595 }
336 }
596
337
597−-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
338+-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
598−-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
599−-- dependently typed one but which always has a constant key type represented by
339+-- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@.
600−-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
601−-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
340+-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
602−const2PatchDMapWithMoveWith
341+const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v'
603− :: forall k v p' a
604− . (v -> PatchTarget1 p' a)
605− -> PatchMapWithMove k (Proxy v)
606− -> PatchDMapWithMove (Const2 k a) p'
607const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
342const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
608− where g :: (k, MapWithMove.NodeInfo k (Proxy v)) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
343+ where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
609 g (k, ni) = Const2 k :=> NodeInfo
344 g (k, ni) = Const2 k :=> NodeInfo
610 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
345 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
611 MapWithMove.From_Insert v -> From_Insert $ f v
346 MapWithMove.From_Insert v -> From_Insert $ f v
612 MapWithMove.From_Delete -> From_Delete
347 MapWithMove.From_Delete -> From_Delete
613− MapWithMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey
348+ MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
614 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
349 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
615 }
350 }
616
351
617-- | Apply the insertions, deletions, and moves to a given 'DMap'.
352-- | Apply the insertions, deletions, and moves to a given 'DMap'.
618−instance GCompare k => PatchHet (PatchDMapWithMove k p) where
353+instance GCompare k => Patch (PatchDMapWithMove k v) where
619− type PatchSource (PatchDMapWithMove k p) = DMap k (PatchSource1 p)
620− type PatchTarget (PatchDMapWithMove k p) = DMap k (PatchTarget1 p)
354+ type PatchTarget (PatchDMapWithMove k v) = DMap k v
621−instance GCompare k => Patch (PatchDMapWithMove k p) where
622− apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions)
355+ apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.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?
623− -- TODO: return Nothing sometimes --Note: the strict application here is
624− -- critical to ensuring that incremental merges don't hold onto all their
625− -- prerequisite events forever; can we make this more robust?
626 where insertions = DMap.mapMaybeWithKey insertFunc p
356 where insertions = DMap.mapMaybeWithKey insertFunc p
627− insertFunc :: forall a. k a -> NodeInfo k p a -> Maybe (PatchTarget1 p a)
357+ insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
628 insertFunc _ ni = case _nodeInfo_from ni of
358 insertFunc _ ni = case _nodeInfo_from ni of
629 From_Insert v -> Just v
359 From_Insert v -> Just v
630 From_Move k -> DMap.lookup k old
360 From_Move k -> DMap.lookup k old
631 From_Delete -> Nothing
361 From_Delete -> Nothing
632 deletions = DMap.mapMaybeWithKey deleteFunc p
362 deletions = DMap.mapMaybeWithKey deleteFunc p
633− deleteFunc :: forall a. k a -> NodeInfo k p a -> Maybe (Constant () a)
363+ deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
634 deleteFunc _ ni = case _nodeInfo_from ni of
364 deleteFunc _ ni = case _nodeInfo_from ni of
635 From_Delete -> Just $ Constant ()
365 From_Delete -> Just $ Constant ()
636 _ -> Nothing
366 _ -> Nothing
637
367
638−-- | Get the values that will be replaced, deleted, or moved if the given patch
368+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
639−-- is applied to the given 'DMap'.
640getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
369getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
641getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
370getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
642 where f _ v ni = Pair v $ _nodeInfo_to ni
371 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Data/Patch/DMapWithReset.hsdeleted−122
@@ -1,122 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE LambdaCase #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE PolyKinds #-}
6−{-# LANGUAGE Rank2Types #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE StandaloneDeriving #-}
9−{-# LANGUAGE TypeApplications #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−
13−{-# OPTIONS_GHC -Wall #-}
14−
15−-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
16−module Data.Patch.DMapWithReset where
17−
18−import Data.Patch.Class
19−
20−import Data.Dependent.Map (DMap, GCompare (..))
21−import qualified Data.Dependent.Map as DMap
22−import Data.Semigroup (Semigroup (..))
23−import Data.Constraint.Extras
24−
25−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
26−-- Insertions are represented as @'ComposeMaybe' (Just value)@,
27−-- while deletions are represented as @'ComposeMaybe' Nothing@.
28−newtype PatchDMapWithReset k p = PatchDMapWithReset { unPatchDMapWithReset :: DMap k (By p) }
29−
30−-- | Holds the information about each key: where its new value should come from,
31−-- and where its old value should go to
32−data By p a
33− = By_Insert (PatchTarget (p a)) -- ^ Insert the given value here
34− | By_Delete -- ^ Delete the existing value, if any, from here
35− | By_Patch (p a) -- ^ Patch the value here with the given patch
36−
37−instance (Semigroup (p a), Patch (p a)) => Semigroup (By p a) where
38− x@(By_Insert _) <> _ = x
39− By_Delete <> _ = By_Delete
40− By_Patch x <> By_Insert y = By_Insert (applyAlways x y)
41− By_Patch x <> By_Patch y = By_Patch (x <> y)
42− By_Patch _ <> By_Delete = By_Delete
43−
44−instance (Monoid (p a), Patch (p a)) => Monoid (By p a) where
45− mappend = (<>)
46− mempty = By_Patch mempty
47−
48−instance
49− ( GCompare k
50− , Has' Semigroup k p
51− , Has' Patch k p
52− )
53− => Semigroup (PatchDMapWithReset k p) where
54− PatchDMapWithReset xs <> PatchDMapWithReset ys = PatchDMapWithReset $ DMap.unionWithKey
55− (\k -> has' @Patch @p k
56− $ has' @Semigroup @p k
57− $ (<>)) xs ys
58−
59−instance
60− ( GCompare k
61− , Has' Semigroup k p
62− , Has' Patch k p
63− )
64− => Monoid (PatchDMapWithReset k p) where
65− mappend = (<>)
66− mempty = PatchDMapWithReset DMap.empty
67−
68−class (Patch (p a), PatchTarget (p a) ~ Patches1LocallyTarget p a) => Patches1Locally p a where
69− type Patches1LocallyTarget p :: k -> *
70−
71−data These1 f g x
72− = This1 (f x)
73− | That1 (g x)
74− | These1 (f x) (g x)
75−
76−mergeWithKey
77− :: forall k v1 v2 v.
78− (GCompare k)
79− => (forall x. k x -> v1 x -> Maybe (v x))
80− -> (forall x. k x -> v2 x -> Maybe (v x))
81− -> (forall x. k x -> v1 x -> v2 x -> Maybe (v x))
82− -> DMap k v1 -> DMap k v2 -> DMap k v
83−mergeWithKey f g fg = \xs ys -> DMap.mapMaybeWithKey onlyThat $ DMap.unionWithKey doIt (DMap.map This1 xs) (DMap.map That1 ys)
84− where
85− doIt _ (This1 xs) (That1 ys) = These1 xs ys
86− doIt _ _ _ = error "mergeWithKey misalligned keys"
87−
88− onlyThat :: forall x. k x -> These1 v1 v2 x -> Maybe (v x)
89− onlyThat k = \case
90− This1 xs -> f k xs
91− That1 ys -> g k ys
92− These1 xs ys -> fg k xs ys
93−{-# INLINE mergeWithKey #-}
94−
95−-- | Apply the insertions or deletions to a given 'DMap'.
96−instance (GCompare k, Has (Patches1Locally p) k) => PatchHet (PatchDMapWithReset k p) where
97−
98− type PatchSource (PatchDMapWithReset k p) = DMap k (Patches1LocallyTarget p)
99− type PatchTarget (PatchDMapWithReset k p) = DMap k (Patches1LocallyTarget p)
100−
101−instance (GCompare k, Has (Patches1Locally p) k) => Patch (PatchDMapWithReset k p) where
102− apply = go
103− where
104− go :: PatchDMapWithReset k p -> DMap k (Patches1LocallyTarget p) -> Maybe (DMap k (Patches1LocallyTarget p))
105− go (PatchDMapWithReset diff) old = Just $! mergeWithKey (\_ -> Just) inserts updates old diff
106− where
107− updates :: forall x. k x -> Patches1LocallyTarget p x -> By p x -> Maybe (Patches1LocallyTarget p x)
108− updates k ys = has @(Patches1Locally p) k $ \case
109− By_Insert x -> Just x
110− By_Delete -> Nothing
111− By_Patch x -> Just $ applyAlways x ys
112−
113− inserts :: forall x. k x -> By p x -> Maybe (Patches1LocallyTarget p x)
114− inserts k = has @(Patches1Locally p) k $ \case
115− By_Insert x -> Just x
116− By_Delete -> Nothing
117− By_Patch _ -> Nothing
118−
119−deriving instance (Patch (p a), Eq (p a), Eq (PatchTarget (p a))) => Eq (By p a)
120−deriving instance (Patch (p a), Show (p a), Show (PatchTarget (p a))) => Show (By p a)
121−deriving instance (Eq (DMap k (By p))) => Eq (PatchDMapWithReset k p)
122−deriving instance (Show (DMap k (By p))) => Show (PatchDMapWithReset k p)
src/Data/Patch/IntMap.hs+30−15
@@ -1,40 +1,55 @@
1−{-# LANGUAGE DeriveFoldable #-}
1+{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
3{-# LANGUAGE DeriveTraversable #-}
2{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE StandaloneDeriving #-}
7+{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
9+
6-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
10-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
7-- insert/update or delete of associations.
11-- insert/update or delete of associations.
8module Data.Patch.IntMap where
12module Data.Patch.IntMap where
9
13
14+import Control.Lens
10import Data.IntMap.Strict (IntMap)
15import Data.IntMap.Strict (IntMap)
11import qualified Data.IntMap.Strict as IntMap
16import qualified Data.IntMap.Strict as IntMap
12import Data.Maybe
17import Data.Maybe
13−import Data.Semigroup
18+import Data.Monoid.DecidablyEmpty
19+#if !MIN_VERSION_base(4,11,0)
20+import Data.Semigroup (Semigroup (..))
21+#endif
14import Data.Patch.Class
22import Data.Patch.Class
15
23
16-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
24-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
17-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
25-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
18-- and @Nothing@ means delete.
26-- and @Nothing@ means delete.
19−newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
27+newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
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
20
39
21-- | Apply the insertions or deletions to a given 'IntMap'.
40-- | Apply the insertions or deletions to a given 'IntMap'.
22−instance PatchHet (PatchIntMap a) where
23− type PatchSource (PatchIntMap a) = IntMap a
24− type PatchTarget (PatchIntMap a) = IntMap a
25instance Patch (PatchIntMap a) where
41instance Patch (PatchIntMap a) where
42+ type PatchTarget (PatchIntMap a) = IntMap a
26 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
43 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
27 let removes = IntMap.filter isNothing p
44 let removes = IntMap.filter isNothing p
28 adds = IntMap.mapMaybe id p
45 adds = IntMap.mapMaybe id p
29 in IntMap.union adds $ v `IntMap.difference` removes
46 in IntMap.union adds $ v `IntMap.difference` removes
30
47
31−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
48+instance FunctorWithIndex Int PatchIntMap
32−-- If the same key is modified by both patches, the one on the left will take
49+instance FoldableWithIndex Int PatchIntMap
33−-- precedence.
50+instance TraversableWithIndex Int PatchIntMap where
34−instance Semigroup (PatchIntMap v) where
51+ itraverse = itraversed . Indexed
35− PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
52+ itraversed = _Wrapped .> itraversed <. traversed
36− -- PatchMap is idempotent, so stimes n is id for every n
37− stimes = stimesIdempotentMonoid
38
53
39-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
54-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
40-- (that is, all inserts/updates), producing a @PatchIntMap b@.
55-- (that is, all inserts/updates), producing a @PatchIntMap b@.
@@ -44,7 +59,7 @@ mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k m
44-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
59-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
45-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
60-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
46traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
61traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
47−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
62+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
48
63
49-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
64-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
50patchIntMapNewElements :: PatchIntMap a -> [a]
65patchIntMapNewElements :: PatchIntMap a -> [a]
src/Data/Patch/Map.hs+37−22
@@ -1,34 +1,40 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TemplateHaskell #-}
2{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeFamilies #-}
10+
3-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
11-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
4-- and deletions
12-- and deletions
5module Data.Patch.Map where
13module Data.Patch.Map where
6
14
7import Data.Patch.Class
15import Data.Patch.Class
8
16
17+import Control.Lens
9import Data.Map (Map)
18import Data.Map (Map)
10import qualified Data.Map as Map
19import qualified Data.Map as Map
11import Data.Maybe
20import Data.Maybe
12−import Data.Semigroup
21+import Data.Monoid.DecidablyEmpty
22+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
13
23
14-- | A set of changes to a 'Map'. Any element may be inserted/updated or
24-- | A set of changes to a 'Map'. Any element may be inserted/updated or
15-- deleted. Insertions are represented as values wrapped in 'Just', while
25-- deleted. Insertions are represented as values wrapped in 'Just', while
16-- deletions are represented as 'Nothing's
26-- deletions are represented as 'Nothing's
17newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
27newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
18− deriving (Show, Read, Eq, Ord)
28+ deriving ( Show, Read, Eq, Ord
29+ , Foldable, Traversable
30+ , DecidablyEmpty
31+ )
19
32
20−-- | Apply the insertions or deletions to a given 'Map'.
33+-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
21−instance Ord k => PatchHet (PatchMap k v) where
34+-- Deletions are unaffected.
22− type PatchSource (PatchMap k v) = Map k v
35+deriving instance Functor (PatchMap k)
23− type PatchTarget (PatchMap k v) = Map k v
36+-- | The empty 'PatchMap' contains no insertions or deletions
24−instance Ord k => Patch (PatchMap k v) where
37+deriving instance Ord k => Monoid (PatchMap k v)
25− {-# INLINABLE apply #-}
26− apply (PatchMap 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?
27− where insertions = Map.mapMaybeWithKey (const id) p
28− deletions = Map.mapMaybeWithKey (const nothingToJust) p
29− nothingToJust = \case
30− Nothing -> Just ()
31− Just _ -> Nothing
32
38
33-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
39-- | @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
40-- If the same key is modified by both patches, the one on the left will take
@@ -38,15 +44,22 @@ instance Ord k => Semigroup (PatchMap k v) where
38 -- PatchMap is idempotent, so stimes n is id for every n
44 -- PatchMap is idempotent, so stimes n is id for every n
39 stimes = stimesIdempotentMonoid
45 stimes = stimesIdempotentMonoid
40
46
41−-- | The empty 'PatchMap' contains no insertions or deletions
47+-- | Apply the insertions or deletions to a given 'Map'.
42−instance Ord k => Monoid (PatchMap k v) where
48+instance Ord k => Patch (PatchMap k v) where
43− mempty = PatchMap mempty
49+ type PatchTarget (PatchMap k v) = Map k v
44− mappend = (<>)
50+ {-# INLINABLE apply #-}
51+ apply (PatchMap 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?
52+ where insertions = Map.mapMaybeWithKey (const id) p
53+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
54+ nothingToJust = \case
55+ Nothing -> Just ()
56+ Just _ -> Nothing
45
57
46−-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
58+instance FunctorWithIndex k (PatchMap k)
47−-- Deletions are unaffected.
59+instance FoldableWithIndex k (PatchMap k)
48−instance Functor (PatchMap k) where
60+instance TraversableWithIndex k (PatchMap k) where
49− fmap f = PatchMap . fmap (fmap f) . unPatchMap
61+ itraverse = itraversed . Indexed
62+ itraversed = _Wrapped .> itraversed <. traversed
50
63
51-- | Returns all the new elements that will be added to the 'Map'
64-- | Returns all the new elements that will be added to the 'Map'
52patchMapNewElements :: PatchMap k v -> [v]
65patchMapNewElements :: PatchMap k v -> [v]
@@ -55,3 +68,5 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
55-- | Returns all the new elements that will be added to the 'Map'
68-- | Returns all the new elements that will be added to the 'Map'
56patchMapNewElementsMap :: PatchMap k v -> Map k v
69patchMapNewElementsMap :: PatchMap k v -> Map k v
57patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
70patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71+
72+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+240−251
@@ -1,323 +1,312 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE PatternGuards #-}
8{-# LANGUAGE PatternGuards #-}
9+{-# LANGUAGE PatternSynonyms #-}
5{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE StandaloneDeriving #-}
11{-# LANGUAGE StandaloneDeriving #-}
12+{-# LANGUAGE TemplateHaskell #-}
13+{-# LANGUAGE TypeApplications #-}
7{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE UndecidableInstances #-}
15+{-# LANGUAGE ViewPatterns #-}
16+
9-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
17-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
10-- another
18-- another
11−module Data.Patch.MapWithMove where
19+module Data.Patch.MapWithMove
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
12
58
59+ -- * To
60+ , To
61+ ) where
62+
63+import Data.Coerce
64+import Data.Kind (Type)
13import Data.Patch.Class
65import Data.Patch.Class
66+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67+import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
14
68
15−import Control.Arrow
69+import Control.Lens
16−import Control.Monad.Trans.State
17−import Data.Foldable
18−import Data.Function
19import Data.List
70import Data.List
20import Data.Map (Map)
71import Data.Map (Map)
21import qualified Data.Map as Map
72import qualified Data.Map as Map
22−import Data.Maybe
73+import Data.Proxy
23−#if !MIN_VERSION_base(4,9,0)
74+#if !MIN_VERSION_base(4,11,0)
24−import Data.Semigroup (Semigroup (..), (<>))
75+import Data.Semigroup (Semigroup (..))
25#endif
76#endif
26−import Data.Monoid.DecidablyEmpty
77+import Data.Traversable (foldMapDefault)
27−import qualified Data.Set as Set
28−import Data.These (These (..))
29−import Data.Tuple
30
78
31-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
79-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
32-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
80-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
33-- and vice versa. There should never be any unpaired From/To keys.
81-- and vice versa. There should never be any unpaired From/To keys.
34−newtype PatchMapWithMove k p = PatchMapWithMove (Map k (NodeInfo k p))
82+newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
35−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithMove k p)
83+ { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
36−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithMove k p)
84+ unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
37−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithMove k p)
38−
39−-- | Holds the information about each key: where its new value should come from,
40−-- and where its old value should go to
41−data NodeInfo k p = NodeInfo
42− { _nodeInfo_from :: !(From k p)
43− -- ^ Where do we get the new value for this key?
44− , _nodeInfo_to :: !(To k)
45− -- ^ If the old value is being kept (i.e. moved rather than deleted or
46− -- replaced), where is it going?
47 }
85 }
48−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
86+ deriving ( Show, Read, Eq, Ord
49−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
87+-- Haddock cannot handle documentation here before GHC 8.6
50−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
88+ ,
51−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
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+ )
52
97
53−-- | Describe how a key's new value should be produced
98+pattern Coerce :: Coercible a b => a -> b
54−data From k p
99+pattern Coerce x <- (coerce -> x)
55− = From_Insert (PatchTarget p) -- ^ Insert the given value here
100+ where Coerce x = coerce x
56− | From_Delete -- ^ Delete the existing value, if any, from here
101+
57− | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
102+{-# COMPLETE PatchMapWithMove #-}
103+pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
104+-- | Extract the representation of the 'PatchMapWithMove' as a map of
105+-- 'NodeInfo'.
106+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
107+pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
108+
109+_PatchMapWithMove
110+ :: Iso
111+ (PatchMapWithMove k0 v0)
112+ (PatchMapWithMove k1 v1)
113+ (Map k0 (NodeInfo k0 v0))
114+ (Map k1 (NodeInfo k1 v1))
115+_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
58
116
59−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
117+instance Functor (PatchMapWithMove k) where
60−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
118+ fmap f = runIdentity . traverse (Identity . f)
61−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
62−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
63
119
64−-- | Describe where a key's old value will go. If this is 'Just', that means
120+instance Foldable (PatchMapWithMove k) where
65−-- the key's old value will be moved to the given other key; if it is 'Nothing',
121+ foldMap = foldMapDefault
66−-- that means it will be deleted.
122+
67−type To = Maybe
123+instance Traversable (PatchMapWithMove k) where
124+ traverse =
125+ _PatchMapWithMove .
126+ traverse .
127+ traverse
128+
129+instance FunctorWithIndex k (PatchMapWithMove k)
130+instance FoldableWithIndex k (PatchMapWithMove k)
131+instance TraversableWithIndex k (PatchMapWithMove k) where
132+ itraverse = itraversed . Indexed
133+ itraversed =
134+ _PatchMapWithMove .>
135+ itraversed <.
136+ traverse
68
137
69-- | Create a 'PatchMapWithMove', validating it
138-- | Create a 'PatchMapWithMove', validating it
70−patchMapWithMove
71− :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithMove k p)
139+patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
72−patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
140+patchMapWithMove = fmap PatchMapWithMove' . PM.patchMapWithPatchingMove . coerce
73− where valid = forwardLinks == backwardLinks
74− forwardLinks = Map.mapMaybe _nodeInfo_to m
75− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
76− case _nodeInfo_from p of
77− From_Move from _ -> Just (from, to)
78− _ -> Nothing
79
141
80-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
142-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
81−patchMapWithMoveInsertAll
143+patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
82− :: Map k (PatchTarget p) -> PatchMapWithMove k p
83−patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
144+patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
84− { _nodeInfo_from = From_Insert v
85− , _nodeInfo_to = Nothing
86− }
87
145
88−-- | Extract the internal representation of the 'PatchMapWithMove'
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'.
89−unPatchMapWithMove
147+insertMapKey :: k -> v -> PatchMapWithMove k v
90− :: PatchMapWithMove k p -> Map k (NodeInfo k p)
148+insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
91−unPatchMapWithMove (PatchMapWithMove p) = p
92
149
93−-- | Make a @'PatchMapWithMove' k p@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
94−insertMapKey
95− :: k -> PatchTarget p -> PatchMapWithMove k p
96−insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
97−
98−-- |Make a @'PatchMapWithMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
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:
99--
151--
100-- @
152-- @
101-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
153-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
102-- @
154-- @
103−moveMapKey
104− :: (DecidablyEmpty p, Patch p) => Ord k => k -> k -> PatchMapWithMove k p
155+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
105−moveMapKey src dst
156+moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
106− | src == dst = mempty
157+
107− | otherwise =
108− PatchMapWithMove $ Map.fromList
109− [ (dst, NodeInfo (From_Move src mempty) Nothing)
110− , (src, NodeInfo From_Delete (Just dst))
111− ]
112−
113−-- |Make a @'PatchMapWithMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
158+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
114--
159--
115-- @
160-- @
116-- let aMay = Map.lookup a map
161-- let aMay = Map.lookup a map
117-- bMay = Map.lookup b map
162-- bMay = Map.lookup b map
118−-- in maybe id (Map.insert a) (bMay `mplus` aMay)
163+-- in maybe id (Map.insert a) (bMay <> aMay)
119−-- . maybe id (Map.insert b) (aMay `mplus` bMay)
164+-- . maybe id (Map.insert b) (aMay <> bMay)
120-- . Map.delete a . Map.delete b $ map
165-- . Map.delete a . Map.delete b $ map
121-- @
166-- @
122−swapMapKey
123− :: (DecidablyEmpty p, Patch p) => Ord k => k -> k -> PatchMapWithMove k p
167+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
124−swapMapKey src dst
168+swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
125− | src == dst = mempty
126− | otherwise =
127− PatchMapWithMove $ Map.fromList
128− [ (dst, NodeInfo (From_Move src mempty) (Just src))
129− , (src, NodeInfo (From_Move dst mempty) (Just dst))
130− ]
131
169
132-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
170-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
133−deleteMapKey
134− :: k -> PatchMapWithMove k v
171+deleteMapKey :: k -> PatchMapWithMove k v
135−deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
172+deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
136
173
137-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
174-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
138--
175--
139-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
176-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
140−unsafePatchMapWithMove
141− :: Map k (NodeInfo k p) -> PatchMapWithMove k p
177+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
142−unsafePatchMapWithMove = PatchMapWithMove
178+unsafePatchMapWithMove = coerce PM.unsafePatchMapWithPatchingMove
143
179
144-- | Apply the insertions, deletions, and moves to a given 'Map'
180-- | Apply the insertions, deletions, and moves to a given 'Map'
145−instance (Ord k, Patch p) => PatchHet (PatchMapWithMove k p) where
181+instance Ord k => Patch (PatchMapWithMove k v) where
146− type PatchSource (PatchMapWithMove k p) = Map k (PatchSource p)
147− type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
182+ type PatchTarget (PatchMapWithMove k v) = Map k v
148−instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) where
149− -- TODO: return Nothing sometimes
150− -- Note: the strict application here is critical to ensuring that incremental
151− -- merges don't hold onto all their prerequisite events forever; can we make
152− -- this more robust?
153− apply (PatchMapWithMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
183+ apply (PatchMapWithMove' p) = apply p
154− where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
184+
155− From_Insert v -> Just v
156− From_Move k p -> applyAlways p <$> Map.lookup k old
157− From_Delete -> Nothing
158− deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
159− From_Delete -> Just ()
160− _ -> Nothing
161−
162−-- | Returns all the new elements that will be added to the 'Map'
185+-- | Returns all the new elements that will be added to the 'Map'.
163−patchMapWithMoveNewElements
186+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
164− :: PatchMapWithMove k p -> [PatchTarget p]
187+patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
165−patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
166
188
167-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
189-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
168−patchMapWithMoveNewElementsMap
190+patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
169− :: PatchMapWithMove k p -> Map k (PatchTarget p)
170−patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
191+patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
171− where f ni = case _nodeInfo_from ni of
172− From_Insert v -> Just v
173− From_Move _ _ -> Nothing
174− From_Delete -> Nothing
175
192
176-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
193-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
177-- its values using the given ordering function. The set keys of the 'Map' is
194-- its values using the given ordering function. The set keys of the 'Map' is
178-- not changed.
195-- not changed.
179−patchThatSortsMapWith
196+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
180− :: (Ord k, Monoid p)
181− => (PatchTarget p -> PatchTarget p -> Ordering)
182− -> Map k (PatchTarget p) -> PatchMapWithMove k p
183−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
197+patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
184− where unsorted = Map.toList m
185− sorted = sortBy (cmp `on` snd) unsorted
186− f (to, _) (from, _) = if to == from then Nothing else
187− Just (from, to)
188− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
189− g (to, _) (from, _) = if to == from then Nothing else
190− let Just movingTo = Map.lookup from reverseMapping
191− in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
192
198
193-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
199-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
194-- will produce a 'Map' with the same values as the second 'Map' but with the
200-- will produce a 'Map' with the same values as the second 'Map' but with the
195-- values sorted with the given ordering function.
201-- values sorted with the given ordering function.
196−patchThatChangesAndSortsMapWith
202+patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
197− :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
198− => (PatchTarget p -> PatchTarget p -> Ordering)
199− -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
200patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
203patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
201 where newList = Map.toList newByIndexUnsorted
204 where newList = Map.toList newByIndexUnsorted
202 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
205 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
203
206
204-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
207-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
205-- will produce the second 'Map'.
208-- will produce the second 'Map'.
206−patchThatChangesMap
209+patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
207− :: (Ord k, Ord (PatchTarget p), Monoid p)
208− => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
209−patchThatChangesMap oldByIndex newByIndex = patch
210+patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
210− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
211+ PM.patchThatChangesMap oldByIndex newByIndex
211− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
212+
212− let f k v = do
213+--
213− remainingValues <- get
214+-- NodeInfo
214− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
215+--
215− then Map.delete v remainingValues
216+
216− else Map.insert v remainingKeys remainingValues
217+-- | Holds the information about each key: where its new value should come from,
217− case Map.lookup v remainingValues of
218+-- and where its old value should go to
218− Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
219+newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
219− Just fromKs ->
220+
220− if k `Set.member` fromKs
221+deriving instance (Show k, Show p) => Show (NodeInfo k p)
221− then do
222+deriving instance (Read k, Read p) => Read (NodeInfo k p)
222− putRemainingKeys $ Set.delete k fromKs
223+deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
223− return $ NodeInfo (From_Move k mempty) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
224+deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
224− else do
225+
225− (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
226+{-# COMPLETE NodeInfo #-}
226− putRemainingKeys remainingKeys
227+pattern NodeInfo :: To k -> From k v -> NodeInfo k v
227− return $ NodeInfo (From_Move fromK mempty) $ Just undefined
228+_nodeInfo_to :: NodeInfo k v -> To k
228− Map.traverseWithKey f newByIndex
229+_nodeInfo_from :: NodeInfo k v -> From k v
229− unusedOldKeys = fold unusedValuesByValue
230+pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
230− pointlessMove k = \case
231+ PM.NodeInfo
231− From_Move k' _ | k == k' -> True
232+ { PM._nodeInfo_to = _nodeInfo_to
232− _ -> False
233+ , PM._nodeInfo_from = Coerce _nodeInfo_from
233− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
234+ }
234− then Just undefined
235+
235− else Nothing
236+_NodeInfo
236− 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
237+ :: Iso
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
237
261
238-- | Change the 'From' value of a 'NodeInfo'
262-- | Change the 'From' value of a 'NodeInfo'
239−nodeInfoMapFrom
240− :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
263+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
241−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
264+nodeInfoMapFrom f = coerce $ PM.nodeInfoMapFrom (unFrom' . f . From')
242
265
243-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
266-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
244-- 'Applicative', 'Monad', etc.) action to get the new value
267-- 'Applicative', 'Monad', etc.) action to get the new value
245nodeInfoMapMFrom
268nodeInfoMapMFrom
269+ :: Functor f
270+ => (From k v -> f (From k v))
246− :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
271+ -> NodeInfo k v -> f (NodeInfo k v)
247−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
272+nodeInfoMapMFrom f = fmap NodeInfo'
273+ . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
274+ . coerce
248
275
249-- | Set the 'To' field of a 'NodeInfo'
276-- | Set the 'To' field of a 'NodeInfo'
250−nodeInfoSetTo
251− :: To k -> NodeInfo k v -> NodeInfo k v
277+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
252−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
278+nodeInfoSetTo = coerce . PM.nodeInfoSetTo
253−
279+
254−-- |Helper data structure used for composing patches using the monoid instance.
280+--
255−data Fixup k v
281+-- From
256− = Fixup_Delete
282+--
257− | Fixup_Update (These (From k v) (To k))
283+
258−
284+-- | Describe how a key's new value should be produced
259−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
285+newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
260−instance ( Ord k
286+
261− , DecidablyEmpty p
287+{-# COMPLETE From_Insert, From_Delete, From_Move #-}
262− , Patch p
288+
263− ) => Semigroup (PatchMapWithMove k p) where
289+-- | Insert the given value here
264− PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
290+pattern From_Insert :: v -> From k v
265− where
291+pattern From_Insert v = From' (PM.From_Insert v)
266− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
292+
267− h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
293+-- | Delete the existing value, if any, from here
268− h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
294+pattern From_Delete :: From k v
269− (Just toAfter, From_Move fromBefore p)
295+pattern From_Delete = From' PM.From_Delete
270− | fromBefore == toAfter && isNull p
296+
271− -> [(toAfter, Fixup_Delete)]
297+-- | Move the value here from the given key
272− | otherwise
298+pattern From_Move :: k -> From k v
273− -> [ (toAfter, Fixup_Update (This editBefore))
299+pattern From_Move k = From' (PM.From_Move k Proxy)
274− , (fromBefore, Fixup_Update (That mToAfter))
300+
275− ]
301+bitraverseFrom
276− (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
302+ :: Applicative f
277− (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
303+ => (k0 -> f k1)
278− (Nothing, _) -> []
304+ -> (v0 -> f v1)
279− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
305+ -> From k0 v0 -> f (From k1 v1)
280− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
306+bitraverseFrom fk fv = fmap From'
281− | This x <- a, That y <- b
307+ . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
282− = Fixup_Update $ These x y
308+ . coerce
283− | That y <- a, This x <- b
309+
284− = Fixup_Update $ These x y
285− mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
286− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
287− combineNodeInfos _ nia nib = NodeInfo
288− { _nodeInfo_from = _nodeInfo_from nia
289− , _nodeInfo_to = _nodeInfo_to nib
290− }
291− applyFixup _ ni = \case
292− Fixup_Delete -> Nothing
293− Fixup_Update u -> Just $ NodeInfo
294− { _nodeInfo_from = case _nodeInfo_from ni of
295− f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
296− Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
297− Just (From_Insert v) -> From_Insert $ applyAlways p' v
298− Just From_Delete -> From_Delete
299− Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
300− _ -> error "PatchMapWithMove: fixup for non-move From"
301− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
302− }
303− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
304− getHere :: These a b -> Maybe a
305− getHere = \case
306− This a -> Just a
307− These a _ -> Just a
308− That _ -> Nothing
309− getThere :: These a b -> Maybe b
310− getThere = \case
311− This _ -> Nothing
312− These _ b -> Just b
313− That b -> Just b
314−
315−--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
316−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
317−instance ( Ord k
318− , Monoid p
319− , DecidablyEmpty p
320− , Patch p
321− ) => Monoid (PatchMapWithMove k p) where
322− mempty = PatchMapWithMove mempty
310+makeWrapped ''PatchMapWithMove
323− mappend = (<>)
311+makeWrapped ''NodeInfo
312+makeWrapped ''From
src/Data/Patch/MapWithPatchingMove.hsnew file+432
@@ -0,0 +1,432 @@
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/Patch/Patchable.hsdeleted−45
@@ -1,45 +0,0 @@
1−{-# LANGUAGE DeriveGeneric #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE LambdaCase #-}
4−{-# LANGUAGE StandaloneDeriving #-}
5−{-# LANGUAGE TypeFamilies #-}
6−
7−-- The derived instances are undecidable in the case of a pathological instance like
8−-- instance Patch x where
9−-- type PatchTarget x = Patchable x
10−{-# LANGUAGE UndecidableInstances #-}
11−
12−module Data.Patch.Patchable where
13−
14−-- import Data.Aeson
15−import GHC.Generics
16−
17−import Data.Patch
18−
19−-- | Like SemiMap/PartialMap but for anything patchable
20−data Patchable p
21− = Patchable_Patch p
22− | Patchable_Complete (PatchTarget p)
23− deriving (Generic)
24−
25−completePatchable :: Patchable p -> Maybe (PatchTarget p)
26−completePatchable = \case
27− Patchable_Complete t -> Just t
28− Patchable_Patch _ -> Nothing
29−
30−deriving instance (Eq p, Eq (PatchTarget p)) => Eq (Patchable p)
31−deriving instance (Ord p, Ord (PatchTarget p)) => Ord (Patchable p)
32−deriving instance (Show p, Show (PatchTarget p)) => Show (Patchable p)
33−deriving instance (Read p, Read (PatchTarget p)) => Read (Patchable p)
34−-- instance (ToJSON p, ToJSON (PatchTarget p)) => ToJSON (Patchable p)
35−-- instance (FromJSON p, FromJSON (PatchTarget p)) => FromJSON (Patchable p)
36−
37−instance (Monoid p, Patch p) => Monoid (Patchable p) where
38− mempty = Patchable_Patch mempty
39− mappend = (<>)
40−
41−instance (Semigroup p, Patch p) => Semigroup (Patchable p) where
42− (<>) = curry $ \case
43− (Patchable_Patch a, Patchable_Patch b) -> Patchable_Patch $ a <> b
44− (Patchable_Patch a, Patchable_Complete b) -> Patchable_Complete $ applyAlways a b
45− (Patchable_Complete a, _) -> Patchable_Complete a
src/Data/Semigroup/Additive.hsnew file+54
@@ -0,0 +1,54 @@
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+2−1
@@ -1,7 +1,7 @@
1module Main where
1module Main where
2
2
3import Control.Monad
3import Control.Monad
4−import Language.Haskell.HLint3 (hlint)
4+import Language.Haskell.HLint (hlint)
5import System.Directory
5import System.Directory
6import System.Exit (exitFailure, exitSuccess)
6import System.Exit (exitFailure, exitSuccess)
7import System.FilePath
7import System.FilePath
@@ -23,6 +23,7 @@ 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
26 ]
27 ]
27 recurseInto = and <$> sequence
28 recurseInto = and <$> sequence
28 [ fileType ==? Directory
29 [ fileType ==? Directory
test/tests.hsnew file+43
@@ -0,0 +1,43 @@
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