on develop help sign in
Merge branch 'patch-map-inner-patch-desugar' into dpatch-map-inner-patch
6ef716aJohn Ericson
.github/workflows/haskell.ymlnew file+47
@@ -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 all
46+ - name: Build Docs
47+ run: cabal haddock
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+24
@@ -6,6 +6,30 @@
6
6
7* `PatchDMapWithMove` supports moves with a patch.
7* `PatchDMapWithMove` supports moves with a patch.
8
8
9+* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
10+ Care is taken to make this not a breaking change.
11+ In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
12+ There are complete constructor and field patterns too, and everything is
13+ exported under the newtype as real constructors and fields would be.
14+
15+## 0.0.4.0
16+
17+* Enable PolyKinds
18+
19+## 0.0.3.2
20+
21+* Update version bounds
22+
23+## 0.0.3.1
24+
25+* Replace `fromJust` with something easier to debug.
26+
27+## 0.0.3.0
28+
29+* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
30+
31+* Create `DecidablyEmpty` subclass of `Monoid`.
32+
9## 0.0.2.0
33## 0.0.2.0
10
34
11* Consistently provide:
35* Consistently provide:
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:
patch.cabal+16−10
@@ -1,7 +1,9 @@
1Name: patch
1Name: patch
2−Version: 0.0.2.0
2+Version: 0.0.4.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,16 @@ 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.4
42
45
43 exposed-modules: Control.Category.DecidablyEmpty
46 exposed-modules: Control.Category.DecidablyEmpty
44 , Data.Functor.Misc
47 , Data.Functor.Misc
@@ -51,11 +54,13 @@ library
51 , Data.Patch.IntMap
54 , Data.Patch.IntMap
52 , Data.Patch.Map
55 , Data.Patch.Map
53 , Data.Patch.MapWithMove
56 , Data.Patch.MapWithMove
57+ , Data.Patch.MapWithPatchingMove
54
58
55 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
59 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
60+ default-extensions: PolyKinds
56
61
57 if flag(split-these)
62 if flag(split-these)
58− build-depends: these >= 1 && <1.1
63+ build-depends: these >= 1 && <1.2
59 , these-lens >= 1 && <1.1
64 , these-lens >= 1 && <1.1
60 , semialign >=1 && <1.2
65 , semialign >=1 && <1.2
61 , monoidal-containers >= 0.6 && < 0.7
66 , monoidal-containers >= 0.6 && < 0.7
@@ -64,6 +69,7 @@ library
64 , monoidal-containers == 0.4.0.0
69 , monoidal-containers == 0.4.0.0
65
70
66test-suite hlint
71test-suite hlint
72+ default-language: Haskell2010
67 type: exitcode-stdio-1.0
73 type: exitcode-stdio-1.0
68 main-is: hlint.hs
74 main-is: hlint.hs
69 hs-source-dirs: test
75 hs-source-dirs: test
@@ -71,7 +77,7 @@ test-suite hlint
71 , directory
77 , directory
72 , filepath
78 , filepath
73 , filemanip
79 , filemanip
74− , hlint < 2.1 || >= 2.2.2
80+ , hlint (< 2.1 || >= 2.2.2) && < 3.3
75 if impl(ghcjs)
81 if impl(ghcjs)
76 buildable: False
82 buildable: False
77
83
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 #-}
@@ -52,7 +51,7 @@ import qualified Data.IntMap as IntMap
52import Data.Map (Map)
51import Data.Map (Map)
53import qualified Data.Map as Map
52import qualified Data.Map as Map
54import qualified Data.Semigroupoid as Cat
53import qualified Data.Semigroupoid as Cat
55−import Data.Some (Some(Some))
54+import Data.Some (Some, mkSome)
56import Data.These
55import Data.These
57import Data.Type.Equality ((:~:)(Refl))
56import Data.Type.Equality ((:~:)(Refl))
58import Data.Typeable hiding (Refl)
57import Data.Typeable hiding (Refl)
@@ -140,7 +139,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
140-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
139-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
141-- the keys, using a function to remove the wrapping 'Functor'
140-- the keys, using a function to remove the wrapping 'Functor'
142weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
141weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
143−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
142+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
144
143
145--------------------------------------------------------------------------------
144--------------------------------------------------------------------------------
146-- WrapArg
145-- 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+3−3
@@ -18,8 +18,8 @@ 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
@@ -27,7 +27,7 @@ import Data.Patch.Class as X
27import Data.Patch.DMap as X hiding (getDeletions)
27import Data.Patch.DMap as X hiding (getDeletions)
28import Data.Patch.DMapWithMove as X
28import Data.Patch.DMapWithMove as X
29 ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
29 ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
30− , patchDMapWithMoveToPatchMapWithMoveWith
30+ , patchDMapWithMoveToPatchMapWithPatchingMoveWith
31 , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
31 , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
32 , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
32 , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
33 )
33 )
src/Data/Patch/Class.hs+10−5
@@ -1,9 +1,11 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE DefaultSignatures #-}
2{-# LANGUAGE DerivingStrategies #-}
3{-# LANGUAGE DerivingStrategies #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE PolyKinds #-}
7{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeOperators #-}
10{-# LANGUAGE TypeOperators #-}
9-- | The interface for types which represent changes made to other types
11-- | The interface for types which represent changes made to other types
@@ -12,14 +14,17 @@ module Data.Patch.Class where
12import qualified Data.Semigroupoid as Cat
14import qualified Data.Semigroupoid as Cat
13import Data.Functor.Identity
15import Data.Functor.Identity
14import Data.Functor.Misc
16import Data.Functor.Misc
17+import Data.Kind (Type)
15import Data.Maybe
18import Data.Maybe
19+#if !MIN_VERSION_base(4,11,0)
16import Data.Semigroup (Semigroup(..))
20import Data.Semigroup (Semigroup(..))
21+#endif
17import Data.Proxy
22import Data.Proxy
18import Data.Type.Equality ((:~:) (..))
23import Data.Type.Equality ((:~:) (..))
19
24
20class PatchHet p where
25class PatchHet p where
21− type PatchSource p :: *
26+ type PatchSource p :: Type
22− type PatchTarget p :: *
27+ type PatchTarget p :: Type
23 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
28 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
24 -- 'Nothing'.
29 -- 'Nothing'.
25 applyHet
30 applyHet
@@ -63,11 +68,11 @@ instance PatchHet (Identity a) where
63instance Patch (Identity a) where
68instance Patch (Identity a) where
64 apply (Identity a) _ = Just a
69 apply (Identity a) _ = Just a
65
70
66−-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
71+-- | 'Proxy' can be used as a 'Patch' that does nothing.
67−instance PatchHet (Proxy (a :: *)) where
72+instance forall (a :: Type). PatchHet (Proxy a) where
68 type PatchSource (Proxy a) = a
73 type PatchSource (Proxy a) = a
69 type PatchTarget (Proxy a) = a
74 type PatchTarget (Proxy a) = a
70−instance Patch (Proxy (a :: *)) where
75+instance forall (a :: Type). Patch (Proxy a) where
71 apply ~Proxy _ = Nothing
76 apply ~Proxy _ = Nothing
72
77
73-- | Like '(.)', but composes functions that return patches rather than
78-- | Like '(.)', but composes functions that return patches rather than
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 => PatchHet (PatchDMap k v) where
45instance GCompare k => PatchHet (PatchDMap k v) where
35 type PatchSource (PatchDMap k v) = DMap k v
46 type PatchSource (PatchDMap k v) = DMap k v
src/Data/Patch/DMapWithMove.hs+59−47
@@ -21,6 +21,7 @@ module Data.Patch.DMapWithMove where
21
21
22import qualified Control.Category as Cat
22import qualified Control.Category as Cat
23--import qualified Control.Category.DecidablyEmpty as Cat
23--import qualified Control.Category.DecidablyEmpty as Cat
24+
24import Data.Constraint.Extras (Has')
25import Data.Constraint.Extras (Has')
25import Data.Dependent.Map (DMap)
26import Data.Dependent.Map (DMap)
26import Data.Dependent.Sum (DSum (..))
27import Data.Dependent.Sum (DSum (..))
@@ -36,9 +37,12 @@ import Data.GADT.Compare (GEq (..), GCompare (..))
36import Data.GADT.Show (GRead, GShow, gshow)
37import Data.GADT.Show (GRead, GShow, gshow)
37import qualified Data.Map as Map
38import qualified Data.Map as Map
38import Data.Maybe
39import Data.Maybe
39−import Data.Semigroup (Semigroup (..), (<>))
40+import Data.Monoid.DecidablyEmpty
40import Data.Semigroupoid as Cat
41import Data.Semigroupoid as Cat
42+#if !MIN_VERSION_base(4,11,0)
41−import Data.Some (Some(Some))
43+import Data.Semigroup (Semigroup (..), (<>))
44+#endif
45+import Data.Some (Some, mkSome)
42import Data.Proxy (Proxy (..))
46import Data.Proxy (Proxy (..))
43import Data.These (These (..))
47import Data.These (These (..))
44
48
@@ -47,10 +51,10 @@ import Data.Patch.Class
47 , PatchHet2 (..), PatchSource1, PatchTarget1
51 , PatchHet2 (..), PatchSource1, PatchTarget1
48 , applyAlwaysHet2
52 , applyAlwaysHet2
49 )
53 )
50−import Data.Patch.MapWithMove (PatchMapWithMove (..))
54+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove (..))
51−import qualified Data.Patch.MapWithMove as MapWithMove
55+import qualified Data.Patch.MapWithPatchingMove as MapWithPatchingMove
52
56
53−-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
57+-- | Like 'PatchMapWithPatchingMove', but for 'DMap'. Each key carries a 'NodeInfo'
54-- which describes how it will be changed by the patch and connects move sources
58-- which describes how it will be changed by the patch and connects move sources
55-- and destinations.
59-- and destinations.
56--
60--
@@ -80,6 +84,14 @@ newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
80-- , Has' Ord k (PatchTarget1 p)
84-- , Has' Ord k (PatchTarget1 p)
81-- ) => Ord (PatchDMapWithMove k p)
85-- ) => Ord (PatchDMapWithMove k p)
82
86
87+-- It won't let me derive for some reason
88+instance ( GCompare k
89+ , Cat.Semigroupoid v
90+ , PatchHet2 v
91+ , PatchSource1 v ~ PatchTarget1 v
92+ ) => DecidablyEmpty (PatchDMapWithMove k v) where
93+ isEmpty (PatchDMapWithMove m) = DMap.null m
94+
83-- | Structure which represents what changes apply to a particular key.
95-- | Structure which represents what changes apply to a particular key.
84-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
96-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
85-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
97-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
@@ -360,8 +372,8 @@ moveDMapKey src dst = case src `geq` dst of
360-- @
372-- @
361-- let aMay = DMap.lookup a dmap
373-- let aMay = DMap.lookup a dmap
362-- bMay = DMap.lookup b dmap
374-- bMay = DMap.lookup b dmap
363−-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
375+-- in maybe id (DMap.insert a) (bMay <> aMay)
364−-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
376+-- . maybe id (DMap.insert b) (aMay <> bMay)
365-- . DMap.delete a . DMap.delete b $ dmap
377-- . DMap.delete a . DMap.delete b $ dmap
366-- @
378-- @
367swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
379swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
@@ -416,8 +428,8 @@ unsafePatchDMapWithMove = PatchDMapWithMove
416-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while
428-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while
417-- checking invariants. If the invariants are satisfied, @Right p@ is returned
429-- checking invariants. If the invariants are satisfied, @Right p@ is returned
418-- otherwise @Left errors@.
430-- otherwise @Left errors@.
419−patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
431+patchDMapWithPatchingMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
420−patchDMapWithMove dm =
432+patchDMapWithPatchingMove dm =
421 case validationErrorsForPatchDMapWithMove dm of
433 case validationErrorsForPatchDMapWithMove dm of
422 [] -> Right $ unsafePatchDMapWithMove dm
434 [] -> Right $ unsafePatchDMapWithMove dm
423 errs -> Left errs
435 errs -> Left errs
@@ -507,7 +519,7 @@ nodeInfoMapFromM f g ni = NodeInfo
507 <$> f (_nodeInfo_from ni)
519 <$> f (_nodeInfo_from ni)
508 <*> g (_nodeInfo_to ni)
520 <*> g (_nodeInfo_to ni)
509
521
510−-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
522+-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithPatchingMove' by weakening the keys
511-- from @k a@ to @'Some' k@ and applying a given weakening function
523-- from @k a@ to @'Some' k@ and applying a given weakening function
512-- @PatchTarget1 p a -> v'@ to values.
524-- @PatchTarget1 p a -> v'@ to values.
513weakenPatchDMapWithMoveWith
525weakenPatchDMapWithMoveWith
@@ -515,65 +527,65 @@ weakenPatchDMapWithMoveWith
515 . (forall a. PatchTarget1 p a -> PatchTarget p')
527 . (forall a. PatchTarget1 p a -> PatchTarget p')
516 -> (forall from to. p from to -> p')
528 -> (forall from to. p from to -> p')
517 -> PatchDMapWithMove k p
529 -> PatchDMapWithMove k p
518− -> PatchMapWithMove (Some k) p'
530+ -> PatchMapWithPatchingMove (Some k) p'
519weakenPatchDMapWithMoveWith f g (PatchDMapWithMove m) =
531weakenPatchDMapWithMoveWith f g (PatchDMapWithMove m) =
520− PatchMapWithMove $ weakenDMapWith h m
532+ PatchMapWithPatchingMove $ weakenDMapWith h m
521− where h :: forall a. NodeInfo k p a -> MapWithMove.NodeInfo (Some k) p'
533+ where h :: forall a. NodeInfo k p a -> MapWithPatchingMove.NodeInfo (Some k) p'
522− h ni = MapWithMove.NodeInfo
534+ h ni = MapWithPatchingMove.NodeInfo
523− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
535+ { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
524− From_Insert v -> MapWithMove.From_Insert $ f v
536+ From_Insert v -> MapWithPatchingMove.From_Insert $ f v
525− From_Delete -> MapWithMove.From_Delete
537+ From_Delete -> MapWithPatchingMove.From_Delete
526− From_Move (k :=> Flip p) -> MapWithMove.From_Move (Some k) $ g p
538+ From_Move (k :=> Flip p) -> MapWithPatchingMove.From_Move (mkSome k) $ g p
527− , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
539+ , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
528− To_NonMove -> MapWithMove.To_NonMove
540+ To_NonMove -> Nothing
529− To_Move (k :=> p) -> MapWithMove.To_Move (Some k) $ g p
541+ To_Move (k :=> _) -> Just (mkSome k)
530 }
542 }
531
543
532−-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
544+-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithPatchingMove' k
533-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
545-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
534-- dependency in the typing and all points are already @a@, hence the function
546-- dependency in the typing and all points are already @a@, hence the function
535-- to map each value to @v'@ is not higher rank.
547-- to map each value to @v'@ is not higher rank.
536−patchDMapWithMoveToPatchMapWithMoveWith
548+patchDMapWithMoveToPatchMapWithPatchingMoveWith
537 :: forall k p p' a
549 :: forall k p p' a
538 . (PatchTarget1 p a -> PatchTarget p')
550 . (PatchTarget1 p a -> PatchTarget p')
539 -> (p a a -> p')
551 -> (p a a -> p')
540 -> PatchDMapWithMove (Const2 k a) p
552 -> PatchDMapWithMove (Const2 k a) p
541− -> PatchMapWithMove k p'
553+ -> PatchMapWithPatchingMove k p'
542−patchDMapWithMoveToPatchMapWithMoveWith f g (PatchDMapWithMove m) =
554+patchDMapWithMoveToPatchMapWithPatchingMoveWith f g (PatchDMapWithMove m) =
543− PatchMapWithMove $ dmapToMapWith h m
555+ PatchMapWithPatchingMove $ dmapToMapWith h m
544− where h :: NodeInfo (Const2 k a) p a -> MapWithMove.NodeInfo k p'
556+ where h :: NodeInfo (Const2 k a) p a -> MapWithPatchingMove.NodeInfo k p'
545− h ni = MapWithMove.NodeInfo
557+ h ni = MapWithPatchingMove.NodeInfo
546− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
558+ { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
547− From_Insert v -> MapWithMove.From_Insert $ f v
559+ From_Insert v -> MapWithPatchingMove.From_Insert $ f v
548− From_Delete -> MapWithMove.From_Delete
560+ From_Delete -> MapWithPatchingMove.From_Delete
549− From_Move (Const2 k :=> Flip p) -> MapWithMove.From_Move k $ g p
561+ From_Move (Const2 k :=> Flip p) -> MapWithPatchingMove.From_Move k $ g p
550− , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
562+ , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
551− To_NonMove -> MapWithMove.To_NonMove
563+ To_NonMove -> Nothing
552− To_Move (Const2 k :=> p) -> MapWithMove.To_Move k $ g p
564+ To_Move (Const2 k :=> _) -> Just k
553 }
565 }
554
566
555−-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
567+-- | "Strengthen" a @'PatchMapWithPatchingMove' k v@ into a @'PatchDMapWithMove
556-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
568-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
557-- dependently typed one but which always has a constant key type represented by
569-- dependently typed one but which always has a constant key type represented by
558-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
570-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
559−-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
571+-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithPatchingMoveWith'
560const2PatchDMapWithMoveWith
572const2PatchDMapWithMoveWith
561 :: forall k v v' a
573 :: forall k v v' a
562 . (v -> v' a)
574 . (v -> v' a)
563− -> PatchMapWithMove k (Proxy v)
575+ -> PatchMapWithPatchingMove k (Proxy v)
564 -> PatchDMapWithMove (Const2 k a) (Proxy3 v')
576 -> PatchDMapWithMove (Const2 k a) (Proxy3 v')
565−const2PatchDMapWithMoveWith f (PatchMapWithMove p) =
577+const2PatchDMapWithMoveWith f (PatchMapWithPatchingMove p) =
566 PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
578 PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
567− where g :: (k, MapWithMove.NodeInfo k (Proxy v))
579+ where g :: (k, MapWithPatchingMove.NodeInfo k (Proxy v))
568 -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
580 -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
569 g (k, ni) = Const2 k :=> NodeInfo
581 g (k, ni) = Const2 k :=> NodeInfo
570− { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
582+ { _nodeInfo_from = case MapWithPatchingMove._nodeInfo_from ni of
571− MapWithMove.From_Insert v -> From_Insert $ f v
583+ MapWithPatchingMove.From_Insert v -> From_Insert $ f v
572− MapWithMove.From_Delete -> From_Delete
584+ MapWithPatchingMove.From_Delete -> From_Delete
573− MapWithMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
585+ MapWithPatchingMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
574− , _nodeInfo_to = case MapWithMove._nodeInfo_to ni of
586+ , _nodeInfo_to = case MapWithPatchingMove._nodeInfo_to ni of
575− MapWithMove.To_NonMove -> To_NonMove
587+ Nothing -> To_NonMove
576− MapWithMove.To_Move toKey Proxy -> To_Move $ Const2 toKey :=> Proxy3
588+ Just toKey -> To_Move $ Const2 toKey :=> Proxy3
577 }
589 }
578
590
579-- | Apply the insertions, deletions, and moves to a given 'DMap'.
591-- | Apply the insertions, deletions, and moves to a given 'DMap'.
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
@@ -58,7 +61,7 @@ mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k m
58-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
61-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
59-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
62-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
60traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
63traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
61−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
64+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
62
65
63-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
66-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
64patchIntMapNewElements :: PatchIntMap a -> [a]
67patchIntMapNewElements :: 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 => PatchHet (PatchMap k v) where
48instance Ord k => PatchHet (PatchMap k v) where
45 type PatchSource (PatchMap k v) = Map k v
49 type PatchSource (PatchMap k v) = Map k v
@@ -59,11 +63,6 @@ instance TraversableWithIndex k (PatchMap k) where
59 itraverse = itraversed . Indexed
63 itraverse = itraversed . Indexed
60 itraversed = _Wrapped .> itraversed <. traversed
64 itraversed = _Wrapped .> itraversed <. traversed
61
65
62−-- | The empty 'PatchMap' contains no insertions or deletions
63−instance Ord k => Monoid (PatchMap k v) where
64− mempty = PatchMap mempty
65− mappend = (<>)
66−
67-- | Returns all the new elements that will be added to the 'Map'
66-- | Returns all the new elements that will be added to the 'Map'
68patchMapNewElements :: PatchMap k v -> [v]
67patchMapNewElements :: PatchMap k v -> [v]
69patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
68patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
@@ -71,3 +70,5 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
71-- | Returns all the new elements that will be added to the 'Map'
70-- | Returns all the new elements that will be added to the 'Map'
72patchMapNewElementsMap :: PatchMap k v -> Map k v
71patchMapNewElementsMap :: PatchMap k v -> Map k v
73patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
72patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
73+
74+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+237−287
@@ -2,364 +2,314 @@
2{-# LANGUAGE DeriveTraversable #-}
2{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE PatternGuards #-}
8{-# LANGUAGE PatternGuards #-}
9+{-# LANGUAGE PatternSynonyms #-}
8{-# LANGUAGE ScopedTypeVariables #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE StandaloneDeriving #-}
11{-# LANGUAGE StandaloneDeriving #-}
10{-# LANGUAGE TemplateHaskell #-}
12{-# LANGUAGE TemplateHaskell #-}
11{-# LANGUAGE TypeApplications #-}
13{-# LANGUAGE TypeApplications #-}
12{-# LANGUAGE TypeFamilies #-}
14{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE UndecidableInstances #-}
15+{-# LANGUAGE ViewPatterns #-}
14
16
15-- | '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
16-- another
18-- another
17−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
18
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)
19import 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
20
68
21−import Control.Arrow
69+import Control.Lens
22−import Control.Lens hiding (from, to)
23−import Control.Monad.Trans.State
24−import Data.Foldable
25−import Data.Function
26import Data.List
70import Data.List
27import Data.Map (Map)
71import Data.Map (Map)
28import qualified Data.Map as Map
72import qualified Data.Map as Map
29−import Data.Maybe
73+import Data.Proxy
30−--import Data.Proxy
31−#if !MIN_VERSION_base(4,9,0)
74+#if !MIN_VERSION_base(4,11,0)
32−import Data.Semigroup (Semigroup (..), (<>))
75+import Data.Semigroup (Semigroup (..))
33#endif
76#endif
34−import Data.Monoid.DecidablyEmpty
77+import Data.Traversable (foldMapDefault)
35−import qualified Data.Set as Set
36−import Data.These (These (..))
37−import Data.Tuple
38
78
39-- | 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@
40-- 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@,
41-- 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.
42−newtype PatchMapWithMove k p = PatchMapWithMove
82+newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
43− { -- | Extract the internal representation of the 'PatchMapWithMove'
83+ { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
44− unPatchMapWithMove :: Map k (NodeInfo k p)
84+ unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
45 }
85 }
46−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithMove k p)
86+ deriving ( Show, Read, Eq, Ord
47−deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithMove k p)
87+-- Haddock cannot handle documentation here before GHC 8.6
48−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithMove k p)
88+ ,
49−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithMove k p)
89+#if __GLASGOW_HASKELL__ >= 806
90+ -- | Compose patches having the same effect as applying the
91+ -- patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p .
92+ -- 'applyAlways' q@
93+#endif
94+ Semigroup
95+ , Monoid
96+ )
50
97
51−-- -- | Skips the patch so it's 'Functor' and friends
98+pattern Coerce :: Coercible a b => a -> b
52−-- newtype PatchMapWithMove' k v = PatchMapWithMove' { unPatchMapWithMove' :: PatchMapWithMove k (Proxy v) }
99+pattern Coerce x <- (coerce -> x)
53−--
100+ where Coerce x = coerce x
54−-- deriving instance Functor (PatchMapWithMove' k)
55−-- deriving instance Foldable (PatchMapWithMove' k)
56−-- deriving instance Traversable (PatchMapWithMove' k)
57
101
58−-- | Holds the information about each key: where its new value should come from,
102+{-# COMPLETE PatchMapWithMove #-}
59−-- and where its old value should go to
103+pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
60−data NodeInfo k p = NodeInfo
104+-- | Extract the representation of the 'PatchMapWithMove' as a map of
61− { _nodeInfo_from :: !(From k p)
105+-- 'NodeInfo'.
62− -- ^ Where do we get the new value for this key?
106+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
63− , _nodeInfo_to :: !(To k p)
107+pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
64− -- ^ If the old value is being kept (i.e. moved rather than deleted or
65− -- replaced), where is it going?
66− }
67−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
68−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
69−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
70−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
71
108
72−-- | Describe how a key's new value should be produced
109+_PatchMapWithMove
73−data From k p
110+ :: Iso
74− = From_Insert (PatchTarget p) -- ^ Insert the given value here
111+ (PatchMapWithMove k0 v0)
75− | From_Delete -- ^ Delete the existing value, if any, from here
112+ (PatchMapWithMove k1 v1)
76− | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
113+ (Map k0 (NodeInfo k0 v0))
77−
114+ (Map k1 (NodeInfo k1 v1))
78−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
115+_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
79−deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
80−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
81−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
82−
83−-- | Describe where a key's old value will go. If this is 'Just', that means
84−-- the key's old value will be moved to the given other key; if it is 'Nothing',
85−-- that means it will be deleted.
86−data To k p
87− = To_NonMove
88− | To_Move !k !p
89− deriving ( Show, Read, Eq, Ord
90− , Functor, Foldable, Traversable
91− )
92
116
93−makeWrapped ''PatchMapWithMove
117+instance Functor (PatchMapWithMove k) where
118+ fmap f = runIdentity . traverse (Identity . f)
119+
94−--makeWrapped ''PatchMapWithMove'
120+instance Foldable (PatchMapWithMove k) where
121+ foldMap = foldMapDefault
95
122
123+instance Traversable (PatchMapWithMove k) where
124+ traverse =
125+ _PatchMapWithMove .
126+ traverse .
127+ traverse
128+
96−-- instance FunctorWithIndex k (PatchMapWithMove k)
129+instance FunctorWithIndex k (PatchMapWithMove k)
97−-- instance FoldableWithIndex k (PatchMapWithMove k)
130+instance FoldableWithIndex k (PatchMapWithMove k)
98−-- instance TraversableWithIndex k (PatchMapWithMove k) where
131+instance TraversableWithIndex k (PatchMapWithMove k) where
99−-- itraverse = itraversed . Indexed
132+ itraverse = itraversed . Indexed
100−-- itraversed = _Wrapped .> itraversed <. traversed
133+ itraversed =
134+ _PatchMapWithMove .>
135+ itraversed <.
136+ traverse
101
137
102-- | Create a 'PatchMapWithMove', validating it
138-- | Create a 'PatchMapWithMove', validating it
103−patchMapWithMove
104− :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithMove k p)
139+patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
105−patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
140+patchMapWithMove = fmap coerce . PM.patchMapWithPatchingMove . coerce
106− where valid = forwardLinks == backwardLinks
107− forwardLinks = flip Map.mapMaybe m $ \x -> case _nodeInfo_to x of
108− To_NonMove -> Nothing
109− To_Move to _ -> Just to
110− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
111− case _nodeInfo_from p of
112− From_Move from _ -> Just (from, to)
113− _ -> Nothing
114
141
115-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
142-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
116−patchMapWithMoveInsertAll
143+patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
117− :: Map k (PatchTarget p) -> PatchMapWithMove k p
118−patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
144+patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
119− { _nodeInfo_from = From_Insert v
120− , _nodeInfo_to = To_NonMove
121− }
122
145
123−-- | Make a @'PatchMapWithMove' k p@ which has the effect of inserting or replacing a value @v@ at 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'.
124−insertMapKey
125− :: k -> PatchTarget p -> PatchMapWithMove k p
147+insertMapKey :: k -> v -> PatchMapWithMove k v
126−insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) To_NonMove
148+insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
127
149
128−-- |Make a @'PatchMapWithMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
150+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
129--
151--
130-- @
152-- @
131-- '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))
132-- @
154-- @
133−moveMapKey
134− :: (DecidablyEmpty p, Patch p) => Ord k => k -> k -> PatchMapWithMove k p
155+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
135−moveMapKey src dst
156+moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
136− | src == dst = mempty
157+
137− | otherwise =
138− PatchMapWithMove $ Map.fromList
139− [ (dst, NodeInfo (From_Move src mempty) To_NonMove)
140− , (src, NodeInfo From_Delete (To_Move dst mempty))
141− ]
142−
143−-- |Make a @'PatchMapWithMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
158+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
144--
159--
145-- @
160-- @
146-- let aMay = Map.lookup a map
161-- let aMay = Map.lookup a map
147-- bMay = Map.lookup b map
162-- bMay = Map.lookup b map
148−-- in maybe id (Map.insert a) (bMay `mplus` aMay)
163+-- in maybe id (Map.insert a) (bMay <> aMay)
149−-- . maybe id (Map.insert b) (aMay `mplus` bMay)
164+-- . maybe id (Map.insert b) (aMay <> bMay)
150-- . Map.delete a . Map.delete b $ map
165-- . Map.delete a . Map.delete b $ map
151-- @
166-- @
152−swapMapKey
153− :: (DecidablyEmpty p, Patch p)
154− => Ord k => k -> k -> PatchMapWithMove k p
167+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
155−swapMapKey src dst
168+swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
156− | src == dst = mempty
169+
157− | otherwise =
158− PatchMapWithMove $ Map.fromList
159− [ (dst, NodeInfo (From_Move src mempty) (To_Move dst mempty))
160− , (src, NodeInfo (From_Move dst mempty) (To_Move dst mempty))
161− ]
162−
163−-- | Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in
170+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
164−-- the mapping, equivalent to 'Map.delete'.
165−deleteMapKey
166− :: k -> PatchMapWithMove k v
171+deleteMapKey :: k -> PatchMapWithMove k v
167−deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete To_NonMove
172+deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
168
173
169-- | 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.
170--
175--
171-- __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.
172−unsafePatchMapWithMove
173− :: Map k (NodeInfo k p) -> PatchMapWithMove k p
177+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
174−unsafePatchMapWithMove = PatchMapWithMove
178+unsafePatchMapWithMove = PatchMapWithMove' . PM.unsafePatchMapWithPatchingMove . coerce
175
179
176-- | Apply the insertions, deletions, and moves to a given 'Map'
180-- | Apply the insertions, deletions, and moves to a given 'Map'
177−instance (Ord k, Patch p) => PatchHet (PatchMapWithMove k p) where
181+instance Ord k => PatchHet (PatchMapWithMove k v) where
178− type PatchSource (PatchMapWithMove k p) = Map k (PatchSource p)
182+ type PatchSource (PatchMapWithMove k v) = Map k v
179− type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
183+ type PatchTarget (PatchMapWithMove k v) = Map k v
180−
184+
181−instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) where
185+instance Ord k => Patch (PatchMapWithMove k v) where
182− -- TODO: return Nothing sometimes
183− -- Note: the strict application here is critical to ensuring that incremental
184− -- merges don't hold onto all their prerequisite events forever; can we make
185− -- this more robust?
186− apply (PatchMapWithMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
186+ apply (PatchMapWithMove' p) = apply p
187− where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
187+
188− From_Insert v -> Just v
189− From_Move k p -> applyAlways p <$> Map.lookup k old
190− From_Delete -> Nothing
191− deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
192− From_Delete -> Just ()
193− _ -> Nothing
194−
195−-- | Returns all the new elements that will be added to the 'Map'
188+-- | Returns all the new elements that will be added to the 'Map'.
196−patchMapWithMoveNewElements
189+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
197− :: PatchMapWithMove k p -> [PatchTarget p]
190+patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
198−patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
199
191
200-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
192-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
201−patchMapWithMoveNewElementsMap
193+patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
202− :: PatchMapWithMove k p -> Map k (PatchTarget p)
203−patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
194+patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
204− where f ni = case _nodeInfo_from ni of
205− From_Insert v -> Just v
206− From_Move _ _ -> Nothing
207− From_Delete -> Nothing
208
195
209-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
196-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
210-- its values using the given ordering function. The set keys of the 'Map' is
197-- its values using the given ordering function. The set keys of the 'Map' is
211-- not changed.
198-- not changed.
212−patchThatSortsMapWith
199+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
213− :: (Ord k, Monoid p)
214− => (PatchTarget p -> PatchTarget p -> Ordering)
215− -> Map k (PatchTarget p) -> PatchMapWithMove k p
216−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
200+patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
217− where unsorted = Map.toList m
218− sorted = sortBy (cmp `on` snd) unsorted
219− f (to, _) (from, _) = if to == from then Nothing else
220− Just (from, to)
221− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
222− g (to, _) (from, _) = if to == from then Nothing else
223− let Just movingTo = Map.lookup from reverseMapping
224− in Just (to, NodeInfo (From_Move from mempty) $ To_Move movingTo mempty)
225
201
226-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
202-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
227-- will produce a 'Map' with the same values as the second 'Map' but with the
203-- will produce a 'Map' with the same values as the second 'Map' but with the
228-- values sorted with the given ordering function.
204-- values sorted with the given ordering function.
229−patchThatChangesAndSortsMapWith
205+patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
230− :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
231− => (PatchTarget p -> PatchTarget p -> Ordering)
232− -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
233patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
206patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
234 where newList = Map.toList newByIndexUnsorted
207 where newList = Map.toList newByIndexUnsorted
235 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
208 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
236
209
237-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
210-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
238-- will produce the second 'Map'.
211-- will produce the second 'Map'.
239−patchThatChangesMap
212+patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
240− :: (Ord k, Ord (PatchTarget p), Monoid p)
241− => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithMove k p
242−patchThatChangesMap oldByIndex newByIndex = patch
213+patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
243− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
214+ PM.patchThatChangesMap oldByIndex newByIndex
244− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
215+
245− let f k v = do
216+--
246− remainingValues <- get
217+-- NodeInfo
247− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
218+--
248− then Map.delete v remainingValues
219+
249− else Map.insert v remainingKeys remainingValues
220+-- | Holds the information about each key: where its new value should come from,
250− case Map.lookup v remainingValues of
221+-- and where its old value should go to
251− Nothing -> return $ NodeInfo (From_Insert v) $ To_Move undefined undefined -- There's no existing value we can take
222+newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
252− Just fromKs ->
223+
253− if k `Set.member` fromKs
224+deriving instance (Show k, Show p) => Show (NodeInfo k p)
254− then do
225+deriving instance (Read k, Read p) => Read (NodeInfo k p)
255− putRemainingKeys $ Set.delete k fromKs
226+deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
256− return $ NodeInfo (From_Move k mempty) $ To_Move undefined undefined -- There's an existing value, and it's here, so no patch necessary
227+deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
257− else do
228+
258− (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
229+{-# COMPLETE NodeInfo #-}
259− putRemainingKeys remainingKeys
230+pattern NodeInfo :: To k -> From k v -> NodeInfo k v
260− return $ NodeInfo (From_Move fromK mempty) $ To_Move undefined undefined
231+_nodeInfo_to :: NodeInfo k v -> To k
261− Map.traverseWithKey f newByIndex
232+_nodeInfo_from :: NodeInfo k v -> From k v
262− unusedOldKeys = fold unusedValuesByValue
233+pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
263− pointlessMove k = \case
234+ PM.NodeInfo
264− From_Move k' _ | k == k' -> True
235+ { PM._nodeInfo_to = _nodeInfo_to
265− _ -> False
236+ , PM._nodeInfo_from = Coerce _nodeInfo_from
266− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
237+ }
267− then To_Move undefined undefined
238+
268− else To_NonMove
239+_NodeInfo
269− patch = unsafePatchMapWithMove $ Map.filterWithKey
240+ :: Iso
270− (\k -> not . pointlessMove k . _nodeInfo_from)
241+ (NodeInfo k0 v0)
271− $ Map.mergeWithKey
242+ (NodeInfo k1 v1)
272− (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a)
243+ (PM.NodeInfo k0 (Proxy v0))
273− (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k)
244+ (PM.NodeInfo k1 (Proxy v1))
274− (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k)
245+_NodeInfo = iso unNodeInfo' NodeInfo'
275− insertsAndMoves
246+
276− oldByIndex
247+instance Functor (NodeInfo k) where
248+ fmap f = runIdentity . traverse (Identity . f)
249+
250+instance Foldable (NodeInfo k) where
251+ foldMap = foldMapDefault
252+
253+instance Traversable (NodeInfo k) where
254+ traverse = bitraverseNodeInfo pure
255+
256+bitraverseNodeInfo
257+ :: Applicative f
258+ => (k0 -> f k1)
259+ -> (v0 -> f v1)
260+ -> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
261+bitraverseNodeInfo fk fv = fmap coerce
262+ . PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
263+ . coerce
277
264
278-- | Change the 'From' value of a 'NodeInfo'
265-- | Change the 'From' value of a 'NodeInfo'
279−nodeInfoMapFrom
280− :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
266+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
281−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
267+nodeInfoMapFrom f = coerce . PM.nodeInfoMapFrom (unFrom' . f . From') . coerce
282
268
283-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
269-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
284-- 'Applicative', 'Monad', etc.) action to get the new value
270-- 'Applicative', 'Monad', etc.) action to get the new value
285nodeInfoMapMFrom
271nodeInfoMapMFrom
272+ :: Functor f
273+ => (From k v -> f (From k v))
286− :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
274+ -> NodeInfo k v -> f (NodeInfo k v)
287−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
275+nodeInfoMapMFrom f = fmap coerce
276+ . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
277+ . coerce
288
278
289-- | Set the 'To' field of a 'NodeInfo'
279-- | Set the 'To' field of a 'NodeInfo'
290−nodeInfoSetTo
291− :: To k p -> NodeInfo k p -> NodeInfo k p
280+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
292−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
281+nodeInfoSetTo = coerce . PM.nodeInfoSetTo
293−
282+
294−-- | Helper data structure used for composing patches using the monoid instance.
283+--
295−data Fixup k p
284+-- From
296− = Fixup_Delete
285+--
297− | Fixup_Update (These (From k p) (To k p))
286+
298−
287+-- | Describe how a key's new value should be produced
299−-- | Compose patches having the same effect as applying the patches in turn:
288+newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
300−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
289+
301−instance ( Ord k
290+{-# COMPLETE From_Insert, From_Delete, From_Move #-}
302− , DecidablyEmpty p
291+
303− , Patch p
292+-- | Insert the given value here
304− ) => Semigroup (PatchMapWithMove k p) where
293+pattern From_Insert :: v -> From k v
305− PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
294+pattern From_Insert v = From' (PM.From_Insert v)
306− where
295+
307− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
296+-- | Delete the existing value, if any, from here
308− h :: (k, (To k p, From k p)) -> [(k, Fixup k p)]
297+pattern From_Delete :: From k v
309− h (_, (editAfter, editBefore)) = case (editAfter, editBefore) of
298+pattern From_Delete = From' PM.From_Delete
310− (To_Move toAfter p1, From_Move fromBefore p0)
299+
311− | fromBefore == toAfter && isNull p0
300+-- | Move the value here from the given key
312− -> [(toAfter, Fixup_Delete)]
301+pattern From_Move :: k -> From k v
313− | otherwise
302+pattern From_Move k = From' (PM.From_Move k Proxy)
314− -> [ (toAfter, Fixup_Update (This $ From_Move fromBefore $ p1 <> p0))
303+
315− , (fromBefore, Fixup_Update (That $ To_Move toAfter $ p1 <> p0))
304+bitraverseFrom
316− ]
305+ :: Applicative f
317− (To_NonMove, From_Move fromBefore _) ->
306+ => (k0 -> f k1)
318− -- The item is destroyed in the second patch, so indicate that it is
307+ -> (v0 -> f v1)
319− -- destroyed in the source map
308+ -> From k0 v0 -> f (From k1 v1)
320− [(fromBefore, Fixup_Update (That editAfter))]
309+bitraverseFrom fk fv = fmap coerce
321− (To_Move toAfter p, From_Insert val) ->
310+ . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
322− [(toAfter, Fixup_Update $ This $ From_Insert $ applyAlways p val)]
311+ . coerce
323− (To_Move toAfter _, From_Delete) ->
312+
324− [(toAfter, Fixup_Update $ This From_Delete)]
313+makeWrapped ''PatchMapWithMove
325− (To_NonMove, _) -> []
314+makeWrapped ''NodeInfo
326− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
315+makeWrapped ''From
327− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
328− | This x <- a, That y <- b
329− = Fixup_Update $ These x y
330− | That y <- a, This x <- b
331− = Fixup_Update $ These x y
332− mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
333− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
334− combineNodeInfos _ nia nib = NodeInfo
335− { _nodeInfo_from = _nodeInfo_from nia
336− , _nodeInfo_to = _nodeInfo_to nib
337− }
338− applyFixup _ ni = \case
339− Fixup_Delete -> Nothing
340− Fixup_Update u -> Just $ NodeInfo
341− { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
342− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
343− }
344− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
345− getHere :: These a b -> Maybe a
346− getHere = \case
347− This a -> Just a
348− These a _ -> Just a
349− That _ -> Nothing
350− getThere :: These a b -> Maybe b
351− getThere = \case
352− This _ -> Nothing
353− These _ b -> Just b
354− That b -> Just b
355−
356−--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
357−-- | Compose patches having the same effect as applying the patches in turn:
358−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
359−instance ( Ord k
360− , Monoid p
361− , DecidablyEmpty p
362− , Patch p
363− ) => Monoid (PatchMapWithMove k p) where
364− mempty = PatchMapWithMove mempty
365− mappend = (<>)
src/Data/Patch/MapWithPatchingMove.hsnew file+434
@@ -0,0 +1,434 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE StandaloneDeriving #-}
10+{-# LANGUAGE TemplateHaskell #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+
14+-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15+-- another
16+module Data.Patch.MapWithPatchingMove
17+ ( PatchMapWithPatchingMove (..)
18+ , patchMapWithPatchingMove
19+ , patchMapWithPatchingMoveInsertAll
20+ , insertMapKey
21+ , moveMapKey
22+ , swapMapKey
23+ , deleteMapKey
24+ , unsafePatchMapWithPatchingMove
25+ , patchMapWithPatchingMoveNewElements
26+ , patchMapWithPatchingMoveNewElementsMap
27+ , patchThatSortsMapWith
28+ , patchThatChangesAndSortsMapWith
29+ , patchThatChangesMap
30+
31+ -- * Node Info
32+ , NodeInfo (..)
33+ , bitraverseNodeInfo
34+ , nodeInfoMapFrom
35+ , nodeInfoMapMFrom
36+ , nodeInfoSetTo
37+
38+ -- * From
39+ , From(..)
40+ , bitraverseFrom
41+
42+ -- * To
43+ , To
44+
45+ -- TODO internals module
46+ , Fixup (..)
47+ ) where
48+
49+import Data.Patch.Class
50+
51+import Control.Arrow
52+import Control.Lens.TH (makeWrapped)
53+import Control.Monad.Trans.State
54+import Data.Foldable
55+import Data.Function
56+import Data.List
57+import Data.Map (Map)
58+import qualified Data.Map as Map
59+import Data.Maybe
60+#if !MIN_VERSION_base(4,11,0)
61+import Data.Semigroup (Semigroup (..))
62+#endif
63+import Data.Monoid.DecidablyEmpty
64+import qualified Data.Set as Set
65+import Data.These (These (..))
66+import Data.Tuple
67+
68+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
69+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
70+-- and vice versa. There should never be any unpaired From/To keys.
71+newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
72+ { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
73+ unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
74+ }
75+
76+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
77+deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
78+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
79+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
80+
81+deriving instance ( Ord k
82+#if !MIN_VERSION_base(4,11,0)
83+ , Semigroup p
84+#endif
85+ , DecidablyEmpty p
86+ , Patch p
87+ ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
88+
89+-- | Create a 'PatchMapWithPatchingMove', validating it
90+patchMapWithPatchingMove
91+ :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
92+patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
93+ where valid = forwardLinks == backwardLinks
94+ forwardLinks = Map.mapMaybe _nodeInfo_to m
95+ backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
96+ case _nodeInfo_from p of
97+ From_Move from _ -> Just (from, to)
98+ _ -> Nothing
99+
100+-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
101+patchMapWithPatchingMoveInsertAll
102+ :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
103+patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
104+ { _nodeInfo_from = From_Insert v
105+ , _nodeInfo_to = Nothing
106+ }
107+
108+-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109+insertMapKey
110+ :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
111+insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
112+
113+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
114+--
115+-- @
116+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
117+-- @
118+moveMapKey
119+ :: ( DecidablyEmpty p
120+#if !MIN_VERSION_base(4,11,0)
121+ , Semigroup p
122+#endif
123+ , Patch p
124+ )
125+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
126+moveMapKey src dst
127+ | src == dst = mempty
128+ | otherwise =
129+ PatchMapWithPatchingMove $ Map.fromList
130+ [ (dst, NodeInfo (From_Move src mempty) Nothing)
131+ , (src, NodeInfo From_Delete (Just dst))
132+ ]
133+
134+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
135+--
136+-- @
137+-- let aMay = Map.lookup a map
138+-- bMay = Map.lookup b map
139+-- in maybe id (Map.insert a) (bMay <> aMay)
140+-- . maybe id (Map.insert b) (aMay <> bMay)
141+-- . Map.delete a . Map.delete b $ map
142+-- @
143+swapMapKey
144+ :: ( DecidablyEmpty p
145+#if !MIN_VERSION_base(4,11,0)
146+ , Semigroup p
147+#endif
148+ , Patch p
149+ )
150+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
151+swapMapKey src dst
152+ | src == dst = mempty
153+ | otherwise =
154+ PatchMapWithPatchingMove $ Map.fromList
155+ [ (dst, NodeInfo (From_Move src mempty) (Just src))
156+ , (src, NodeInfo (From_Move dst mempty) (Just dst))
157+ ]
158+
159+-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
160+-- the mapping, equivalent to 'Map.delete'.
161+deleteMapKey
162+ :: k -> PatchMapWithPatchingMove k v
163+deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
164+
165+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
166+--
167+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
168+unsafePatchMapWithPatchingMove
169+ :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
170+unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
171+
172+-- | Apply the insertions, deletions, and moves to a given 'Map'
173+instance (Ord k, Patch p) => PatchHet (PatchMapWithPatchingMove k p) where
174+ type PatchSource (PatchMapWithPatchingMove k p) = Map k (PatchSource p)
175+ type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
176+
177+instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
178+ -- TODO: return Nothing sometimes
179+ -- Note: the strict application here is critical to ensuring that incremental
180+ -- merges don't hold onto all their prerequisite events forever; can we make
181+ -- this more robust?
182+ apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
183+ where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
184+ From_Insert v -> Just v
185+ From_Move k p -> applyAlways p <$> Map.lookup k old
186+ From_Delete -> Nothing
187+ deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
188+ From_Delete -> Just ()
189+ _ -> Nothing
190+
191+-- | Returns all the new elements that will be added to the 'Map'
192+patchMapWithPatchingMoveNewElements
193+ :: PatchMapWithPatchingMove k p -> [PatchTarget p]
194+patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
195+
196+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
197+patchMapWithPatchingMoveNewElementsMap
198+ :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
199+patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
200+ where f ni = case _nodeInfo_from ni of
201+ From_Insert v -> Just v
202+ From_Move _ _ -> Nothing
203+ From_Delete -> Nothing
204+
205+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
206+-- its values using the given ordering function. The set keys of the 'Map' is
207+-- not changed.
208+patchThatSortsMapWith
209+ :: (Ord k, Monoid p)
210+ => (PatchTarget p -> PatchTarget p -> Ordering)
211+ -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
212+patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
213+ where unsorted = Map.toList m
214+ sorted = sortBy (cmp `on` snd) unsorted
215+ f (to, _) (from, _) = if to == from then Nothing else
216+ Just (from, to)
217+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
218+ g (to, _) (from, _) = if to == from then Nothing else
219+ let Just movingTo = Map.lookup from reverseMapping
220+ in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
221+
222+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
223+-- will produce a 'Map' with the same values as the second 'Map' but with the
224+-- values sorted with the given ordering function.
225+patchThatChangesAndSortsMapWith
226+ :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
227+ => (PatchTarget p -> PatchTarget p -> Ordering)
228+ -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
229+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
230+ where newList = Map.toList newByIndexUnsorted
231+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
232+
233+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
234+-- will produce the second 'Map'.
235+patchThatChangesMap
236+ :: (Ord k, Ord (PatchTarget p), Monoid p)
237+ => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
238+patchThatChangesMap oldByIndex newByIndex = patch
239+ where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
240+ (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
241+ let f k v = do
242+ remainingValues <- get
243+ let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
244+ then Map.delete v remainingValues
245+ else Map.insert v remainingKeys remainingValues
246+ case Map.lookup v remainingValues of
247+ Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
248+ Just fromKs ->
249+ if k `Set.member` fromKs
250+ then do
251+ putRemainingKeys $ Set.delete k fromKs
252+ return $ NodeInfo (From_Move k mempty) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
253+ else do
254+ (fromK, remainingKeys) <- return $
255+ fromMaybe (error "PatchMapWithPatchingMove.patchThatChangesMap: impossible: fromKs was empty") $
256+ Set.minView fromKs -- There's an existing value, but it's not here; move it here
257+ putRemainingKeys remainingKeys
258+ return $ NodeInfo (From_Move fromK mempty) $ Just undefined
259+ Map.traverseWithKey f newByIndex
260+ unusedOldKeys = fold unusedValuesByValue
261+ pointlessMove k = \case
262+ From_Move k' _ | k == k' -> True
263+ _ -> False
264+ keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
265+ then Just undefined
266+ else Nothing
267+ patch = unsafePatchMapWithPatchingMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
268+
269+--
270+-- NodeInfo
271+--
272+
273+-- | Holds the information about each key: where its new value should come from,
274+-- and where its old value should go to
275+data NodeInfo k p = NodeInfo
276+ { _nodeInfo_from :: !(From k p)
277+ -- ^ Where do we get the new value for this key?
278+ , _nodeInfo_to :: !(To k)
279+ -- ^ If the old value is being kept (i.e. moved rather than deleted or
280+ -- replaced), where is it going?
281+ }
282+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
283+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
284+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
285+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
286+
287+-- | Traverse the 'NodeInfo' over the key, patch, and patch target. Because of
288+-- the type families here, this doesn't it any bi- or tri-traversal class.
289+bitraverseNodeInfo
290+ :: Applicative f
291+ => (k0 -> f k1)
292+ -> (p0 -> f p1)
293+ -> (PatchTarget p0 -> f (PatchTarget p1))
294+ -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
295+bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
296+ <$> bitraverseFrom fk fp fpt from
297+ <*> traverse fk to
298+
299+-- | Change the 'From' value of a 'NodeInfo'
300+nodeInfoMapFrom
301+ :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
302+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
303+
304+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
305+-- 'Applicative', 'Monad', etc.) action to get the new value
306+nodeInfoMapMFrom
307+ :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
308+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
309+
310+-- | Set the 'To' field of a 'NodeInfo'
311+nodeInfoSetTo
312+ :: To k -> NodeInfo k v -> NodeInfo k v
313+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
314+
315+--
316+-- From
317+--
318+
319+-- | Describe how a key's new value should be produced
320+data From k p
321+ = From_Insert (PatchTarget p) -- ^ Insert the given value here
322+ | From_Delete -- ^ Delete the existing value, if any, from here
323+ | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
324+
325+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
326+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
327+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
328+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
329+
330+-- | Traverse the 'From' over the key, patch, and patch target. Because of
331+-- the type families here, this doesn't it any bi- or tri-traversal class.
332+bitraverseFrom
333+ :: Applicative f
334+ => (k0 -> f k1)
335+ -> (p0 -> f p1)
336+ -> (PatchTarget p0 -> f (PatchTarget p1))
337+ -> From k0 p0 -> f (From k1 p1)
338+bitraverseFrom fk fp fpt = \case
339+ From_Insert pt -> From_Insert <$> fpt pt
340+ From_Delete -> pure From_Delete
341+ From_Move k p -> From_Move <$> fk k <*> fp p
342+
343+--
344+-- To
345+--
346+
347+-- | Describe where a key's old value will go. If this is 'Just', that means
348+-- the key's old value will be moved to the given other key; if it is 'Nothing',
349+-- that means it will be deleted.
350+type To = Maybe
351+
352+--
353+-- Fixup
354+--
355+
356+-- | Helper data structure used for composing patches using the monoid instance.
357+data Fixup k v
358+ = Fixup_Delete
359+ | Fixup_Update (These (From k v) (To k))
360+
361+-- | Compose patches having the same effect as applying the patches in turn:
362+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
363+instance ( Ord k
364+#if !MIN_VERSION_base(4,11,0)
365+ , Semigroup p
366+#endif
367+ , DecidablyEmpty p
368+ , Patch p
369+ ) => Semigroup (PatchMapWithPatchingMove k p) where
370+ PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
371+ where
372+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
373+ h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
374+ h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
375+ (Just toAfter, From_Move fromBefore p)
376+ | fromBefore == toAfter && isEmpty p
377+ -> [(toAfter, Fixup_Delete)]
378+ | otherwise
379+ -> [ (toAfter, Fixup_Update (This editBefore))
380+ , (fromBefore, Fixup_Update (That mToAfter))
381+ ]
382+ (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
383+ (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
384+ (Nothing, _) -> []
385+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
386+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
387+ | This x <- a, That y <- b
388+ = Fixup_Update $ These x y
389+ | That y <- a, This x <- b
390+ = Fixup_Update $ These x y
391+ mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
392+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
393+ combineNodeInfos _ nia nib = NodeInfo
394+ { _nodeInfo_from = _nodeInfo_from nia
395+ , _nodeInfo_to = _nodeInfo_to nib
396+ }
397+ applyFixup _ ni = \case
398+ Fixup_Delete -> Nothing
399+ Fixup_Update u -> Just $ NodeInfo
400+ { _nodeInfo_from = case _nodeInfo_from ni of
401+ f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
402+ Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
403+ Just (From_Insert v) -> From_Insert $ applyAlways p' v
404+ Just From_Delete -> From_Delete
405+ Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
406+ _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
407+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
408+ }
409+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
410+ getHere :: These a b -> Maybe a
411+ getHere = \case
412+ This a -> Just a
413+ These a _ -> Just a
414+ That _ -> Nothing
415+ getThere :: These a b -> Maybe b
416+ getThere = \case
417+ This _ -> Nothing
418+ These _ b -> Just b
419+ That b -> Just b
420+
421+--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
422+-- | Compose patches having the same effect as applying the patches in turn:
423+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
424+instance ( Ord k
425+#if !MIN_VERSION_base(4,11,0)
426+ , Semigroup p
427+#endif
428+ , DecidablyEmpty p
429+ , Patch p
430+ ) => Monoid (PatchMapWithPatchingMove k p) where
431+ mempty = PatchMapWithPatchingMove mempty
432+ mappend = (<>)
433+
434+makeWrapped ''PatchMapWithPatchingMove
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