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