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