on 6fcf213 help sign in
.github/workflows/haskell.ymldeleted−47
since splitsnapshots
unifiedsplit
@@ -1,47 +0,0 @@
1−name: github-action
2−
3−on: [push, pull_request]
4−
5−jobs:
6− build:
7− strategy:
8− matrix:
9− ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2']
10− os: ['ubuntu-latest', 'macos-latest']
11− exclude:
12− # There are some linker warnings in 802 on darwin that
13− # cause compilation to fail
14− # See https://github.com/NixOS/nixpkgs/issues/25139
15− - ghc: '8.0.2'
16− os: 'macos-latest'
17− runs-on: ${{ matrix.os }}
18−
19− name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
20− steps:
21− - uses: actions/checkout@v2
22− - uses: actions/setup-haskell@v1
23− with:
24− ghc-version: ${{ matrix.ghc }}
25− - name: Cache
26− uses: actions/cache@v1
27− env:
28− cache-name: cache-cabal
29− with:
30− path: ~/.cabal
31− key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
32− restore-keys: |
33− ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
34− ${{ runner.os }}-${{ matrix.ghc }}-build-
35− ${{ runner.os }}-${{ matrix.ghc }}-
36− ${{ runner.os }}
37−
38− - name: Install dependencies
39− run: |
40− cabal update
41− cabal build --only-dependencies --enable-tests --enable-benchmarks
42− - name: Build
43− run: cabal build --enable-tests --enable-benchmarks all
44− - name: Run tests
45− run: cabal test --enable-tests all
46− - name: Build Docs
47− run: cabal haddock
.travis.ymlnew file+171
@@ -0,0 +1,171 @@
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.3.20190814
8+#
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
15+cache:
16+ directories:
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
28+matrix:
29+ include:
30+ - compiler: ghcjs-8.4
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32+ - compiler: ghc-8.8.1
33+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
34+ - compiler: ghc-8.6.5
35+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
36+ - compiler: ghc-8.4.4
37+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
38+ - compiler: ghc-8.2.2
39+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
40+ - compiler: ghc-8.0.2
41+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
42+before_install:
43+ - |
44+ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
45+ sudo add-apt-repository -y ppa:hvr/ghcjs;
46+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
47+ sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
48+ sudo apt-get update;
49+ sudo apt-get install $CC cabal-install-3.0 nodejs;
50+ fi
51+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
52+ - WITHCOMPILER="-w $HC"
53+ - |
54+ if echo $CC | grep -q ghcjs; then
55+ GHCJS=true
56+ HC=${HC}js
57+ WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
58+ else
59+ GHCJS=false;
60+ fi
61+ - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
62+ - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
63+ - HCPKG="$HC-pkg"
64+ - unset CC
65+ - CABAL=/opt/ghc/bin/cabal
66+ - CABALHOME=$HOME/.cabal
67+ - export PATH="$CABALHOME/bin:$PATH"
68+ - TOP=$(pwd)
69+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
70+ - echo $HCNUMVER
71+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
72+ - set -o pipefail
73+ - |
74+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
75+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
76+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
77+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
78+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
79+ echo ' if (state == "cabal") {' >> .colorful.awk
80+ echo ' print blue($0)' >> .colorful.awk
81+ echo ' } else {' >> .colorful.awk
82+ echo ' print $0' >> .colorful.awk
83+ echo ' }' >> .colorful.awk
84+ echo '}' >> .colorful.awk
85+ - cat .colorful.awk
86+ - |
87+ color_cabal_output () {
88+ awk -f $TOP/.colorful.awk
89+ }
90+ - echo text | color_cabal_output
91+install:
92+ - ${CABAL} --version
93+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
94+ - node --version
95+ - echo $GHCJS
96+ - TEST=--enable-tests
97+ - BENCH=--enable-benchmarks
98+ - BENCH=--disable-benchmarks
99+ - HEADHACKAGE=false
100+ - rm -f $CABALHOME/config
101+ - |
102+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
103+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
104+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
105+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
106+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
107+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
108+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
109+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
110+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
111+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
112+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
113+ echo "install-dirs user" >> $CABALHOME/config
114+ echo " prefix: $CABALHOME" >> $CABALHOME/config
115+ echo "repository hackage.haskell.org" >> $CABALHOME/config
116+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
117+ - cat $CABALHOME/config
118+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
119+ - travis_retry ${CABAL} v2-update -v
120+ - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
121+ # Generate cabal.project
122+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
123+ - touch cabal.project
124+ - |
125+ echo "packages: ." >> cabal.project
126+ - |
127+ - cat cabal.project || true
128+ - cat cabal.project.local || true
129+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
130+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
131+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
132+ - rm cabal.project.freeze
133+script:
134+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
135+ # Packaging...
136+ - ${CABAL} v2-sdist all | color_cabal_output
137+ # Unpacking...
138+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
139+ - cd ${DISTDIR} || false
140+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
141+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
142+ - PKGDIR_patch="$(find . -maxdepth 1 -type d -regex '.*/patch-[0-9.]*')"
143+ # Generate cabal.project
144+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
145+ - touch cabal.project
146+ - |
147+ echo "packages: ${PKGDIR_patch}" >> cabal.project
148+ - |
149+ - cat cabal.project || true
150+ - cat cabal.project.local || true
151+ # Building...
152+ # this builds all libraries and executables (without tests/benchmarks)
153+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
154+ # Building with tests and benchmarks...
155+ # build & run tests, build benchmarks
156+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
157+ # Testing...
158+ - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
159+ # cabal check...
160+ - (cd ${PKGDIR_patch} && ${CABAL} -vnormal check)
161+ # haddock...
162+ - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
163+ # Constraint sets
164+ - rm -rf cabal.project.local
165+ # Constraint set old-these
166+ - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi
167+ # Constraint set old-witherable
168+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
169+
170+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
171+# EOF
CONTRIBUTING.md+1−1
@@ -16,7 +16,7 @@ Before opening an issue, please check whether your issue has already been report
16* Describe the issue you're encountering or the suggestion you're making
16* Describe the issue you're encountering or the suggestion you're making
17* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
17* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
18* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
18* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
19−* Describe how you're building patch (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
19+* Describe how you're building patch (i.e., via patch-platform, cabal install, stack, obelisk, etc.). If you're using patch-platform or obelisk, provide the git hash of your checkout.
20
20
21## Submitting Changes
21## Submitting Changes
22
22
ChangeLog.md+6−34
@@ -2,39 +2,11 @@
2
2
3## Unreleased
3## Unreleased
4
4
5−* New dep of `data-orphans` for old GHC to get instances honestly instead of
5+* `PatchMapWithMove` supports moves with a patch.
6− via `monoidal-containers`.
7
6
8−## 0.0.5.0 - 2021-12-17
7+* `PatchDMapWithMove` supports moves with a patch.
9
8
10−* `Additive` now lives in `Data.Semigroup.Additive`, but is still reexported
9+## 0.0.2.0
11− from `Data.Patch` for compatability.
12−
13−* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
14− Care is taken to make this not a breaking change.
15− In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
16− There are complete constructor and field patterns too, and everything is
17− exported under the newtype as real constructors and fields would be.
18−
19−## 0.0.4.0 - 2021-04-20
20−
21−* Enable PolyKinds
22−
23−## 0.0.3.2 - 2020-11-06
24−
25−* Update version bounds
26−
27−## 0.0.3.1 - 2020-02-05
28−
29−* Replace `fromJust` with something easier to debug.
30−
31−## 0.0.3.0 - 2020-02-05
32−
33−* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
34−
35−* Create `DecidablyEmpty` subclass of `Monoid`.
36−
37−## 0.0.2.0 - 2020-01-17
38
10
39* Consistently provide:
11* Consistently provide:
40
12
@@ -46,16 +18,16 @@
46
18
47 for `PatchMap`, `PatchIntMap`, and `PatchMapWithMove`.
19 for `PatchMap`, `PatchIntMap`, and `PatchMapWithMove`.
48
20
49−## 0.0.1.0 - 2020-01-09
21+## 0.0.1.0
50
22
51* Support older GHCs with `split-these` flag.
23* Support older GHCs with `split-these` flag.
52
24
53* Additional instances for the `Group` class for basic types.
25* Additional instances for the `Group` class for basic types.
54
26
55−## 0.0.0.1 - 2020-01-08
27+## 0.0.0.1
56
28
57* Remove unneeded dependencies
29* Remove unneeded dependencies
58
30
59−## 0.0.0.0 - 2020-01-08
31+## 0.0.0.0
60
32
61* Extract patching functionality from Reflex.
33* Extract patching functionality from Reflex.
README.md+4−6
@@ -1,10 +1,8 @@
1−# patch
1+## Patch
2
2
3−[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/patch.svg)](https://hackage.haskell.org/package/patch) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/patch/badge)](https://matrix.hackage.haskell.org/#/package/patch) [![Travis CI](https://api.travis-ci.org/reflex-frp/patch.svg?branch=develop)](https://travis-ci.org/reflex-frp/patch) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/patch/LICENSE)
3+Infrastructure for writing patches which act on other types.
4
4
5−Data structures for describing changes to other data structures.
6−
7−A `Patch` type represents a kind of change made to a data structure.
5+A `Patch` type represents a kind of change made to a datastructure.
8
6
9```haskell
7```haskell
10class Patch p where
8class Patch p where
@@ -14,7 +12,7 @@ class Patch p where
14 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
12 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
15```
13```
16
14
17−## Patching Maps
15+### Patching Maps
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).
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).
19
17
20`PatchMap` is defined as:
18`PatchMap` is defined as:
cabal.haskell-cinew file+14
@@ -0,0 +1,14 @@
1+distribution: xenial
2+benchmarks: False
3+unconstrained: False
4+installed: -all
5+
6+-- https://github.com/haskell/cabal/issues/6106
7+install-dependencies: False
8+
9+constraint-set old-these
10+ ghc: <8.8
11+ constraints: these <1
12+
13+constraint-set old-witherable
14+ constraints: witherable <0.3.2
patch.cabal+14−34
@@ -1,9 +1,7 @@
1Name: patch
1Name: patch
2−Version: 0.0.5.0
2+Version: 0.0.2.0
3−Synopsis: Data structures for describing changes to other data structures.
3+Synopsis: Infrastructure for writing patches which act on other types.
4Description:
4Description:
5− Data structures for describing changes to other data structures.
6− .
7 In this library, a patch is something which can be applied, analogous to a
5 In this library, a patch is something which can be applied, analogous to a
8 function, and which distinguishes returning the argument it was provided from
6 function, and which distinguishes returning the argument it was provided from
9 returning something else.
7 returning something else.
@@ -14,7 +12,7 @@ Maintainer: maintainer@obsidian.systems
14Stability: Experimental
12Stability: Experimental
15Category: FRP
13Category: FRP
16Build-type: Simple
14Build-type: Simple
17−Cabal-version: >=1.10
15+Cabal-version: >=1.9.2
18homepage: https://obsidian.systems
16homepage: https://obsidian.systems
19bug-reports: https://github.com/reflex-frp/patch/issues
17bug-reports: https://github.com/reflex-frp/patch/issues
20extra-source-files:
18extra-source-files:
@@ -22,7 +20,7 @@ extra-source-files:
22 ChangeLog.md
20 ChangeLog.md
23
21
24tested-with:
22tested-with:
25− GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
23+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
26 GHCJS ==8.4
24 GHCJS ==8.4
27
25
28flag split-these
26flag split-these
@@ -32,58 +30,40 @@ flag split-these
32
30
33library
31library
34 hs-source-dirs: src
32 hs-source-dirs: src
35− default-language: Haskell2010
36− build-depends: base >= 4.9 && < 4.15
33+ build-depends: base >= 4.9 && < 4.14
37 , constraints-extras >= 0.3 && < 0.4
34 , constraints-extras >= 0.3 && < 0.4
38 , containers >= 0.6 && < 0.7
35 , containers >= 0.6 && < 0.7
39− , dependent-map >= 0.3 && < 0.5
36+ , dependent-map >= 0.3 && < 0.4
40− , dependent-sum >= 0.6 && < 0.8
37+ , dependent-sum >= 0.6 && < 0.7
41 , lens >= 4.7 && < 5
38 , lens >= 4.7 && < 5
42 , semigroupoids >= 4.0 && < 6
39 , semigroupoids >= 4.0 && < 6
43 , transformers >= 0.5.6.0 && < 0.6
40 , transformers >= 0.5.6.0 && < 0.6
44− , witherable >= 0.3 && < 0.5
41+ , witherable >= 0.3 && < 0.3.2
45
42
46− if impl(ghc < 8.6) -- really, if base < 8.12
43+ exposed-modules: Control.Category.DecidablyEmpty
47− build-depends: base-orphans >= 0.8 && < 0.9
48−
49− exposed-modules: Data.Functor.Misc
44+ , Data.Functor.Misc
50 , Data.Monoid.DecidablyEmpty
45 , Data.Monoid.DecidablyEmpty
51 , Data.Patch
46 , Data.Patch
52 , Data.Patch.Class
47 , Data.Patch.Class
53 , Data.Patch.DMap
48 , Data.Patch.DMap
54 , Data.Patch.DMapWithMove
49 , Data.Patch.DMapWithMove
50+ , Data.Patch.DMapWithMove.By
55 , Data.Patch.IntMap
51 , Data.Patch.IntMap
56 , Data.Patch.Map
52 , Data.Patch.Map
57 , Data.Patch.MapWithMove
53 , Data.Patch.MapWithMove
58− , Data.Patch.MapWithPatchingMove
59− , Data.Semigroup.Additive
60
54
61 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
55 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
62− default-extensions: PolyKinds
63
56
64 if flag(split-these)
57 if flag(split-these)
65− build-depends: these >= 1 && <1.2
58+ build-depends: these >= 1 && <1.1
59+ , these-lens >= 1 && <1.1
66 , semialign >=1 && <1.2
60 , semialign >=1 && <1.2
67 , monoidal-containers >= 0.6 && < 0.7
61 , monoidal-containers >= 0.6 && < 0.7
68 else
62 else
69 build-depends: these >= 0.4 && <0.9
63 build-depends: these >= 0.4 && <0.9
70 , monoidal-containers == 0.4.0.0
64 , monoidal-containers == 0.4.0.0
71
65
72−test-suite tests
73− default-language: Haskell2010
74− type: exitcode-stdio-1.0
75− main-is: tests.hs
76− hs-source-dirs: test
77− build-depends: base
78− , patch
79− , containers
80− , hedgehog
81− , HUnit
82− if impl(ghcjs)
83− buildable: False
84−
85test-suite hlint
66test-suite hlint
86− default-language: Haskell2010
87 type: exitcode-stdio-1.0
67 type: exitcode-stdio-1.0
88 main-is: hlint.hs
68 main-is: hlint.hs
89 hs-source-dirs: test
69 hs-source-dirs: test
@@ -91,7 +71,7 @@ test-suite hlint
91 , directory
71 , directory
92 , filepath
72 , filepath
93 , filemanip
73 , filemanip
94− , hlint (< 2.1 || >= 2.2.2) && < 3.3
74+ , hlint < 2.1 || >= 2.2.2
95 if impl(ghcjs)
75 if impl(ghcjs)
96 buildable: False
76 buildable: False
97
77
src/Control/Category/DecidablyEmpty.hsnew file+9
@@ -0,0 +1,9 @@
1+{-# LANGUAGE TypeOperators #-}
2+-- TODO upstream somwhere else?
3+module Control.Category.DecidablyEmpty where
4+
5+import Control.Category
6+import Data.Type.Equality
7+
8+class Category c => DecidablyEmpty c where
9+ isId :: c a b -> Maybe (a :~: b)
src/Data/Functor/Misc.hs+31−7
@@ -1,11 +1,12 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE DeriveTraversable #-}
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
@@ -16,6 +17,8 @@
16module Data.Functor.Misc
17module Data.Functor.Misc
17 ( -- * Const2
18 ( -- * Const2
18 Const2 (..)
19 Const2 (..)
20+ , Proxy3 (..)
21+ , First2 (..)
19 , unConst2
22 , unConst2
20 , dmapToMap
23 , dmapToMap
21 , dmapToIntMap
24 , dmapToIntMap
@@ -36,6 +39,7 @@ module Data.Functor.Misc
36 , ComposeMaybe (..)
39 , ComposeMaybe (..)
37 ) where
40 ) where
38
41
42+import qualified Control.Category as Cat
39import Control.Applicative ((<$>))
43import Control.Applicative ((<$>))
40import Data.Dependent.Map (DMap)
44import Data.Dependent.Map (DMap)
41import qualified Data.Dependent.Map as DMap
45import qualified Data.Dependent.Map as DMap
@@ -47,7 +51,8 @@ import Data.IntMap (IntMap)
47import qualified Data.IntMap as IntMap
51import qualified Data.IntMap as IntMap
48import Data.Map (Map)
52import Data.Map (Map)
49import qualified Data.Map as Map
53import qualified Data.Map as Map
54+import qualified Data.Semigroupoid as Cat
50−import Data.Some (Some, mkSome)
55+import Data.Some (Some(Some))
51import Data.These
56import Data.These
52import Data.Type.Equality ((:~:)(Refl))
57import Data.Type.Equality ((:~:)(Refl))
53import Data.Typeable hiding (Refl)
58import Data.Typeable hiding (Refl)
@@ -62,15 +67,15 @@ data Const2 :: * -> x -> x -> * where
62 Const2 :: k -> Const2 k v v
67 Const2 :: k -> Const2 k v v
63 deriving (Typeable)
68 deriving (Typeable)
64
69
65−-- | Extract the value from a Const2
66−unConst2 :: Const2 k v v' -> k
67−unConst2 (Const2 k) = k
68−
69deriving instance Eq k => Eq (Const2 k v v')
70deriving instance Eq k => Eq (Const2 k v v')
70deriving instance Ord k => Ord (Const2 k v v')
71deriving instance Ord k => Ord (Const2 k v v')
71deriving instance Show k => Show (Const2 k v v')
72deriving instance Show k => Show (Const2 k v v')
72deriving instance Read k => Read (Const2 k v v)
73deriving instance Read k => Read (Const2 k v v)
73
74
75+-- | Extract the value from a Const2
76+unConst2 :: Const2 k v v' -> k
77+unConst2 (Const2 k) = k
78+
74instance Show k => GShow (Const2 k v) where
79instance Show k => GShow (Const2 k v) where
75 gshowsPrec n x@(Const2 _) = showsPrec n x
80 gshowsPrec n x@(Const2 _) = showsPrec n x
76
81
@@ -86,6 +91,25 @@ instance Ord k => GCompare (Const2 k v) where
86 EQ -> GEQ
91 EQ -> GEQ
87 GT -> GGT
92 GT -> GGT
88
93
94+data Proxy3 :: x -> y -> z -> * where
95+ Proxy3 :: Proxy3 vx vy vz
96+ deriving ( Show, Read, Eq, Ord
97+ , Functor, Foldable, Traversable
98+ , Typeable
99+ )
100+
101+instance Cat.Category (Proxy3 x) where
102+ id = Proxy3
103+ ~Proxy3 . ~Proxy3 = Proxy3
104+
105+newtype First2 (t :: k -> *) (a :: k) (b :: k) = First2 (t b)
106+ deriving ( Show, Read, Eq, Ord
107+ , Functor, Foldable, Traversable
108+ )
109+
110+instance Cat.Semigroupoid (First2 x) where
111+ First2 x `o` ~(First2 _) = First2 x
112+
89-- | Convert a 'DMap' to a regular 'Map'
113-- | Convert a 'DMap' to a regular 'Map'
90dmapToMap :: DMap (Const2 k v) Identity -> Map k v
114dmapToMap :: DMap (Const2 k v) Identity -> Map k v
91dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
115dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
@@ -116,7 +140,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
116-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
140-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
117-- the keys, using a function to remove the wrapping 'Functor'
141-- the keys, using a function to remove the wrapping 'Functor'
118weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
142weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
119−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
143+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
120
144
121--------------------------------------------------------------------------------
145--------------------------------------------------------------------------------
122-- WrapArg
146-- WrapArg
src/Data/Monoid/DecidablyEmpty.hs+1−111
@@ -1,115 +1,5 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DefaultSignatures #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE StandaloneDeriving #-}
5−{-# LANGUAGE TypeOperators #-}
6−
7-- TODO upstream somwhere else?
1-- TODO upstream somwhere else?
8module Data.Monoid.DecidablyEmpty where
2module Data.Monoid.DecidablyEmpty where
9
3
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.
38class Monoid a => DecidablyEmpty a where
4class Monoid a => DecidablyEmpty a where
39− isEmpty :: a -> Bool
5+ isNull :: 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+23−10
@@ -18,12 +18,11 @@ 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,11,0)
21+#if !MIN_VERSION_base(4,9,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
27import Data.Patch.Class as X
26import Data.Patch.Class as X
28import Data.Patch.DMap as X hiding (getDeletions)
27import Data.Patch.DMap as X hiding (getDeletions)
29import Data.Patch.DMapWithMove as X
28import Data.Patch.DMapWithMove as X
@@ -46,25 +45,34 @@ class (Semigroup q, Monoid q) => Group q where
46 (~~) :: q -> q -> q
45 (~~) :: q -> q -> q
47 r ~~ s = r <> negateG s
46 r ~~ s = r <> negateG s
48
47
48+-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
49+class Semigroup q => Additive q where
50+
49-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
51-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
50newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
52newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
51
53
52−instance Additive p => Patch (AdditivePatch p) where
54+instance Additive p => PatchHet (AdditivePatch p) where
55+ type PatchSource (AdditivePatch p) = p
53 type PatchTarget (AdditivePatch p) = p
56 type PatchTarget (AdditivePatch p) = p
57+instance Additive p => Patch (AdditivePatch p) where
54 apply (AdditivePatch p) q = Just $ p <> q
58 apply (AdditivePatch p) q = Just $ p <> q
55
59
56instance (Ord k, Group q) => Group (MonoidalMap k q) where
60instance (Ord k, Group q) => Group (MonoidalMap k q) where
57 negateG = fmap negateG
61 negateG = fmap negateG
58
62
63+instance (Ord k, Additive q) => Additive (MonoidalMap k q)
64+
59-- | Trivial group.
65-- | Trivial group.
60instance Group () where
66instance Group () where
61− negateG _ = ()
67+ negateG ~() = ()
62− _ ~~ _ = ()
68+ ~() ~~ ~() = ()
69+instance Additive ()
63
70
64-- | Product group. A Pair of groups gives rise to a group
71-- | Product group. A Pair of groups gives rise to a group
65instance (Group a, Group b) => Group (a, b) where
72instance (Group a, Group b) => Group (a, b) where
66 negateG (a, b) = (negateG a, negateG b)
73 negateG (a, b) = (negateG a, negateG b)
67 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
74 (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
75+instance (Additive a, Additive b) => Additive (a, b)
68
76
69-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
77-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
70-- Base does not define Monoid (Compose f g a) so this is the best we can
78-- Base does not define Monoid (Compose f g a) so this is the best we can
@@ -72,24 +80,29 @@ instance (Group a, Group b) => Group (a, b) where
72instance Group (f (g a)) => Group ((f :.: g) a) where
80instance Group (f (g a)) => Group ((f :.: g) a) where
73 negateG (Comp1 xs) = Comp1 (negateG xs)
81 negateG (Comp1 xs) = Comp1 (negateG xs)
74 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
82 Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
83+instance Additive (f (g a)) => Additive ((f :.: g) a)
75
84
76-- | Product of groups, Functor style.
85-- | Product of groups, Functor style.
77instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
86instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
78 negateG (a :*: b) = negateG a :*: negateG b
87 negateG (a :*: b) = negateG a :*: negateG b
79 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
88 (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
89+instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
80
90
81-- | Trivial group, Functor style
91-- | Trivial group, Functor style
82instance Group (Proxy x) where
92instance Group (Proxy x) where
83− negateG _ = Proxy
93+ negateG ~Proxy = Proxy
84− _ ~~ _ = Proxy
94+ ~Proxy ~~ ~Proxy = Proxy
95+instance Additive (Proxy x)
85
96
86-- | Const lifts groups into a functor.
97-- | Const lifts groups into a functor.
87deriving instance Group a => Group (Const a x)
98deriving instance Group a => Group (Const a x)
88−
99+instance Additive a => Additive (Const a x)
89−-- | Identity lifts groups pointwise (at only one point)
100+-- | Ideitnty lifts groups pointwise (at only one point)
90deriving instance Group a => Group (Identity a)
101deriving instance Group a => Group (Identity a)
102+instance Additive a => Additive (Identity a)
91
103
92-- | Functions lift groups pointwise.
104-- | Functions lift groups pointwise.
93instance Group b => Group (a -> b) where
105instance Group b => Group (a -> b) where
94 negateG f = negateG . f
106 negateG f = negateG . f
95 (~~) = liftA2 (~~)
107 (~~) = liftA2 (~~)
108+instance Additive b => Additive (a -> b)
src/Data/Patch/Class.hs+132−11
@@ -1,23 +1,53 @@
1−{-# LANGUAGE CPP #-}
1+{-# LANGUAGE DefaultSignatures #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
2+{-# LANGUAGE DerivingStrategies #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE PolyKinds #-}
3{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeOperators #-}
4-- | The interface for types which represent changes made to other types
9-- | The interface for types which represent changes made to other types
5module Data.Patch.Class where
10module Data.Patch.Class where
6
11
12+import qualified Data.Semigroupoid as Cat
7import Data.Functor.Identity
13import Data.Functor.Identity
8−import Data.Kind (Type)
14+import Data.Functor.Misc
9import Data.Maybe
15import Data.Maybe
10−#if !MIN_VERSION_base(4,11,0)
11import Data.Semigroup (Semigroup(..))
16import Data.Semigroup (Semigroup(..))
12−#endif
13import Data.Proxy
17import Data.Proxy
18+import Data.Type.Equality ((:~:) (..))
19+
20+class PatchHet p where
21+ type PatchSource p :: *
22+ type PatchTarget p :: *
23+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
24+ -- 'Nothing'.
25+ applyHet
26+ :: p
27+ -> PatchSource p
28+ -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
29+ default applyHet
30+ :: Patch p
31+ => p
32+ -> PatchSource p
33+ -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
34+ applyHet p a = case apply p a of
35+ Nothing -> Left Refl
36+ Just a' -> Right a'
37+
38+-- | Apply a 'PatchHet'; if it does nothing, return the original value
39+applyAlwaysHet :: PatchHet p => p -> PatchSource p -> PatchTarget p
40+applyAlwaysHet p t = case applyHet p t of
41+ Left Refl -> t
42+ Right t' -> t'
14
43
15-- | A 'Patch' type represents a kind of change made to a datastructure.
44-- | A 'Patch' type represents a kind of change made to a datastructure.
16--
45--
17-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
46-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
18-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
47-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
48+class ( PatchHet p
49+ , PatchSource p ~ PatchTarget p
19−class Patch p where
50+ ) => Patch p where
20− type PatchTarget p :: Type
21 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
51 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
22 -- 'Nothing'.
52 -- 'Nothing'.
23 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
53 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -27,19 +57,110 @@ applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
27applyAlways p t = fromMaybe t $ apply p t
57applyAlways p t = fromMaybe t $ apply p t
28
58
29-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
59-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
30−instance Patch (Identity a) where
60+instance PatchHet (Identity a) where
61+ type PatchSource (Identity a) = a
31 type PatchTarget (Identity a) = a
62 type PatchTarget (Identity a) = a
63+instance Patch (Identity a) where
32 apply (Identity a) _ = Just a
64 apply (Identity a) _ = Just a
33
65
34−-- | 'Proxy' can be used as a 'Patch' that does nothing.
66+-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
35−instance forall (a :: Type). Patch (Proxy a) where
67+instance PatchHet (Proxy (a :: *)) where
68+ type PatchSource (Proxy a) = a
36 type PatchTarget (Proxy a) = a
69 type PatchTarget (Proxy a) = a
70+instance Patch (Proxy (a :: *)) where
37 apply ~Proxy _ = Nothing
71 apply ~Proxy _ = Nothing
38
72
39-- | Like '(.)', but composes functions that return patches rather than
73-- | Like '(.)', but composes functions that return patches rather than
40-- functions that return new values. The Semigroup instance for patches must
74-- functions that return new values. The Semigroup instance for patches must
41-- apply patches right-to-left, like '(.)'.
75-- apply patches right-to-left, like '(.)'.
42−composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
76+composePatchFunctions
77+ :: (Patch p, Semigroup p)
78+ => (PatchTarget p -> p)
79+ -> (PatchTarget p -> p)
80+ -> PatchTarget p -> p
43composePatchFunctions g f a =
81composePatchFunctions g f a =
44 let fp = f a
82 let fp = f a
45 in g (applyAlways fp a) <> fp
83 in g (applyAlways fp a) <> fp
84+
85+
86+class PatchHet2Base (p :: k -> k -> *) where
87+ type PatchSource1 p :: k -> *
88+ type PatchTarget1 p :: k -> *
89+
90+class ( PatchHet2Base p
91+ , PatchHet (p from to)
92+ , PatchSource1 p from ~ PatchSource (p from to)
93+ , PatchTarget1 p to ~ PatchTarget (p from to)
94+ ) => PatchHet2Locally (p :: k -> k -> *) from to where
95+instance ( PatchHet2Base p
96+ , PatchHet (p from to)
97+ , PatchSource1 p from ~ PatchSource (p from to)
98+ , PatchTarget1 p to ~ PatchTarget (p from to)
99+ ) => PatchHet2Locally (p :: k -> k -> *) from to where
100+
101+applyHet2Locally
102+ :: PatchHet2Locally p from to
103+ => p from to
104+ -> PatchSource1 p from
105+ -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
106+applyHet2Locally = applyHet
107+
108+applyAlwaysHet2Locally
109+ :: PatchHet2Locally p from to
110+ => p from to
111+ -> PatchSource1 p from
112+ -> PatchTarget1 p to
113+applyAlwaysHet2Locally = applyAlwaysHet
114+
115+-- TODO once we can use quantified constraints, perhaps combine PatchHet2Base and
116+-- PatchHet2Locally, or at least get rid of this.
117+class PatchHet2Base p => PatchHet2 (p :: k -> k -> *) where
118+ applyHet2
119+ :: p from to
120+ -> PatchSource1 p from
121+ -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
122+
123+applyAlwaysHet2
124+ :: PatchHet2 p
125+ => p from to
126+ -> PatchSource1 p from
127+ -> PatchTarget1 p to
128+applyAlwaysHet2 p t = case applyHet2 p t of
129+ Left Refl -> t
130+ Right t' -> t'
131+
132+-- | Connect the classes without quanitified constraints
133+newtype ProjectLocal p from to = ProjectLocal { unProjectLocal :: p from to }
134+ deriving newtype Cat.Semigroupoid
135+
136+instance PatchHet2 p => PatchHet (ProjectLocal p from to) where
137+ type PatchSource (ProjectLocal p from to) = PatchSource1 p from
138+ type PatchTarget (ProjectLocal p from to) = PatchTarget1 p to
139+ applyHet (ProjectLocal p) = applyHet2 p
140+
141+instance PatchHet2 p => PatchHet2Base (ProjectLocal p) where
142+ type PatchSource1 (ProjectLocal p) = PatchSource1 p
143+ type PatchTarget1 (ProjectLocal p) = PatchTarget1 p
144+
145+class ( PatchHet2Base p
146+ , PatchSource1 p ~ PatchTarget1 p
147+ ) => Patch2 p
148+instance ( PatchHet2Base p
149+ , PatchSource1 p ~ PatchTarget1 p
150+ ) => Patch2 p
151+
152+-- | 'First2' can be used as a 'Patch' that always fully replaces the value
153+instance PatchHet (First2 (t :: k -> *) (from :: k) (to :: k)) where
154+ type PatchSource (First2 t from to) = t from
155+ type PatchTarget (First2 t from to) = t to
156+ applyHet (First2 val) _ = Right val
157+
158+-- | 'Proxy3' can be used as a 'Patch' that always does nothing
159+instance PatchHet (Proxy3 (t :: k -> *) (a :: k) (a :: k)) where
160+ type PatchSource (Proxy3 t a a) = t a
161+ type PatchTarget (Proxy3 t a a) = t a
162+ applyHet ~Proxy3 _ = Left Refl
163+
164+instance PatchHet2Base (Proxy3 (t :: k -> *) :: k -> k -> *) where
165+ type PatchSource1 (Proxy3 t) = t
166+ type PatchTarget1 (Proxy3 t) = t
src/Data/Patch/DMap.hs+4−13
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE PolyKinds #-}
3{-# LANGUAGE PolyKinds #-}
@@ -6,7 +5,6 @@
6{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE StandaloneDeriving #-}
6{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
9−
10-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
8-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
11module Data.Patch.DMap where
9module Data.Patch.DMap where
12
10
@@ -14,18 +12,13 @@ import Data.Patch.Class
14import Data.Patch.IntMap
12import Data.Patch.IntMap
15import Data.Patch.Map
13import Data.Patch.Map
16
14
17−import Data.Dependent.Map (DMap)
15+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
18−import Data.Dependent.Sum (DSum (..))
19−import Data.GADT.Compare (GCompare (..))
20import qualified Data.Dependent.Map as DMap
16import qualified Data.Dependent.Map as DMap
21import Data.Functor.Constant
17import Data.Functor.Constant
22import Data.Functor.Misc
18import Data.Functor.Misc
23import qualified Data.IntMap as IntMap
19import qualified Data.IntMap as IntMap
24import qualified Data.Map as Map
20import qualified Data.Map as Map
25−import Data.Monoid.DecidablyEmpty
26−#if !MIN_VERSION_base(4,11,0)
27import Data.Semigroup (Semigroup (..))
21import Data.Semigroup (Semigroup (..))
28−#endif
29import Data.Some (Some)
22import Data.Some (Some)
30
23
31-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
24-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
@@ -37,13 +30,11 @@ deriving instance GCompare k => Semigroup (PatchDMap k v)
37
30
38deriving instance GCompare k => Monoid (PatchDMap k v)
31deriving instance GCompare k => Monoid (PatchDMap k v)
39
32
40−-- It won't let me derive for some reason
41−instance GCompare k => DecidablyEmpty (PatchDMap k v) where
42− isEmpty (PatchDMap m) = DMap.null m
43−
44-- | Apply the insertions or deletions to a given 'DMap'.
33-- | Apply the insertions or deletions to a given 'DMap'.
45−instance GCompare k => Patch (PatchDMap k v) where
34+instance GCompare k => PatchHet (PatchDMap k v) where
35+ type PatchSource (PatchDMap k v) = DMap k v
46 type PatchTarget (PatchDMap k v) = DMap k v
36 type PatchTarget (PatchDMap k v) = DMap k v
37+instance GCompare k => Patch (PatchDMap k v) where
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?
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?
48 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
39 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
49 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
40 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
src/Data/Patch/DMapWithMove.hs+407−161
@@ -1,108 +1,222 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE InstanceSigs #-}
5{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE PatternGuards #-}
9{-# LANGUAGE PatternGuards #-}
8{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE Rank2Types #-}
11{-# LANGUAGE Rank2Types #-}
10{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE StandaloneDeriving #-}
11{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
16+{-# LANGUAGE UndecidableSuperClasses #-}
13
17
14-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
18-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
15-- move values between keys.
19-- move values between keys.
16module Data.Patch.DMapWithMove where
20module Data.Patch.DMapWithMove where
17
21
18−import Data.Patch.Class
22+import qualified Control.Category as Cat
19−import Data.Patch.MapWithMove (PatchMapWithMove (..))
23+--import qualified Control.Category.DecidablyEmpty as Cat
20−import qualified Data.Patch.MapWithMove as MapWithMove
21−
22−import Data.Constraint.Extras
24+import Data.Constraint.Extras (Has')
23import Data.Dependent.Map (DMap)
25import Data.Dependent.Map (DMap)
24import Data.Dependent.Sum (DSum (..))
26import Data.Dependent.Sum (DSum (..))
25import qualified Data.Dependent.Map as DMap
27import qualified Data.Dependent.Map as DMap
26−import Data.Functor.Constant
28+import Data.Functor.Constant (Constant (..))
27import Data.Functor.Misc
29import Data.Functor.Misc
30+ ( Const2 (..), Proxy3 (..)
31+ , weakenDMapWith
32+ , dmapToMapWith
33+ )
28−import Data.Functor.Product
34+import Data.Functor.Product (Product (..))
29import Data.GADT.Compare (GEq (..), GCompare (..))
35import Data.GADT.Compare (GEq (..), GCompare (..))
30−import Data.GADT.Show (GShow, gshow)
36+import Data.GADT.Show (GRead, GShow, gshow)
31import qualified Data.Map as Map
37import qualified Data.Map as Map
32import Data.Maybe
38import Data.Maybe
33−import Data.Monoid.DecidablyEmpty
39+import Data.Semigroup (Semigroup (..), (<>))
34−#if !MIN_VERSION_base(4,11,0)
40+import Data.Semigroupoid as Cat
41+import Data.Some (Some(Some))
42+import Data.Proxy (Proxy (..))
35−import Data.Semigroup (Semigroup (..))
43+import Data.These (These (..))
36−#endif
44+
37−import Data.Some (Some, mkSome)
45+import Data.Patch.Class
38−import Data.These
46+ ( Patch (..), PatchHet (..)
39−
47+ , PatchHet2 (..), PatchSource1, PatchTarget1
48+ , applyAlwaysHet2
49+ )
50+import Data.Patch.MapWithMove (PatchMapWithMove (..))
51+import qualified Data.Patch.MapWithMove as MapWithMove
52+
53+-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
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
54+-- which describes how it will be changed by the patch and connects move sources
41−-- destinations.
55+-- and destinations.
42--
56--
43-- Invariants:
57-- Invariants:
44--
58--
45−-- * A key should not move to itself.
59+-- * A key should not move to itself.
60+--
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)@)
61+-- * A move should always be represented with both the destination key (as a
62+-- 'From_Move') and the source key (as a @'ComposeMaybe' ('Just'
63+-- destination)@)
47newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
64newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
48
65
49−-- It won't let me derive for some reason
66+--deriving instance ( GShow k
50−instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
67+-- , HasZip Show k p
51− isEmpty (PatchDMapWithMove m) = DMap.null m
68+-- , Has' Show k (PatchTarget1 p)
52−
69+-- ) => Show (PatchDMapWithMove k p)
70+--deriving instance ( GRead k
71+-- , HasZip Read k p
72+-- , Has' Read k (PatchTarget1 p)
73+-- ) => Read (PatchDMapWithMove k p)
74+--deriving instance ( GEq k
75+-- , HasZip Eq k p
76+-- , Has' Eq k (PatchTarget1 p)
77+-- ) => Eq (PatchDMapWithMove k p)
78+--deriving instance ( GCompare k
79+-- , HasZip Ord k p
80+-- , Has' Ord k (PatchTarget1 p)
81+-- ) => Ord (PatchDMapWithMove k p)
82+
83+-- | Structure which represents what changes apply to a particular key.
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
84+-- @_nodeInfo_from@ specifies what happens to this key, and in particular 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.
85+-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
86+-- key the current key is moving to if involved in a move.
55−data NodeInfo k v a = NodeInfo
87+data NodeInfo k p a = NodeInfo
56− { _nodeInfo_from :: !(From k v a)
88+ { _nodeInfo_from :: !(From k p a)
57− -- ^Change applying to the current key, be it an insert, move, or delete.
89+ -- ^ Change applying to the current key, be it an insert, move, or delete.
58− , _nodeInfo_to :: !(To k a)
90+ , _nodeInfo_to :: !(To k p a)
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'.
91+ -- ^ Where this key is moving to, if involved in a move. Should only be
92+ -- @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
60 }
93 }
94+
61− deriving (Show)
95+--deriving instance ( Show (k a)
62−
96+-- , Show (p a a)
97+-- , Show (PatchTarget1 p a)
98+-- ) => Show (NodeInfo k p a)
99+--deriving instance ( Read (k a)
100+-- , Read (p a a)
101+-- , Read (PatchTarget1 p a)
102+-- ) => Read (NodeInfo k p a)
103+--deriving instance ( Eq (k a)
104+-- , Eq (p a a)
105+-- , Eq (PatchTarget1 p a)
106+-- ) => Eq (NodeInfo k p a)
107+--deriving instance ( Ord (k a)
108+-- , Ord (p a a)
109+-- , Ord (PatchTarget1 p a)
110+-- ) => Ord (NodeInfo k p a)
111+
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
112+-- | Structure describing a particular change to a key, be it inserting a new
113+-- key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting
64−-- key (@From_Delete@), or moving a key (@From_Move@).
114+-- a key (@From_Delete@), or moving a key (@From_Move@).
115+--
116+-- This type isn't used directly as the from field patch, but is instead wrapped
117+-- in an existential. However, it is nice to be able to reason about this in
118+-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
65−data From (k :: a -> *) (v :: a -> *) :: a -> * where
119+data From (k :: a -> *) (p :: a -> a -> *) :: a -> * where
66− -- |Insert a new or update an existing key with the given value @v a@
120+ -- | Insert a new or update an existing key with the given value @PatchTarget1
121+ -- p a@
67− From_Insert :: v a -> From k v a
122+ From_Insert :: PatchTarget1 p to -> From k p to
68− -- |Delete the existing key
123+ -- | Delete the existing key
69− From_Delete :: From k v a
124+ From_Delete :: From k p to
125+ -- | Move the value from the given key @k a@ to this key. The source key
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@,
126+ -- should also have an entry in the patch giving the current key as
71− -- usually but not necessarily with @From_Delete@.
127+ -- @_nodeInfo_to@, usually but not necessarily with @From_Delete@.
72− From_Move :: !(k a) -> From k v a
128+ From_Move :: !(DSum k (Flip p to)) -> From k p to
73− deriving (Show, Read, Eq, Ord)
129+
74−
130+deriving instance ( Show (k a), GShow k
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
131+ , Has' Show k (Flip p a)
76−-- operation.
132+ , Show (PatchTarget1 p a)
77−type To = ComposeMaybe
133+ ) => Show (From k p a)
134+deriving instance ( Read (k a), GRead k
135+ , Has' Read k (Flip p a)
136+ , Read (PatchTarget1 p a)
137+ ) => Read (From k p a)
138+deriving instance ( GEq k
139+ , Has' Eq k (Flip p a)
140+ , Eq (PatchTarget1 p a)
141+ ) => Eq (From k p a)
142+deriving instance ( GCompare k
143+ , Has' Eq k (Flip p a) -- superclass bug
144+ , Has' Ord k (Flip p a)
145+ , Ord (PatchTarget1 p a)
146+ ) => Ord (From k p a)
147+
148+newtype Flip p to from = Flip (p from to)
149+
150+instance Cat.Category p => Cat.Category (Flip (p :: k -> k -> *)) where
151+ id = Flip Cat.id
152+ Flip y . Flip x = Flip $ x Cat.. y
153+
154+-- | The "to" part of a 'NodeInfo'. Rather than be built out of @From@ like @From@
155+-- is, we store just the information necessary to compose a @To@ and @From@ like
156+-- @oLocal@ composes two @From@s.
157+data To (k :: a -> *) (p :: a -> a -> *) :: a -> * where
158+ -- | Delete or leave in place
159+ To_NonMove :: To k p from
160+ -- | Move the value from the given key @k a@ to this key. The target key
161+ -- should also have an entry in the patch giving the current key in
162+ -- @_nodeInfo_from@, usually but not necessarily with @To_Delete@.
163+ To_Move :: !(DSum k (p from)) -> To k p from
164+
165+deriving instance ( Show (k a), GShow k
166+ , Has' Show k (p a)
167+ , Show (PatchTarget1 p a)
168+ ) => Show (To k p a)
169+deriving instance ( Read (k a), GRead k
170+ , Has' Read k (p a)
171+ , Read (PatchTarget1 p a)
172+ ) => Read (To k p a)
173+deriving instance ( GEq k
174+ , Has' Eq k (p a)
175+ , Eq (PatchTarget1 p a)
176+ ) => Eq (To k p a)
177+deriving instance ( GCompare k
178+ , Has' Eq k (p a) -- superclass bug
179+ , Has' Ord k (p a)
180+ , Ord (PatchTarget1 p a)
181+ ) => Ord (To k p a)
78
182
79-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
183-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
80−validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
184+validPatchDMapWithMove
185+ :: forall k v
186+ . (GCompare k, GShow k)
187+ => DMap k (NodeInfo k v)
188+ -> Bool
81validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
189validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
82
190
83-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
191-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
84−validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
192+validationErrorsForPatchDMapWithMove
193+ :: forall k v
194+ . (GCompare k, GShow k)
195+ => DMap k (NodeInfo k v)
196+ -> [String]
85validationErrorsForPatchDMapWithMove m =
197validationErrorsForPatchDMapWithMove m =
86 noSelfMoves <> movesBalanced
198 noSelfMoves <> movesBalanced
87 where
199 where
88 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
200 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
201+ selfMove (dst :=> NodeInfo (From_Move (src :=> _)) _)
89− selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
202+ | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
203+ selfMove (src :=> NodeInfo _ (To_Move (dst :=> _)))
90− selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
204+ | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
91 selfMove _ = Nothing
205 selfMove _ = Nothing
92 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
206 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
93− unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
207+ unbalancedMove (dst :=> NodeInfo (From_Move (src :=> _)) _) =
94 case DMap.lookup src m of
208 case DMap.lookup src m of
95 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
209 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
96− Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
210+ Just (NodeInfo _ (To_Move (dst' :=> _))) ->
97 if isNothing (dst' `geq` dst)
211 if isNothing (dst' `geq` dst)
98 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
212 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
99 else Nothing
213 else Nothing
100 _ ->
214 _ ->
101 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
215 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
102− unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
216+ unbalancedMove (src :=> NodeInfo _ (To_Move (dst :=> _))) =
103 case DMap.lookup dst m of
217 case DMap.lookup dst m of
104 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
218 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
105− Just (NodeInfo (From_Move src') _) ->
219+ Just (NodeInfo (From_Move (src' :=> _)) _) ->
106 if isNothing (src' `geq` src)
220 if isNothing (src' `geq` src)
107 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
221 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
108 else Nothing
222 else Nothing
@@ -111,35 +225,49 @@ validationErrorsForPatchDMapWithMove m =
111 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
225 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
112 unbalancedMove _ = Nothing
226 unbalancedMove _ = Nothing
113
227
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−
118-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
228-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
119data Pair1 f g a = Pair1 (f a) (g a)
229data Pair1 f g a = Pair1 (f a) (g a)
120
230
121-- |Helper data structure used for composing patches using the monoid instance.
231-- |Helper data structure used for composing patches using the monoid instance.
122−data Fixup k v a
232+data Fixup k p a
123 = Fixup_Delete
233 = Fixup_Delete
124− | Fixup_Update (These (From k v a) (To k a))
234+ | Fixup_Update (These (From k p a) (To k p a))
125−
235+
126−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
236+-- | Compose patches having the same effect as applying the patches in turn:
237+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
238+instance ( GCompare k
239+ , Cat.Semigroupoid p
240+ -- , Cat.DecidablyEmpty p
241+ , PatchHet2 p
242+ , PatchSource1 p ~ PatchTarget1 p
127−instance GCompare k => Semigroup (PatchDMapWithMove k v) where
243+ ) => Semigroup (PatchDMapWithMove k p) where
128 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
244 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
129 where
245 where
246+ connections :: [DSum k (Pair1 (To k p) (From k p))]
247+ connections = DMap.toList $ DMap.intersectionWithKey
130− connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
248+ (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b))
249+ ma
250+ mb
131− h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
251+ h :: DSum k (Pair1 (To k p) (From k p)) -> [DSum k (Fixup k p)]
132− h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
252+ h ((_ :: k between) :=> Pair1 editAfter editBefore) = case (editAfter, editBefore) of
133− (Just toAfter, From_Move fromBefore)
253+ (To_Move ((toAfter :: k after) :=> p1), From_Move ((fromBefore :: k before) :=> Flip p0)) ->
134− | isJust $ fromBefore `geq` toAfter
254+ --case toAfter `geq` fromBefore of
255+ -- Just Refl | Just Refl <- Cat.isId p0 ->
135− -> [toAfter :=> Fixup_Delete]
256+ -- [ toAfter :=> Fixup_Delete ]
136− | otherwise
257+ -- _ ->
137− -> [ toAfter :=> Fixup_Update (This editBefore)
258+ [ toAfter :=> Fixup_Update (This $ From_Move $ fromBefore :=> (Flip $ p1 `o` p0))
138− , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
259+ , fromBefore :=> Fixup_Update (That $ To_Move $ toAfter :=> (p1 `o` p0))
139− ]
260+ ]
261+ (To_NonMove, From_Move (fromBefore :=> _)) ->
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
262+ -- The item is destroyed in the second patch, so indicate that it is
263+ -- destroyed in the source map
264+ [fromBefore :=> Fixup_Update (That To_NonMove)]
265+ (To_Move (toAfter :=> p), From_Insert val) ->
266+ [toAfter :=> Fixup_Update (This $ From_Insert $ applyAlwaysHet2 p val)]
267+ (To_Move (toAfter :=> _), From_Delete) ->
141− (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
268+ [toAfter :=> Fixup_Update (This From_Delete)]
142− (Nothing, _) -> []
269+ (To_NonMove, _) ->
270+ []
143 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
271 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
144 mergeFixups _ (Fixup_Update a) (Fixup_Update b)
272 mergeFixups _ (Fixup_Update a) (Fixup_Update b)
145 | This x <- a, That y <- b
273 | This x <- a, That y <- b
@@ -170,13 +298,19 @@ instance GCompare k => Semigroup (PatchDMapWithMove k v) where
170 These _ b -> Just b
298 These _ b -> Just b
171 That b -> Just b
299 That b -> Just b
172
300
173−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
301+-- | Compose patches having the same effect as applying the patches in turn:
302+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
303+instance ( GCompare k
304+ , Cat.Semigroupoid p
305+ -- , DecidablyEmpty p
306+ , PatchHet2 p
307+ , PatchSource1 p ~ PatchTarget1 p
174−instance GCompare k => Monoid (PatchDMapWithMove k v) where
308+ ) => Monoid (PatchDMapWithMove k p) where
175 mempty = PatchDMapWithMove mempty
309 mempty = PatchDMapWithMove mempty
176 mappend = (<>)
310 mappend = (<>)
177
311
178{-
312{-
179−mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
313+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
180PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
314PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
181 where
315 where
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
316 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
@@ -197,44 +331,51 @@ PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWith
197 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
331 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
198-}
332-}
199
333
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'.
334+-- | Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or
335+-- updating a value @PatchTarget1 p a@ to the given key @k a@, like
336+-- 'DMap.insert'.
201−insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
337+insertDMapKey :: k a -> PatchTarget1 p a -> PatchDMapWithMove k p
202insertDMapKey k v =
338insertDMapKey k v =
203− PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
339+ PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) To_NonMove
204
340
205−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to:
341+-- | Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value
342+-- from the first key @k a@ to the second key @k a@, equivalent to:
206--
343--
207-- @
344-- @
208-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
345-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
209-- @
346-- @
347+moveDMapKey
348+ :: GCompare k
210−moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
349+ => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
211moveDMapKey src dst = case src `geq` dst of
350moveDMapKey src dst = case src `geq` dst of
212 Nothing -> PatchDMapWithMove $ DMap.fromList
351 Nothing -> PatchDMapWithMove $ DMap.fromList
213− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
352+ [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) To_NonMove
214− , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
353+ , src :=> NodeInfo From_Delete (To_Move $ dst :=> Proxy3)
215 ]
354 ]
216− Just _ -> mempty
355+ Just _ -> PatchDMapWithMove DMap.empty
217
356
218−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
357+-- | Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys
358+-- in the mapping, equivalent to:
219--
359--
220-- @
360-- @
221-- let aMay = DMap.lookup a dmap
361-- let aMay = DMap.lookup a dmap
222-- bMay = DMap.lookup b dmap
362-- bMay = DMap.lookup b dmap
223−-- in maybe id (DMap.insert a) (bMay <> aMay)
363+-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
224−-- . maybe id (DMap.insert b) (aMay <> bMay)
364+-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
225-- . DMap.delete a . DMap.delete b $ dmap
365-- . DMap.delete a . DMap.delete b $ dmap
226-- @
366-- @
227−swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
367+swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
228swapDMapKey src dst = case src `geq` dst of
368swapDMapKey src dst = case src `geq` dst of
229 Nothing -> PatchDMapWithMove $ DMap.fromList
369 Nothing -> PatchDMapWithMove $ DMap.fromList
230− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
370+ [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) (To_Move $ src :=> Proxy3)
231− , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
371+ , src :=> NodeInfo (From_Move (dst :=> Flip Proxy3)) (To_Move $ dst :=> Proxy3)
232 ]
372 ]
233− Just _ -> mempty
373+ Just _ -> PatchDMapWithMove DMap.empty
234
374
235−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'DMap.delete'.
375+-- | Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in
376+-- the mapping, equivalent to 'DMap.delete'.
236deleteDMapKey :: k a -> PatchDMapWithMove k v
377deleteDMapKey :: k a -> PatchDMapWithMove k v
237−deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
378+deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete To_NonMove
238
379
239{-
380{-
240k1, k2 :: Const2 Int () ()
381k1, k2 :: Const2 Int () ()
@@ -260,17 +401,20 @@ dst (PatchDMapWithMove x _) = x
260src (PatchDMapWithMove _ x) = x
401src (PatchDMapWithMove _ x) = x
261-}
402-}
262
403
263−-- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'.
404+-- | Extract the 'DMap' representing the patch changes from the
405+-- 'PatchDMapWithMove'.
264unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
406unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
265unPatchDMapWithMove (PatchDMapWithMove p) = p
407unPatchDMapWithMove (PatchDMapWithMove p) = p
266
408
267−-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove', without checking any invariants.
409+-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove',
410+-- without checking any invariants.
268--
411--
269-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
412-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
270unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
413unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
271unsafePatchDMapWithMove = PatchDMapWithMove
414unsafePatchDMapWithMove = PatchDMapWithMove
272
415
416+-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while
273−-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while checking invariants. If the invariants are satisfied, @Right p@ is returned
417+-- checking invariants. If the invariants are satisfied, @Right p@ is returned
274-- otherwise @Left errors@.
418-- otherwise @Left errors@.
275patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
419patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
276patchDMapWithMove dm =
420patchDMapWithMove dm =
@@ -278,94 +422,196 @@ patchDMapWithMove dm =
278 [] -> Right $ unsafePatchDMapWithMove dm
422 [] -> Right $ unsafePatchDMapWithMove dm
279 errs -> Left errs
423 errs -> Left errs
280
424
281−-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
425+-- | Map a natural transform @v -> v'@ over the given patch, transforming
426+-- @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
282−mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
427+mapPatchDMapWithMove
428+ :: forall k p p'
429+ . (forall a. PatchTarget1 p a -> PatchTarget1 p' a)
430+ -> (forall from to. p from to -> p' from to)
431+ -> PatchDMapWithMove k p
432+ -> PatchDMapWithMove k p'
283−mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
433+mapPatchDMapWithMove f g (PatchDMapWithMove m) =
434+ PatchDMapWithMove $ DMap.map (\ni -> NodeInfo
284− DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
435+ { _nodeInfo_from = h $ _nodeInfo_from ni
436+ , _nodeInfo_to = j $ _nodeInfo_to ni
437+ }) m
285− where g :: forall a. From k v a -> From k v' a
438+ where h :: forall a. From k p a -> From k p' a
286− g = \case
439+ h = \case
287 From_Insert v -> From_Insert $ f v
440 From_Insert v -> From_Insert $ f v
288 From_Delete -> From_Delete
441 From_Delete -> From_Delete
289− From_Move k -> From_Move k
442+ From_Move (k :=> Flip p) -> From_Move $ k :=> Flip (g p)
290−
443+ j :: forall a. To k p a -> To k p' a
444+ j = \case
445+ To_NonMove -> To_NonMove
446+ To_Move (k :=> p) -> To_Move $ k :=> g p
447+
448+-- | Traverse an effectful function @forall a. PatchTarget1 p a -> m (v ' a)@
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')@.
449+-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
450+-- ('PatchDMapWithMove' k v')@.
292−traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
451+traversePatchDMapWithMove
452+ :: forall m k p p'
453+ . Applicative m
454+ => (forall a. PatchTarget1 p a -> m (PatchTarget1 p' a))
455+ -> (forall from to. p from to -> m (p' from to))
456+ -> PatchDMapWithMove k p
457+ -> m (PatchDMapWithMove k p')
293−traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
458+traversePatchDMapWithMove f g = traversePatchDMapWithMoveWithKey
294−
459+ (\_ -> f)
460+ (\_ _ -> g)
461+
462+-- | Map an effectful function @forall a. k a -> PatchTarget1 p a -> m (v ' a)@
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')@.
463+-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
464+-- ('PatchDMapWithMove' k v')@.
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')
465+traversePatchDMapWithMoveWithKey
466+ :: forall m k p p'
467+ . Applicative m
468+ => (forall a. k a -> PatchTarget1 p a -> m (PatchTarget1 p' a))
469+ -> (forall from to. k from -> k to -> p from to -> m (p' from to))
470+ -> PatchDMapWithMove k p
471+ -> m (PatchDMapWithMove k p')
297−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
472+traversePatchDMapWithMoveWithKey f g (PatchDMapWithMove m) =
473+ PatchDMapWithMove <$> DMap.traverseWithKey (\k ni -> NodeInfo
474+ <$> h k (_nodeInfo_from ni)
475+ <*> j k (_nodeInfo_to ni)) m
298− where g :: forall a. k a -> From k v a -> m (From k v' a)
476+ where h :: forall a. k a -> From k p a -> m (From k p' a)
299− g k = \case
477+ h k = \case
300 From_Insert v -> From_Insert <$> f k v
478 From_Insert v -> From_Insert <$> f k v
301 From_Delete -> pure From_Delete
479 From_Delete -> pure From_Delete
302− From_Move fromKey -> pure $ From_Move fromKey
480+ From_Move (fromKey :=> Flip p) -> From_Move . (fromKey :=>) . Flip <$> g fromKey k p
303−
481+ j :: forall a. k a -> To k p a -> m (To k p' a)
482+ j k = \case
483+ To_NonMove -> pure To_NonMove
484+ To_Move (toKey :=> p) -> To_Move . (toKey :=>) <$> g k toKey p
485+
304−-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
486+-- | Map a function which transforms @'From k PatchTarget1 p a@ into a @'From k
487+-- PatchTarget1 p' a@ over a @'NodeInfo' k PatchTarget1 p a@.
305−nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
488+nodeInfoMapFrom
489+ :: (From k p a -> From k p' a)
490+ -> (To k p a -> To k p' a)
491+ -> NodeInfo k p a
492+ -> NodeInfo k p' a
493+nodeInfoMapFrom f g ni = NodeInfo
306−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
494+ { _nodeInfo_from = f $ _nodeInfo_from ni
307−
495+ , _nodeInfo_to = g $ _nodeInfo_to ni
496+ }
497+
308−-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
498+-- | Map an effectful function which transforms @'From k PatchTarget1 p a@ into
499+-- a @f ('From k PatchTarget1 p' a)@ over a @'NodeInfo' k PatchTarget1 p a@.
309−nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
500+nodeInfoMapFromM
501+ :: Applicative f
502+ => (From k p a -> f (From k p' a))
503+ -> (To k p a -> f (To k p' a))
504+ -> NodeInfo k p a
505+ -> f (NodeInfo k p' a)
506+nodeInfoMapFromM f g ni = NodeInfo
310−nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
507+ <$> f (_nodeInfo_from ni)
311−
508+ <*> g (_nodeInfo_to ni)
509+
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
510+-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
511+-- from @k a@ to @'Some' k@ and applying a given weakening function
313−-- values.
512+-- @PatchTarget1 p a -> v'@ to values.
314−weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
513+weakenPatchDMapWithMoveWith
514+ :: forall k p p'
515+ . (forall a. PatchTarget1 p a -> PatchTarget p')
516+ -> (forall from to. p from to -> p')
517+ -> PatchDMapWithMove k p
518+ -> PatchMapWithMove (Some k) p'
315−weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
519+weakenPatchDMapWithMoveWith f g (PatchDMapWithMove m) =
520+ PatchMapWithMove $ weakenDMapWith h m
316− where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
521+ where h :: forall a. NodeInfo k p a -> MapWithMove.NodeInfo (Some k) p'
317− g ni = MapWithMove.NodeInfo
522+ h ni = MapWithMove.NodeInfo
318 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
523 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
319 From_Insert v -> MapWithMove.From_Insert $ f v
524 From_Insert v -> MapWithMove.From_Insert $ f v
320 From_Delete -> MapWithMove.From_Delete
525 From_Delete -> MapWithMove.From_Delete
321− From_Move k -> MapWithMove.From_Move $ mkSome k
526+ From_Move (k :=> Flip p) -> MapWithMove.From_Move (Some k) $ g p
322− , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
527+ , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
528+ To_NonMove -> MapWithMove.To_NonMove
529+ To_Move (k :=> p) -> MapWithMove.To_Move (Some k) $ g p
323 }
530 }
324
531
532+-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
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
533+-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
326−-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
534+-- dependency in the typing and all points are already @a@, hence the function
535+-- to map each value to @v'@ is not higher rank.
327−patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v'
536+patchDMapWithMoveToPatchMapWithMoveWith
537+ :: forall k p p' a
538+ . (PatchTarget1 p a -> PatchTarget p')
539+ -> (p a a -> p')
540+ -> PatchDMapWithMove (Const2 k a) p
541+ -> PatchMapWithMove k p'
328−patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
542+patchDMapWithMoveToPatchMapWithMoveWith f g (PatchDMapWithMove m) =
543+ PatchMapWithMove $ dmapToMapWith h m
329− where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
544+ where h :: NodeInfo (Const2 k a) p a -> MapWithMove.NodeInfo k p'
330− g ni = MapWithMove.NodeInfo
545+ h ni = MapWithMove.NodeInfo
331 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
546 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
332 From_Insert v -> MapWithMove.From_Insert $ f v
547 From_Insert v -> MapWithMove.From_Insert $ f v
333 From_Delete -> MapWithMove.From_Delete
548 From_Delete -> MapWithMove.From_Delete
334− From_Move (Const2 k) -> MapWithMove.From_Move k
549+ From_Move (Const2 k :=> Flip p) -> MapWithMove.From_Move k $ g p
335− , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
550+ , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
551+ To_NonMove -> MapWithMove.To_NonMove
552+ To_Move (Const2 k :=> p) -> MapWithMove.To_Move k $ g p
336 }
553 }
337
554
338−-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
555+-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
556+-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
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@.
557+-- dependently typed one but which always has a constant key type represented by
558+-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
340−-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
559+-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
341−const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v'
560+const2PatchDMapWithMoveWith
561+ :: forall k v v' a
562+ . (v -> v' a)
563+ -> PatchMapWithMove k (Proxy v)
564+ -> PatchDMapWithMove (Const2 k a) (Proxy3 v')
565+const2PatchDMapWithMoveWith f (PatchMapWithMove p) =
342−const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
566+ PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
567+ where g :: (k, MapWithMove.NodeInfo k (Proxy v))
343− where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
568+ -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
344 g (k, ni) = Const2 k :=> NodeInfo
569 g (k, ni) = Const2 k :=> NodeInfo
345 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
570 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
346 MapWithMove.From_Insert v -> From_Insert $ f v
571 MapWithMove.From_Insert v -> From_Insert $ f v
347 MapWithMove.From_Delete -> From_Delete
572 MapWithMove.From_Delete -> From_Delete
348− MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
573+ MapWithMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
349− , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
574+ , _nodeInfo_to = case MapWithMove._nodeInfo_to ni of
575+ MapWithMove.To_NonMove -> To_NonMove
576+ MapWithMove.To_Move toKey Proxy -> To_Move $ Const2 toKey :=> Proxy3
350 }
577 }
351
578
352-- | Apply the insertions, deletions, and moves to a given 'DMap'.
579-- | Apply the insertions, deletions, and moves to a given 'DMap'.
580+instance ( GCompare k
581+ , PatchHet2 p
582+ , PatchSource1 p ~ PatchTarget1 p
353−instance GCompare k => Patch (PatchDMapWithMove k v) where
583+ ) => PatchHet (PatchDMapWithMove k p) where
584+ type PatchSource (PatchDMapWithMove k p) = DMap k (PatchSource1 p)
354− type PatchTarget (PatchDMapWithMove k v) = DMap k v
585+ type PatchTarget (PatchDMapWithMove k p) = DMap k (PatchTarget1 p)
586+
587+instance ( GCompare k
588+ , PatchHet2 p
589+ , PatchSource1 p ~ PatchTarget1 p
590+ ) => Patch (PatchDMapWithMove k p) where
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?
591+ apply (PatchDMapWithMove m) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions)
592+ -- TODO: return Nothing sometimes --Note: the strict application here is
593+ -- critical to ensuring that incremental merges don't hold onto all their
594+ -- prerequisite events forever; can we make this more robust?
356− where insertions = DMap.mapMaybeWithKey insertFunc p
595+ where insertions = DMap.mapMaybeWithKey insertFunc m
357− insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
596+ insertFunc :: forall a. k a -> NodeInfo k p a -> Maybe (PatchTarget1 p a)
358 insertFunc _ ni = case _nodeInfo_from ni of
597 insertFunc _ ni = case _nodeInfo_from ni of
359 From_Insert v -> Just v
598 From_Insert v -> Just v
360− From_Move k -> DMap.lookup k old
599+ From_Move (k :=> Flip p) -> applyAlwaysHet2 p <$> DMap.lookup k old
361 From_Delete -> Nothing
600 From_Delete -> Nothing
362− deletions = DMap.mapMaybeWithKey deleteFunc p
601+ deletions = DMap.mapMaybeWithKey deleteFunc m
363− deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
602+ deleteFunc :: forall a. k a -> NodeInfo k p a -> Maybe (Constant () a)
364 deleteFunc _ ni = case _nodeInfo_from ni of
603 deleteFunc _ ni = case _nodeInfo_from ni of
365 From_Delete -> Just $ Constant ()
604 From_Delete -> Just $ Constant ()
366 _ -> Nothing
605 _ -> Nothing
367
606
368−-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
607+-- | Get the values that will be replaced, deleted, or moved if the given patch
608+-- is applied to the given 'DMap'.
369−getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
609+getDeletionsAndMoves
610+ :: ( GCompare k
611+ , PatchSource1 p ~ PatchTarget1 p
612+ )
613+ => PatchDMapWithMove k p
614+ -> DMap k (PatchSource1 p)
615+ -> DMap k (Product (PatchTarget1 p) (To k p))
370getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
616getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
371 where f _ v ni = Pair v $ _nodeInfo_to ni
617 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Data/Patch/DMapWithMove/By.hsnew file+89
@@ -0,0 +1,89 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE InstanceSigs #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE Rank2Types #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE StandaloneDeriving #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE UndecidableInstances #-}
15+{-# LANGUAGE UndecidableSuperClasses #-}
16+
17+module Data.Patch.DMapWithMove.By where
18+
19+import Data.Semigroupoid as Cat
20+
21+import Data.Patch.Class
22+
23+-- | Structure describing a particular change to a key, be it inserting a new
24+-- key (@By_Insert@), updating an existing key (@By_Insert@ again), deleting
25+-- a key (@By_Delete@), or moving a key (@By_Move@).
26+--
27+-- This type isn't used directly as the from field patch, but is instead wrapped
28+-- in an existential. However, it is nice to be able to reason about this in
29+-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
30+data By (k :: a -> *) (p :: a -> a -> *) :: a -> a -> * where
31+ -- | Insert a new or update an existing key with the given value @PatchTarget1
32+ -- p a@
33+ By_Insert :: PatchTarget1 p to -> By k p from to
34+ -- | Delete the existing key
35+ By_Delete :: By k p from to
36+ -- | Move the value from the given key @k a@ to this key. The source key
37+ -- should also have an entry in the patch giving the current key as
38+ -- @_nodeInfo_to@, usually but not necessarily with @By_Delete@.
39+ By_Move :: !(k from) -> p from to -> By k p from to
40+
41+deriving instance ( Show (k from), Show (k to)
42+ , Show (p from to)
43+ , Show (PatchTarget1 p to)
44+ ) => Show (By k p from to)
45+deriving instance ( Read (k from), Read (k to)
46+ , Read (p from to)
47+ , Read (PatchTarget1 p to)
48+ ) => Read (By k p from to)
49+deriving instance ( Eq (k from), Eq (k to)
50+ , Eq (p from to)
51+ , Eq (PatchTarget1 p to)
52+ ) => Eq (By k p from to)
53+deriving instance ( Ord (k from), Ord (k to)
54+ , Ord (p from to)
55+ , Ord (PatchTarget1 p to)
56+ ) => Ord (By k p from to)
57+
58+mapByPatch
59+ :: PatchTarget1 p0 ~ PatchTarget1 p1
60+ => (p0 from to -> p1 from to)
61+ -> By k p0 from to
62+ -> By k p1 from to
63+mapByPatch f = \case
64+ By_Insert v -> By_Insert v
65+ By_Delete -> By_Delete
66+ By_Move k p -> By_Move k $ f p
67+
68+-- | Compose patches having the same effect as applying the patches in turn:
69+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
70+instance ( PatchSource1 p ~ PatchTarget1 p
71+ , Cat.Semigroupoid p
72+ , PatchHet2 p
73+ ) => Cat.Semigroupoid (By k p) where
74+ o p0 p1 = mapByPatch unProjectLocal $
75+ oLocal (mapByPatch ProjectLocal p0) (mapByPatch ProjectLocal p1)
76+
77+oLocal
78+ :: ( PatchSource1 p ~ PatchTarget1 p
79+ , PatchHet2Locally p between after
80+ , Cat.Semigroupoid p
81+ )
82+ => By k p between after
83+ -> By k p before between
84+ -> By k p before after
85+By_Insert new `oLocal` _ = By_Insert new
86+By_Delete `oLocal` _ = By_Delete
87+By_Move _ x `oLocal` By_Insert y = By_Insert $ applyAlwaysHet2Locally x y
88+By_Move _ x `oLocal` By_Move src y = By_Move src $ x `o` y
89+By_Move _ _ `oLocal` By_Delete = By_Delete
src/Data/Patch/IntMap.hs+10−11
@@ -1,9 +1,7 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveTraversable #-}
1{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE TemplateHaskell #-}
5{-# LANGUAGE TemplateHaskell #-}
8{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
9
7
@@ -15,10 +13,7 @@ import Control.Lens
15import Data.IntMap.Strict (IntMap)
13import Data.IntMap.Strict (IntMap)
16import qualified Data.IntMap.Strict as IntMap
14import qualified Data.IntMap.Strict as IntMap
17import Data.Maybe
15import Data.Maybe
18−import Data.Monoid.DecidablyEmpty
16+import Data.Semigroup
19−#if !MIN_VERSION_base(4,11,0)
20−import Data.Semigroup (Semigroup (..))
21−#endif
22import Data.Patch.Class
17import Data.Patch.Class
23
18
24-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
19-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
@@ -26,20 +21,24 @@ import Data.Patch.Class
26-- and @Nothing@ means delete.
21-- and @Nothing@ means delete.
27newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
22newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
28 deriving ( Show, Read, Eq, Ord
23 deriving ( Show, Read, Eq, Ord
29− , Functor, Foldable, Traversable
24+ , Functor, Foldable, Traversable, Monoid
30− , Monoid, DecidablyEmpty
31 )
25 )
32
26
33-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
27-- | @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
28-- If the same key is modified by both patches, the one on the left will take
35-- precedence.
29-- precedence.
36−deriving instance Semigroup (PatchIntMap v)
30+instance Semigroup (PatchIntMap v) where
31+ PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
32+ -- PatchMap is idempotent, so stimes n is id for every n
33+ stimes = stimesIdempotentMonoid
37
34
38makeWrapped ''PatchIntMap
35makeWrapped ''PatchIntMap
39
36
40-- | Apply the insertions or deletions to a given 'IntMap'.
37-- | Apply the insertions or deletions to a given 'IntMap'.
41−instance Patch (PatchIntMap a) where
38+instance PatchHet (PatchIntMap a) where
39+ type PatchSource (PatchIntMap a) = IntMap a
42 type PatchTarget (PatchIntMap a) = IntMap a
40 type PatchTarget (PatchIntMap a) = IntMap a
41+instance Patch (PatchIntMap a) where
43 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
42 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
44 let removes = IntMap.filter isNothing p
43 let removes = IntMap.filter isNothing p
45 adds = IntMap.mapMaybe id p
44 adds = IntMap.mapMaybe id p
@@ -59,7 +58,7 @@ mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k m
59-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
58-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
60-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
59-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
61traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
60traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
62−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
61+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
63
62
64-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
63-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
65patchIntMapNewElements :: PatchIntMap a -> [a]
64patchIntMapNewElements :: PatchIntMap a -> [a]
src/Data/Patch/Map.hs+11−10
@@ -1,7 +1,5 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveTraversable #-}
1{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE StandaloneDeriving #-}
5{-# LANGUAGE StandaloneDeriving #-}
@@ -18,8 +16,7 @@ import Control.Lens
18import Data.Map (Map)
16import Data.Map (Map)
19import qualified Data.Map as Map
17import qualified Data.Map as Map
20import Data.Maybe
18import Data.Maybe
21−import Data.Monoid.DecidablyEmpty
19+import Data.Semigroup
22−import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
23
20
24-- | A set of changes to a 'Map'. Any element may be inserted/updated or
21-- | A set of changes to a 'Map'. Any element may be inserted/updated or
25-- deleted. Insertions are represented as values wrapped in 'Just', while
22-- deleted. Insertions are represented as values wrapped in 'Just', while
@@ -27,14 +24,11 @@ import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
27newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
24newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
28 deriving ( Show, Read, Eq, Ord
25 deriving ( Show, Read, Eq, Ord
29 , Foldable, Traversable
26 , Foldable, Traversable
30− , DecidablyEmpty
31 )
27 )
32
28
33-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
29-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
34-- Deletions are unaffected.
30-- Deletions are unaffected.
35deriving instance Functor (PatchMap k)
31deriving instance Functor (PatchMap k)
36−-- | The empty 'PatchMap' contains no insertions or deletions
37−deriving instance Ord k => Monoid (PatchMap k v)
38
32
39-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
33-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
40-- If the same key is modified by both patches, the one on the left will take
34-- If the same key is modified by both patches, the one on the left will take
@@ -44,9 +38,13 @@ instance Ord k => Semigroup (PatchMap k v) where
44 -- PatchMap is idempotent, so stimes n is id for every n
38 -- PatchMap is idempotent, so stimes n is id for every n
45 stimes = stimesIdempotentMonoid
39 stimes = stimesIdempotentMonoid
46
40
41+makeWrapped ''PatchMap
42+
47-- | Apply the insertions or deletions to a given 'Map'.
43-- | Apply the insertions or deletions to a given 'Map'.
48−instance Ord k => Patch (PatchMap k v) where
44+instance Ord k => PatchHet (PatchMap k v) where
45+ type PatchSource (PatchMap k v) = Map k v
49 type PatchTarget (PatchMap k v) = Map k v
46 type PatchTarget (PatchMap k v) = Map k v
47+instance Ord k => Patch (PatchMap k v) where
50 {-# INLINABLE apply #-}
48 {-# 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?
49 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
50 where insertions = Map.mapMaybeWithKey (const id) p
@@ -61,6 +59,11 @@ instance TraversableWithIndex k (PatchMap k) where
61 itraverse = itraversed . Indexed
59 itraverse = itraversed . Indexed
62 itraversed = _Wrapped .> itraversed <. traversed
60 itraversed = _Wrapped .> itraversed <. traversed
63
61
62+-- | The empty 'PatchMap' contains no insertions or deletions
63+instance Ord k => Monoid (PatchMap k v) where
64+ mempty = PatchMap mempty
65+ mappend = (<>)
66+
64-- | Returns all the new elements that will be added to the 'Map'
67-- | Returns all the new elements that will be added to the 'Map'
65patchMapNewElements :: PatchMap k v -> [v]
68patchMapNewElements :: PatchMap k v -> [v]
66patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
69patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
@@ -68,5 +71,3 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
68-- | Returns all the new elements that will be added to the 'Map'
71-- | Returns all the new elements that will be added to the 'Map'
69patchMapNewElementsMap :: PatchMap k v -> Map k v
72patchMapNewElementsMap :: PatchMap k v -> Map k v
70patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
73patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71−
72−makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+274−236
@@ -2,311 +2,349 @@
2{-# LANGUAGE DeriveTraversable #-}
2{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE PatternGuards #-}
7{-# LANGUAGE PatternGuards #-}
9−{-# LANGUAGE PatternSynonyms #-}
10{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE StandaloneDeriving #-}
9{-# LANGUAGE StandaloneDeriving #-}
12{-# LANGUAGE TemplateHaskell #-}
10{-# LANGUAGE TemplateHaskell #-}
13{-# LANGUAGE TypeApplications #-}
11{-# LANGUAGE TypeApplications #-}
14{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE ViewPatterns #-}
13+{-# LANGUAGE UndecidableInstances #-}
16
14
17-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
18-- another
16-- another
19−module Data.Patch.MapWithMove
17+module Data.Patch.MapWithMove where
20− ( PatchMapWithMove
21− ( PatchMapWithMove
22− , unPatchMapWithMove
23− , ..
24− )
25− , patchMapWithMove
26− , patchMapWithMoveInsertAll
27− , insertMapKey
28− , moveMapKey
29− , swapMapKey
30− , deleteMapKey
31− , unsafePatchMapWithMove
32− , patchMapWithMoveNewElements
33− , patchMapWithMoveNewElementsMap
34− , patchThatSortsMapWith
35− , patchThatChangesAndSortsMapWith
36− , patchThatChangesMap
37
18
38− -- * Node Info
39− , NodeInfo
40− ( NodeInfo
41− , _nodeInfo_to
42− , _nodeInfo_from
43− , ..
44− )
45− , bitraverseNodeInfo
46− , nodeInfoMapFrom
47− , nodeInfoMapMFrom
48− , nodeInfoSetTo
49−
50− -- * From
51− , From
52− ( From_Insert
53− , From_Delete
54− , From_Move
55− , ..
56− )
57− , bitraverseFrom
58−
59− -- * To
60− , To
61− ) where
62−
63−import Data.Coerce
64−import Data.Kind (Type)
65import Data.Patch.Class
19import Data.Patch.Class
66−import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67−import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
68
20
69−import Control.Lens
21+import Control.Arrow
22+import Control.Lens hiding (from, to)
23+import Control.Monad.Trans.State
24+import Data.Foldable
25+import Data.Function
70import Data.List
26import Data.List
71import Data.Map (Map)
27import Data.Map (Map)
72import qualified Data.Map as Map
28import qualified Data.Map as Map
73−import Data.Proxy
29+import Data.Maybe
74−#if !MIN_VERSION_base(4,11,0)
30+#if !MIN_VERSION_base(4,9,0)
75−import Data.Semigroup (Semigroup (..))
31+import Data.Semigroup (Semigroup (..), (<>))
76#endif
32#endif
77−import Data.Traversable (foldMapDefault)
33+import Data.Monoid.DecidablyEmpty
34+import qualified Data.Set as Set
35+import Data.These (These (..))
36+import Data.Tuple
78
37
79-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
38-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
80-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
39-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
81-- and vice versa. There should never be any unpaired From/To keys.
40-- and vice versa. There should never be any unpaired From/To keys.
82−newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
41+newtype PatchMapWithMove k p = PatchMapWithMove
83− { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
42+ { -- | Extract the internal representation of the 'PatchMapWithMove'
84− unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
43+ unPatchMapWithMove :: Map k (NodeInfo k p)
85 }
44 }
86− deriving ( Show, Read, Eq, Ord
45+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithMove k p)
87−-- Haddock cannot handle documentation here before GHC 8.6
46+deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithMove k p)
88− ,
47+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithMove k p)
89−#if __GLASGOW_HASKELL__ >= 806
48+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithMove k p)
90− -- | Compose patches having the same effect as applying the
91− -- patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p .
92− -- 'applyAlways' q@
93−#endif
94− Semigroup
95− , Monoid
96− )
97
49
98−pattern Coerce :: Coercible a b => a -> b
50+-- | Holds the information about each key: where its new value should come from,
99−pattern Coerce x <- (coerce -> x)
51+-- and where its old value should go to
100− where Coerce x = coerce x
52+data NodeInfo k p = NodeInfo
101−
53+ { _nodeInfo_from :: !(From k p)
102−{-# COMPLETE PatchMapWithMove #-}
54+ -- ^ Where do we get the new value for this key?
103−pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
55+ , _nodeInfo_to :: !(To k p)
104−-- | Extract the representation of the 'PatchMapWithMove' as a map of
56+ -- ^ If the old value is being kept (i.e. moved rather than deleted or
105−-- 'NodeInfo'.
57+ -- replaced), where is it going?
106−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
58+ }
107−pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
59+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
108−
60+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
109−_PatchMapWithMove
61+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
110− :: Iso
62+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
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
116−
117−instance Functor (PatchMapWithMove k) where
118− fmap f = runIdentity . traverse (Identity . f)
119−
120−instance Foldable (PatchMapWithMove k) where
121− foldMap = foldMapDefault
122
63
123−instance Traversable (PatchMapWithMove k) where
64+-- | Describe how a key's new value should be produced
124− traverse =
65+data From k p
125− _PatchMapWithMove .
66+ = From_Insert (PatchTarget p) -- ^ Insert the given value here
126− traverse .
67+ | From_Delete -- ^ Delete the existing value, if any, from here
127− traverse
68+ | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
69+
70+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
71+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
72+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
73+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
74+
75+-- | Describe where a key's old value will go. If this is 'Just', that means
76+-- the key's old value will be moved to the given other key; if it is 'Nothing',
77+-- that means it will be deleted.
78+data To k p
79+ = To_NonMove
80+ | To_Move !k !p
81+ deriving ( Show, Read, Eq, Ord
82+ , Functor, Foldable, Traversable
83+ )
128
84
129−instance FunctorWithIndex k (PatchMapWithMove k)
130−instance FoldableWithIndex k (PatchMapWithMove k)
131−instance TraversableWithIndex k (PatchMapWithMove k) where
85+makeWrapped ''PatchMapWithMove
132− itraverse = itraversed . Indexed
133− itraversed =
134− _PatchMapWithMove .>
135− itraversed <.
136− traverse
137
86
138-- | Create a 'PatchMapWithMove', validating it
87-- | Create a 'PatchMapWithMove', validating it
88+patchMapWithMove
139−patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
89+ :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithMove k p)
140−patchMapWithMove = fmap PatchMapWithMove' . PM.patchMapWithPatchingMove . coerce
90+patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
91+ where valid = forwardLinks == backwardLinks
92+ forwardLinks = flip Map.mapMaybe m $ \x -> case _nodeInfo_to x of
93+ To_NonMove -> Nothing
94+ To_Move to _ -> Just to
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
141
99
142-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
100-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
143−patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
101+patchMapWithMoveInsertAll
102+ :: Map k (PatchTarget p) -> PatchMapWithMove k p
144−patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
103+patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
104+ { _nodeInfo_from = From_Insert v
105+ , _nodeInfo_to = To_NonMove
106+ }
145
107
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'.
108+-- | Make a @'PatchMapWithMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109+insertMapKey
147−insertMapKey :: k -> v -> PatchMapWithMove k v
110+ :: k -> PatchTarget p -> PatchMapWithMove k p
148−insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
111+insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) To_NonMove
149
112
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:
113+-- |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:
151--
114--
152-- @
115-- @
153-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
116-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
154-- @
117-- @
118+moveMapKey
155−moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
119+ :: (DecidablyEmpty p, Patch p) => Ord k => k -> k -> PatchMapWithMove k p
156−moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
120+moveMapKey src dst
157−
121+ | src == dst = mempty
122+ | otherwise =
123+ PatchMapWithMove $ Map.fromList
124+ [ (dst, NodeInfo (From_Move src mempty) To_NonMove)
125+ , (src, NodeInfo From_Delete (To_Move dst mempty))
126+ ]
127+
158−-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
128+-- |Make a @'PatchMapWithMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
159--
129--
160-- @
130-- @
161-- let aMay = Map.lookup a map
131-- let aMay = Map.lookup a map
162-- bMay = Map.lookup b map
132-- bMay = Map.lookup b map
163−-- in maybe id (Map.insert a) (bMay <> aMay)
133+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
164−-- . maybe id (Map.insert b) (aMay <> bMay)
134+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
165-- . Map.delete a . Map.delete b $ map
135-- . Map.delete a . Map.delete b $ map
166-- @
136-- @
137+swapMapKey
138+ :: (DecidablyEmpty p, Patch p)
167−swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
139+ => Ord k => k -> k -> PatchMapWithMove k p
168−swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
140+swapMapKey src dst
169−
141+ | src == dst = mempty
142+ | otherwise =
143+ PatchMapWithMove $ Map.fromList
144+ [ (dst, NodeInfo (From_Move src mempty) (To_Move dst mempty))
145+ , (src, NodeInfo (From_Move dst mempty) (To_Move dst mempty))
146+ ]
147+
170−-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
148+-- | Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in
149+-- the mapping, equivalent to 'Map.delete'.
150+deleteMapKey
171−deleteMapKey :: k -> PatchMapWithMove k v
151+ :: k -> PatchMapWithMove k v
172−deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
152+deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete To_NonMove
173
153
174-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
154-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
175--
155--
176-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
156-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
157+unsafePatchMapWithMove
177−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
158+ :: Map k (NodeInfo k p) -> PatchMapWithMove k p
178−unsafePatchMapWithMove = coerce PM.unsafePatchMapWithPatchingMove
159+unsafePatchMapWithMove = PatchMapWithMove
179
160
180-- | Apply the insertions, deletions, and moves to a given 'Map'
161-- | Apply the insertions, deletions, and moves to a given 'Map'
181−instance Ord k => Patch (PatchMapWithMove k v) where
162+instance (Ord k, Patch p) => PatchHet (PatchMapWithMove k p) where
163+ type PatchSource (PatchMapWithMove k p) = Map k (PatchSource p)
182− type PatchTarget (PatchMapWithMove k v) = Map k v
164+ type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
165+
166+instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) where
167+ -- TODO: return Nothing sometimes
168+ -- Note: the strict application here is critical to ensuring that incremental
169+ -- merges don't hold onto all their prerequisite events forever; can we make
170+ -- this more robust?
183− apply (PatchMapWithMove' p) = apply p
171+ apply (PatchMapWithMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
184−
172+ where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
173+ From_Insert v -> Just v
174+ From_Move k p -> applyAlways p <$> Map.lookup k old
175+ From_Delete -> Nothing
176+ deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
177+ From_Delete -> Just ()
178+ _ -> Nothing
179+
185−-- | Returns all the new elements that will be added to the 'Map'.
180+-- | Returns all the new elements that will be added to the 'Map'
186−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
181+patchMapWithMoveNewElements
187−patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
182+ :: PatchMapWithMove k p -> [PatchTarget p]
183+patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
188
184
189-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
185-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
190−patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
186+patchMapWithMoveNewElementsMap
187+ :: PatchMapWithMove k p -> Map k (PatchTarget p)
191−patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
188+patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
189+ where f ni = case _nodeInfo_from ni of
190+ From_Insert v -> Just v
191+ From_Move _ _ -> Nothing
192+ From_Delete -> Nothing
192
193
193-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
194-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
194-- its values using the given ordering function. The set keys of the 'Map' is
195-- its values using the given ordering function. The set keys of the 'Map' is
195-- not changed.
196-- not changed.
196−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
197+patchThatSortsMapWith
198+ :: (Ord k, Monoid p)
199+ => (PatchTarget p -> PatchTarget p -> Ordering)
200+ -> Map k (PatchTarget p) -> PatchMapWithMove k p
197−patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
201+patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
202+ where unsorted = Map.toList m
203+ sorted = sortBy (cmp `on` snd) unsorted
204+ f (to, _) (from, _) = if to == from then Nothing else
205+ Just (from, to)
206+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
207+ g (to, _) (from, _) = if to == from then Nothing else
208+ let Just movingTo = Map.lookup from reverseMapping
209+ in Just (to, NodeInfo (From_Move from mempty) $ To_Move movingTo mempty)
198
210
199-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
211-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
200-- will produce a 'Map' with the same values as the second 'Map' but with the
212-- will produce a 'Map' with the same values as the second 'Map' but with the
201-- values sorted with the given ordering function.
213-- values sorted with the given ordering function.
202−patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
214+patchThatChangesAndSortsMapWith
215+ :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
216+ => (PatchTarget p -> PatchTarget p -> Ordering)
217+ -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
203patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
218patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
204 where newList = Map.toList newByIndexUnsorted
219 where newList = Map.toList newByIndexUnsorted
205 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
220 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
206
221
207-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
222-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
208-- will produce the second 'Map'.
223-- will produce the second 'Map'.
209−patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
224+patchThatChangesMap
210−patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
225+ :: (Ord k, Ord (PatchTarget p), Monoid p)
211− PM.patchThatChangesMap oldByIndex newByIndex
226+ => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
212−
227+patchThatChangesMap oldByIndex newByIndex = patch
213−--
228+ where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
214−-- NodeInfo
229+ (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
215−--
230+ let f k v = do
216−
231+ remainingValues <- get
217−-- | Holds the information about each key: where its new value should come from,
232+ let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
218−-- and where its old value should go to
233+ then Map.delete v remainingValues
219−newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
234+ else Map.insert v remainingKeys remainingValues
220−
235+ case Map.lookup v remainingValues of
221−deriving instance (Show k, Show p) => Show (NodeInfo k p)
236+ Nothing -> return $ NodeInfo (From_Insert v) $ To_Move undefined undefined -- There's no existing value we can take
222−deriving instance (Read k, Read p) => Read (NodeInfo k p)
237+ Just fromKs ->
223−deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
238+ if k `Set.member` fromKs
224−deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
239+ then do
225−
240+ putRemainingKeys $ Set.delete k fromKs
226−{-# COMPLETE NodeInfo #-}
241+ return $ NodeInfo (From_Move k mempty) $ To_Move undefined undefined -- There's an existing value, and it's here, so no patch necessary
227−pattern NodeInfo :: To k -> From k v -> NodeInfo k v
242+ else do
228−_nodeInfo_to :: NodeInfo k v -> To k
243+ (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
229−_nodeInfo_from :: NodeInfo k v -> From k v
244+ putRemainingKeys remainingKeys
230−pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
245+ return $ NodeInfo (From_Move fromK mempty) $ To_Move undefined undefined
231− PM.NodeInfo
246+ Map.traverseWithKey f newByIndex
232− { PM._nodeInfo_to = _nodeInfo_to
247+ unusedOldKeys = fold unusedValuesByValue
233− , PM._nodeInfo_from = Coerce _nodeInfo_from
248+ pointlessMove k = \case
234− }
249+ From_Move k' _ | k == k' -> True
235−
250+ _ -> False
236−_NodeInfo
251+ keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
237− :: Iso
252+ then To_Move undefined undefined
238− (NodeInfo k0 v0)
253+ else To_NonMove
239− (NodeInfo k1 v1)
254+ patch = unsafePatchMapWithMove $ Map.filterWithKey
240− (PM.NodeInfo k0 (Proxy v0))
255+ (\k -> not . pointlessMove k . _nodeInfo_from)
241− (PM.NodeInfo k1 (Proxy v1))
256+ $ Map.mergeWithKey
242−_NodeInfo = iso unNodeInfo' NodeInfo'
257+ (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a)
243−
258+ (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k)
244−instance Functor (NodeInfo k) where
259+ (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k)
245− fmap f = runIdentity . traverse (Identity . f)
260+ insertsAndMoves
246−
261+ oldByIndex
247−instance Foldable (NodeInfo k) where
248− foldMap = foldMapDefault
249−
250−instance Traversable (NodeInfo k) where
251− traverse = bitraverseNodeInfo pure
252−
253−bitraverseNodeInfo
254− :: Applicative f
255− => (k0 -> f k1)
256− -> (v0 -> f v1)
257− -> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
258−bitraverseNodeInfo fk fv = fmap NodeInfo'
259− . PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
260− . coerce
261
262
262-- | Change the 'From' value of a 'NodeInfo'
263-- | Change the 'From' value of a 'NodeInfo'
263−nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
264+nodeInfoMapFrom
264−nodeInfoMapFrom f = coerce $ PM.nodeInfoMapFrom (unFrom' . f . From')
265+ :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
266+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
265
267
266-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
268-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
267-- 'Applicative', 'Monad', etc.) action to get the new value
269-- 'Applicative', 'Monad', etc.) action to get the new value
268nodeInfoMapMFrom
270nodeInfoMapMFrom
269− :: Functor f
271+ :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
270− => (From k v -> f (From k v))
272+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
271− -> NodeInfo k v -> f (NodeInfo k v)
272−nodeInfoMapMFrom f = fmap NodeInfo'
273− . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
274− . coerce
275
273
276-- | Set the 'To' field of a 'NodeInfo'
274-- | Set the 'To' field of a 'NodeInfo'
277−nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
275+nodeInfoSetTo
278−nodeInfoSetTo = coerce . PM.nodeInfoSetTo
276+ :: To k p -> NodeInfo k p -> NodeInfo k p
279−
277+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
280−--
278+
281−-- From
279+-- | Helper data structure used for composing patches using the monoid instance.
282−--
280+data Fixup k p
283−
281+ = Fixup_Delete
284−-- | Describe how a key's new value should be produced
282+ | Fixup_Update (These (From k p) (To k p))
285−newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
283+
286−
284+-- | Compose patches having the same effect as applying the patches in turn:
287−{-# COMPLETE From_Insert, From_Delete, From_Move #-}
285+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
288−
286+instance ( Ord k
289−-- | Insert the given value here
287+ , DecidablyEmpty p
290−pattern From_Insert :: v -> From k v
288+ , Patch p
291−pattern From_Insert v = From' (PM.From_Insert v)
289+ ) => Semigroup (PatchMapWithMove k p) where
292−
290+ PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
293−-- | Delete the existing value, if any, from here
291+ where
294−pattern From_Delete :: From k v
292+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
295−pattern From_Delete = From' PM.From_Delete
293+ h :: (k, (To k p, From k p)) -> [(k, Fixup k p)]
296−
294+ h (_, (editAfter, editBefore)) = case (editAfter, editBefore) of
297−-- | Move the value here from the given key
295+ (To_Move toAfter p1, From_Move fromBefore p0)
298−pattern From_Move :: k -> From k v
296+ | fromBefore == toAfter && isNull p0
299−pattern From_Move k = From' (PM.From_Move k Proxy)
297+ -> [(toAfter, Fixup_Delete)]
300−
298+ | otherwise
301−bitraverseFrom
299+ -> [ (toAfter, Fixup_Update (This $ From_Move fromBefore $ p1 <> p0))
302− :: Applicative f
300+ , (fromBefore, Fixup_Update (That $ To_Move toAfter $ p1 <> p0))
303− => (k0 -> f k1)
301+ ]
304− -> (v0 -> f v1)
302+ (To_NonMove, From_Move fromBefore _) ->
305− -> From k0 v0 -> f (From k1 v1)
303+ -- The item is destroyed in the second patch, so indicate that it is
306−bitraverseFrom fk fv = fmap From'
304+ -- destroyed in the source map
307− . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
305+ [(fromBefore, Fixup_Update (That editAfter))]
308− . coerce
306+ (To_Move toAfter p, From_Insert val) ->
309−
307+ [(toAfter, Fixup_Update $ This $ From_Insert $ applyAlways p val)]
310−makeWrapped ''PatchMapWithMove
308+ (To_Move toAfter _, From_Delete) ->
311−makeWrapped ''NodeInfo
309+ [(toAfter, Fixup_Update $ This From_Delete)]
312−makeWrapped ''From
310+ (To_NonMove, _) -> []
311+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
312+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
313+ | This x <- a, That y <- b
314+ = Fixup_Update $ These x y
315+ | That y <- a, This x <- b
316+ = Fixup_Update $ These x y
317+ mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
318+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
319+ combineNodeInfos _ nia nib = NodeInfo
320+ { _nodeInfo_from = _nodeInfo_from nia
321+ , _nodeInfo_to = _nodeInfo_to nib
322+ }
323+ applyFixup _ ni = \case
324+ Fixup_Delete -> Nothing
325+ Fixup_Update u -> Just $ NodeInfo
326+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
327+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
328+ }
329+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
330+ getHere :: These a b -> Maybe a
331+ getHere = \case
332+ This a -> Just a
333+ These a _ -> Just a
334+ That _ -> Nothing
335+ getThere :: These a b -> Maybe b
336+ getThere = \case
337+ This _ -> Nothing
338+ These _ b -> Just b
339+ That b -> Just b
340+
341+--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
342+-- | Compose patches having the same effect as applying the patches in turn:
343+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
344+instance ( Ord k
345+ , Monoid p
346+ , DecidablyEmpty p
347+ , Patch p
348+ ) => Monoid (PatchMapWithMove k p) where
349+ mempty = PatchMapWithMove mempty
350+ mappend = (<>)
src/Data/Patch/MapWithPatchingMove.hsdeleted−432
@@ -1,432 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE PatternGuards #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE StandaloneDeriving #-}
10−{-# LANGUAGE TemplateHaskell #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−
14−-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15−-- another
16−module Data.Patch.MapWithPatchingMove
17− ( PatchMapWithPatchingMove (..)
18− , patchMapWithPatchingMove
19− , patchMapWithPatchingMoveInsertAll
20− , insertMapKey
21− , moveMapKey
22− , swapMapKey
23− , deleteMapKey
24− , unsafePatchMapWithPatchingMove
25− , patchMapWithPatchingMoveNewElements
26− , patchMapWithPatchingMoveNewElementsMap
27− , patchThatSortsMapWith
28− , patchThatChangesAndSortsMapWith
29− , patchThatChangesMap
30−
31− -- * Node Info
32− , NodeInfo (..)
33− , bitraverseNodeInfo
34− , nodeInfoMapFrom
35− , nodeInfoMapMFrom
36− , nodeInfoSetTo
37−
38− -- * From
39− , From(..)
40− , bitraverseFrom
41−
42− -- * To
43− , To
44−
45− -- TODO internals module
46− , Fixup (..)
47− ) where
48−
49−import Data.Patch.Class
50−
51−import Control.Lens hiding (from, to)
52−import Control.Lens.TH (makeWrapped)
53−import Data.Align (align)
54−import Data.Foldable (toList)
55−import Data.Function
56−import Data.List
57−import Data.Map (Map)
58−import qualified Data.Map as Map
59−import Data.Maybe
60−#if !MIN_VERSION_base(4,11,0)
61−import Data.Semigroup (Semigroup (..))
62−#endif
63−import Data.Monoid.DecidablyEmpty
64−import Data.Set (Set)
65−import qualified Data.Set as Set
66−import Data.These (These (..))
67−
68−-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
69−-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
70−-- and vice versa. There should never be any unpaired From/To keys.
71−newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
72− { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
73− unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
74− }
75−
76−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
77−deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
78−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
79−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
80−
81−deriving instance ( Ord k
82−#if !MIN_VERSION_base(4,11,0)
83− , Semigroup p
84−#endif
85− , DecidablyEmpty p
86− , Patch p
87− ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
88−
89−-- | Create a 'PatchMapWithPatchingMove', validating it
90−patchMapWithPatchingMove
91− :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
92−patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
93− where valid = forwardLinks == backwardLinks
94− forwardLinks = Map.mapMaybe _nodeInfo_to m
95− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
96− case _nodeInfo_from p of
97− From_Move from _ -> Just (from, to)
98− _ -> Nothing
99−
100−-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
101−patchMapWithPatchingMoveInsertAll
102− :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
103−patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
104− { _nodeInfo_from = From_Insert v
105− , _nodeInfo_to = Nothing
106− }
107−
108−-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109−insertMapKey
110− :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
111−insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
112−
113−-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
114−--
115−-- @
116−-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
117−-- @
118−moveMapKey
119− :: ( DecidablyEmpty p
120−#if !MIN_VERSION_base(4,11,0)
121− , Semigroup p
122−#endif
123− , Patch p
124− )
125− => Ord k => k -> k -> PatchMapWithPatchingMove k p
126−moveMapKey src dst
127− | src == dst = mempty
128− | otherwise =
129− PatchMapWithPatchingMove $ Map.fromList
130− [ (dst, NodeInfo (From_Move src mempty) Nothing)
131− , (src, NodeInfo From_Delete (Just dst))
132− ]
133−
134−-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
135−--
136−-- @
137−-- let aMay = Map.lookup a map
138−-- bMay = Map.lookup b map
139−-- in maybe id (Map.insert a) (bMay <> aMay)
140−-- . maybe id (Map.insert b) (aMay <> bMay)
141−-- . Map.delete a . Map.delete b $ map
142−-- @
143−swapMapKey
144− :: ( DecidablyEmpty p
145−#if !MIN_VERSION_base(4,11,0)
146− , Semigroup p
147−#endif
148− , Patch p
149− )
150− => Ord k => k -> k -> PatchMapWithPatchingMove k p
151−swapMapKey src dst
152− | src == dst = mempty
153− | otherwise =
154− PatchMapWithPatchingMove $ Map.fromList
155− [ (dst, NodeInfo (From_Move src mempty) (Just src))
156− , (src, NodeInfo (From_Move dst mempty) (Just dst))
157− ]
158−
159−-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
160−-- the mapping, equivalent to 'Map.delete'.
161−deleteMapKey
162− :: k -> PatchMapWithPatchingMove k v
163−deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
164−
165−-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
166−--
167−-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
168−unsafePatchMapWithPatchingMove
169− :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
170−unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
171−
172−-- | Apply the insertions, deletions, and moves to a given 'Map'
173−instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
174− type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
175− -- TODO: return Nothing sometimes
176− -- Note: the strict application here is critical to ensuring that incremental
177− -- merges don't hold onto all their prerequisite events forever; can we make
178− -- this more robust?
179− apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
180− where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
181− From_Insert v -> Just v
182− From_Move k p -> applyAlways p <$> Map.lookup k old
183− From_Delete -> Nothing
184− deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
185− From_Delete -> Just ()
186− _ -> Nothing
187−
188−-- | Returns all the new elements that will be added to the 'Map'
189−patchMapWithPatchingMoveNewElements
190− :: PatchMapWithPatchingMove k p -> [PatchTarget p]
191−patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
192−
193−-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
194−patchMapWithPatchingMoveNewElementsMap
195− :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
196−patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
197− where f ni = case _nodeInfo_from ni of
198− From_Insert v -> Just v
199− From_Move _ _ -> Nothing
200− From_Delete -> Nothing
201−
202−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
203−-- its values using the given ordering function. The set keys of the 'Map' is
204−-- not changed.
205−patchThatSortsMapWith
206− :: (Ord k, Monoid p)
207− => (PatchTarget p -> PatchTarget p -> Ordering)
208− -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
209−patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
210− where unsorted = Map.toList m
211− sorted = sortBy (cmp `on` snd) unsorted
212− f (to, _) (from, _) = if to == from then Nothing else
213− Just (from, to)
214− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
215− g (to, _) (from, _) = if to == from then Nothing else
216− let Just movingTo = Map.lookup from reverseMapping
217− in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
218−
219−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
220−-- will produce a 'Map' with the same values as the second 'Map' but with the
221−-- values sorted with the given ordering function.
222−patchThatChangesAndSortsMapWith
223− :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
224− => (PatchTarget p -> PatchTarget p -> Ordering)
225− -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
226−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
227− where newList = Map.toList newByIndexUnsorted
228− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
229−
230−-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
231−-- will produce the second 'Map'.
232−-- Note: this will never produce a patch on a value.
233−patchThatChangesMap
234− :: forall k p
235− . (Ord k, Ord (PatchTarget p), Monoid p)
236− => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
237−patchThatChangesMap oldByIndex newByIndex = patch
238− where invert :: Map k (PatchTarget p) -> Map (PatchTarget p) (Set k)
239− invert = Map.fromListWith (<>) . fmap (\(k, v) -> (v, Set.singleton k)) . Map.toList
240− -- In the places where we use unionDistinct, a non-distinct key indicates a bug in this function
241− unionDistinct :: forall k' v'. Ord k' => Map k' v' -> Map k' v' -> Map k' v'
242− unionDistinct = Map.unionWith (error "patchThatChangesMap: non-distinct keys")
243− unionPairDistinct :: (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k))
244− unionPairDistinct (oldFroms, oldTos) (newFroms, newTos) = (unionDistinct oldFroms newFroms, unionDistinct oldTos newTos)
245− -- Generate patch info for a single value
246− -- Keys that are found in both the old and new sets will not be patched
247− -- Keys that are found in only the old set will be moved to a new position if any are available; otherwise they will be deleted
248− -- Keys that are found in only the new set will be populated by moving an old key if any are available; otherwise they will be inserted
249− patchSingleValue :: PatchTarget p -> Set k -> Set k -> (Map k (From k p), Map k (To k))
250− patchSingleValue v oldKeys newKeys = foldl' unionPairDistinct mempty $ align (toList $ oldKeys `Set.difference` newKeys) (toList $ newKeys `Set.difference` oldKeys) <&> \case
251− This oldK -> (mempty, Map.singleton oldK Nothing) -- There's nowhere for this value to go, so we know we are deleting it
252− That newK -> (Map.singleton newK $ From_Insert v, mempty) -- There's nowhere fo this value to come from, so we know we are inserting it
253− These oldK newK -> (Map.singleton newK $ From_Move oldK mempty, Map.singleton oldK $ Just newK)
254− -- Run patchSingleValue on a These. Missing old or new sets are considered empty
255− patchSingleValueThese :: PatchTarget p -> These (Set k) (Set k) -> (Map k (From k p), Map k (To k))
256− patchSingleValueThese v = \case
257− This oldKeys -> patchSingleValue v oldKeys mempty
258− That newKeys -> patchSingleValue v mempty newKeys
259− These oldKeys newKeys -> patchSingleValue v oldKeys newKeys
260− -- Generate froms and tos for all values, then merge them together
261− (froms, tos) = foldl' unionPairDistinct mempty $ Map.mapWithKey patchSingleValueThese $ align (invert oldByIndex) (invert newByIndex)
262− patch = unsafePatchMapWithPatchingMove $ align froms tos <&> \case
263− This from -> NodeInfo from Nothing -- Since we don't have a 'to' record for this key, that must mean it isn't being moved anywhere, so it should be deleted.
264− That to -> NodeInfo From_Delete to -- Since we don't have a 'from' record for this key, it must be getting deleted
265− These from to -> NodeInfo from to
266−
267−--
268−-- NodeInfo
269−--
270−
271−-- | Holds the information about each key: where its new value should come from,
272−-- and where its old value should go to
273−data NodeInfo k p = NodeInfo
274− { _nodeInfo_from :: !(From k p)
275− -- ^ Where do we get the new value for this key?
276− , _nodeInfo_to :: !(To k)
277− -- ^ If the old value is being kept (i.e. moved rather than deleted or
278− -- replaced), where is it going?
279− }
280−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
281−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
282−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
283−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
284−
285−-- | Traverse the 'NodeInfo' over the key, patch, and patch target. Because of
286−-- the type families here, this doesn't it any bi- or tri-traversal class.
287−bitraverseNodeInfo
288− :: Applicative f
289− => (k0 -> f k1)
290− -> (p0 -> f p1)
291− -> (PatchTarget p0 -> f (PatchTarget p1))
292− -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
293−bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
294− <$> bitraverseFrom fk fp fpt from
295− <*> traverse fk to
296−
297−-- | Change the 'From' value of a 'NodeInfo'
298−nodeInfoMapFrom
299− :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
300−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
301−
302−-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
303−-- 'Applicative', 'Monad', etc.) action to get the new value
304−nodeInfoMapMFrom
305− :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
306−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
307−
308−-- | Set the 'To' field of a 'NodeInfo'
309−nodeInfoSetTo
310− :: To k -> NodeInfo k v -> NodeInfo k v
311−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
312−
313−--
314−-- From
315−--
316−
317−-- | Describe how a key's new value should be produced
318−data From k p
319− = From_Insert (PatchTarget p) -- ^ Insert the given value here
320− | From_Delete -- ^ Delete the existing value, if any, from here
321− | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
322−
323−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
324−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
325−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
326−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
327−
328−-- | Traverse the 'From' over the key, patch, and patch target. Because of
329−-- the type families here, this doesn't it any bi- or tri-traversal class.
330−bitraverseFrom
331− :: Applicative f
332− => (k0 -> f k1)
333− -> (p0 -> f p1)
334− -> (PatchTarget p0 -> f (PatchTarget p1))
335− -> From k0 p0 -> f (From k1 p1)
336−bitraverseFrom fk fp fpt = \case
337− From_Insert pt -> From_Insert <$> fpt pt
338− From_Delete -> pure From_Delete
339− From_Move k p -> From_Move <$> fk k <*> fp p
340−
341−--
342−-- To
343−--
344−
345−-- | Describe where a key's old value will go. If this is 'Just', that means
346−-- the key's old value will be moved to the given other key; if it is 'Nothing',
347−-- that means it will be deleted.
348−type To = Maybe
349−
350−--
351−-- Fixup
352−--
353−
354−-- | Helper data structure used for composing patches using the monoid instance.
355−data Fixup k v
356− = Fixup_Delete
357− | Fixup_Update (These (From k v) (To k))
358−
359−-- | Compose patches having the same effect as applying the patches in turn:
360−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
361−instance ( Ord k
362−#if !MIN_VERSION_base(4,11,0)
363− , Semigroup p
364−#endif
365− , DecidablyEmpty p
366− , Patch p
367− ) => Semigroup (PatchMapWithPatchingMove k p) where
368− PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
369− where
370− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
371− h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
372− h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
373− (Just toAfter, From_Move fromBefore p)
374− | fromBefore == toAfter && isEmpty p
375− -> [(toAfter, Fixup_Delete)]
376− | otherwise
377− -> [ (toAfter, Fixup_Update (This editBefore))
378− , (fromBefore, Fixup_Update (That mToAfter))
379− ]
380− (Nothing, From_Move fromBefore _) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
381− (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
382− (Nothing, _) -> []
383− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
384− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
385− | This x <- a, That y <- b
386− = Fixup_Update $ These x y
387− | That y <- a, This x <- b
388− = Fixup_Update $ These x y
389− mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
390− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
391− combineNodeInfos _ nia nib = NodeInfo
392− { _nodeInfo_from = _nodeInfo_from nia
393− , _nodeInfo_to = _nodeInfo_to nib
394− }
395− applyFixup _ ni = \case
396− Fixup_Delete -> Nothing
397− Fixup_Update u -> Just $ NodeInfo
398− { _nodeInfo_from = case _nodeInfo_from ni of
399− f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
400− Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
401− Just (From_Insert v) -> From_Insert $ applyAlways p' v
402− Just From_Delete -> From_Delete
403− Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
404− _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
405− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
406− }
407− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
408− getHere :: These a b -> Maybe a
409− getHere = \case
410− This a -> Just a
411− These a _ -> Just a
412− That _ -> Nothing
413− getThere :: These a b -> Maybe b
414− getThere = \case
415− This _ -> Nothing
416− These _ b -> Just b
417− That b -> Just b
418−
419−--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
420−-- | Compose patches having the same effect as applying the patches in turn:
421−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
422−instance ( Ord k
423−#if !MIN_VERSION_base(4,11,0)
424− , Semigroup p
425−#endif
426− , DecidablyEmpty p
427− , Patch p
428− ) => Monoid (PatchMapWithPatchingMove k p) where
429− mempty = PatchMapWithPatchingMove mempty
430− mappend = (<>)
431−
432−makeWrapped ''PatchMapWithPatchingMove
src/Data/Semigroup/Additive.hsdeleted−54
@@ -1,54 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE TypeOperators #-}
3−{-# LANGUAGE TypeFamilies #-}
4−-- |
5−-- Module:
6−-- Data.Semigroup.Additive
7−-- Description:
8−-- This module defines a class for commutative semigroups, until it is moved
9−-- to another library.
10−module Data.Semigroup.Additive
11− ( Additive
12− ) where
13−
14−import Data.Functor.Const (Const (..))
15−import Data.Functor.Identity
16−import Data.Proxy
17−#if !MIN_VERSION_base(4,12,0)
18−-- for :*: and :.: semigroup instances
19−import Data.Orphans ()
20−#endif
21−#if !MIN_VERSION_base(4,11,0)
22−import Data.Semigroup (Semigroup (..))
23−#endif
24−import GHC.Generics
25−
26−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
27−class Semigroup q => Additive q where
28−
29−-- | Trivial additive semigroup.
30−instance Additive ()
31−
32−-- | Product additive semigroup.
33−-- A Pair of additive semigroups gives rise to a additive semigroup
34−instance (Additive a, Additive b) => Additive (a, b)
35−
36−-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
37−-- Base does not define Monoid (Compose f g a) so this is the best we can
38−-- really do for functor composition.
39−instance Additive (f (g a)) => Additive ((f :.: g) a)
40−
41−-- | Product of additive semigroups, Functor style.
42−instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
43−
44−-- | Trivial additive semigroup, Functor style
45−instance Additive (Proxy x)
46−
47−-- | Const lifts additive semigroups into a functor.
48−instance Additive a => Additive (Const a x)
49−
50−-- | Identity lifts additive semigroups pointwise (at only one point)
51−instance Additive a => Additive (Identity a)
52−
53−-- | Functions lift additive semigroups pointwise.
54−instance Additive b => Additive (a -> b)
test/hlint.hs+1−2
@@ -1,7 +1,7 @@
1module Main where
1module Main where
2
2
3import Control.Monad
3import Control.Monad
4−import Language.Haskell.HLint (hlint)
4+import Language.Haskell.HLint3 (hlint)
5import System.Directory
5import System.Directory
6import System.Exit (exitFailure, exitSuccess)
6import System.Exit (exitFailure, exitSuccess)
7import System.FilePath
7import System.FilePath
@@ -23,7 +23,6 @@ main = do
23 , "--ignore=Reduce duplication"
23 , "--ignore=Reduce duplication"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
25 , "--ignore=Use tuple-section"
25 , "--ignore=Use tuple-section"
26− , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
27 ]
26 ]
28 recurseInto = and <$> sequence
27 recurseInto = and <$> sequence
29 [ fileType ==? Directory
28 [ fileType ==? Directory
test/tests.hsdeleted−43
@@ -1,43 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−module Main where
3−
4−import Test.HUnit (runTestTT, (~:), assertEqual, errors, failures, test)
5−import Data.Patch ( Patch(apply) )
6−import Data.Patch.MapWithMove ( patchThatChangesMap )
7−import Data.Map as Map ( Map, fromList, singleton )
8−import Hedgehog (checkParallel, discover, Property, property, forAll, PropertyT, (===))
9−import Hedgehog.Gen as Gen ( int )
10−import Hedgehog.Range as Range ( linear )
11−import Control.Monad (replicateM)
12−import System.Exit (exitFailure, exitSuccess)
13−import Data.Sequence as Seq ( foldMapWithIndex, replicateM )
14−
15−main :: IO ()
16−main = do
17− counts <- runTestTT $ test [
18− "Simple Move" ~: (do
19− let mapBefore = Map.fromList [(0,1)]
20− mapAfter = Map.fromList [(0,0),(1,1)]
21− patch = patchThatChangesMap mapBefore mapAfter
22− afterPatch = apply patch mapBefore
23− assertEqual "Patch creates the same Map" (Just mapAfter) afterPatch),
24− "Property Checks" ~: propertyChecks
25− ]
26− if errors counts + failures counts == 0 then exitSuccess else exitFailure
27−
28−propertyChecks :: IO Bool
29−propertyChecks = checkParallel $$(discover)
30−
31−prop_patchThatChangesMap :: Property
32−prop_patchThatChangesMap = property $ do
33− mapBefore <- makeRandomIntMap
34− mapAfter <- makeRandomIntMap
35− let patch = patchThatChangesMap mapBefore mapAfter
36− Just mapAfter === apply patch mapBefore
37−
38−makeRandomIntMap :: Monad m => PropertyT m (Map Int Int)
39−makeRandomIntMap = do
40− let genNum = Gen.int (Range.linear 0 100)
41− length <- forAll genNum
42− listOfNumbers <- forAll $ Seq.replicateM length genNum
43− pure $ Seq.foldMapWithIndex Map.singleton listOfNumbers