on develop help sign in
Merge branch 'patch-map-inner-patch-desugar' into dpatch-map-inner-patch
commit6ef716a1668d03736f65a5ddaea54c9e1123c677 authorJohn Ericson <John.Ericson@Obsidian.Systems> dateThu 22 Apr 2021, 22:22 (5 years ago) parent584334f32a1f2f
16 files changed+983−378
.github/workflows/haskell.yml+47 CONTRIBUTING.md+1−1 ChangeLog.md+24 README.md+6−4 patch.cabal+16−10 src/Data/Functor/Misc.hs+2−3 src/Data/Monoid/DecidablyEmpty.hs+111−1 src/Data/Patch.hs+3−3 src/Data/Patch/Class.hs+10−5 src/Data/Patch/DMap.hs+12−1 src/Data/Patch/DMapWithMove.hs+59−47 src/Data/Patch/IntMap.hs+10−7 src/Data/Patch/Map.hs+9−8 src/Data/Patch/MapWithMove.hs+237−287 src/Data/Patch/MapWithPatchingMove.hs+434 test/hlint.hs+2−1
.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
1616* Describe the issue you're encountering or the suggestion you're making
1717* 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.
1818* 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.
2020
2121## Submitting Changes
2222
ChangeLog.md+24
@@ -6,6 +6,30 @@
66
77* `PatchDMapWithMove` supports moves with a patch.
88
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+
933## 0.0.2.0
1034
1135* Consistently provide:
README.md+6−4
@@ -1,8 +1,10 @@
1−## Patch
1+# patch
22
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)
44
5−A `Patch` type represents a kind of change made to a datastructure.
5+Data structures for describing changes to other data structures.
6+
7+A `Patch` type represents a kind of change made to a data structure.
68
79```haskell
810class Patch p where
@@ -12,7 +14,7 @@ class Patch p where
1214 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
1315```
1416
15−### Patching Maps
17+## Patching Maps
1618For 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).
1719
1820`PatchMap` is defined as:
patch.cabal+16−10
@@ -1,7 +1,9 @@
11Name: patch
2−Version: 0.0.2.0
3−Synopsis: Infrastructure for writing patches which act on other types.
2+Version: 0.0.4.0
3+Synopsis: Data structures for describing changes to other data structures.
44Description:
5+ Data structures for describing changes to other data structures.
6+ .
57 In this library, a patch is something which can be applied, analogous to a
68 function, and which distinguishes returning the argument it was provided from
79 returning something else.
@@ -12,7 +14,7 @@ Maintainer: maintainer@obsidian.systems
1214Stability: Experimental
1315Category: FRP
1416Build-type: Simple
15−Cabal-version: >=1.9.2
17+Cabal-version: >=1.10
1618homepage: https://obsidian.systems
1719bug-reports: https://github.com/reflex-frp/patch/issues
1820extra-source-files:
@@ -20,7 +22,7 @@ extra-source-files:
2022 ChangeLog.md
2123
2224tested-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
2426 GHCJS ==8.4
2527
2628flag split-these
@@ -30,15 +32,16 @@ flag split-these
3032
3133library
3234 hs-source-dirs: src
33− build-depends: base >= 4.9 && < 4.14
35+ default-language: Haskell2010
36+ build-depends: base >= 4.9 && < 4.15
3437 , constraints-extras >= 0.3 && < 0.4
3538 , containers >= 0.6 && < 0.7
36− , dependent-map >= 0.3 && < 0.4
37− , dependent-sum >= 0.6 && < 0.7
39+ , dependent-map >= 0.3 && < 0.5
40+ , dependent-sum >= 0.6 && < 0.8
3841 , lens >= 4.7 && < 5
3942 , semigroupoids >= 4.0 && < 6
4043 , transformers >= 0.5.6.0 && < 0.6
41− , witherable >= 0.3 && < 0.3.2
44+ , witherable >= 0.3 && < 0.4
4245
4346 exposed-modules: Control.Category.DecidablyEmpty
4447 , Data.Functor.Misc
@@ -51,11 +54,13 @@ library
5154 , Data.Patch.IntMap
5255 , Data.Patch.Map
5356 , Data.Patch.MapWithMove
57+ , Data.Patch.MapWithPatchingMove
5458
5559 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
60+ default-extensions: PolyKinds
5661
5762 if flag(split-these)
58− build-depends: these >= 1 && <1.1
63+ build-depends: these >= 1 && <1.2
5964 , these-lens >= 1 && <1.1
6065 , semialign >=1 && <1.2
6166 , monoidal-containers >= 0.6 && < 0.7
@@ -64,6 +69,7 @@ library
6469 , monoidal-containers == 0.4.0.0
6570
6671test-suite hlint
72+ default-language: Haskell2010
6773 type: exitcode-stdio-1.0
6874 main-is: hlint.hs
6975 hs-source-dirs: test
@@ -71,7 +77,7 @@ test-suite hlint
7177 , directory
7278 , filepath
7379 , filemanip
74− , hlint < 2.1 || >= 2.2.2
80+ , hlint (< 2.1 || >= 2.2.2) && < 3.3
7581 if impl(ghcjs)
7682 buildable: False
7783
src/Data/Functor/Misc.hs+2−3
@@ -6,7 +6,6 @@
66{-# LANGUAGE GADTs #-}
77{-# LANGUAGE LambdaCase #-}
88{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
109{-# LANGUAGE PolyKinds #-}
1110{-# LANGUAGE RankNTypes #-}
1211{-# LANGUAGE ScopedTypeVariables #-}
@@ -52,7 +51,7 @@ import qualified Data.IntMap as IntMap
5251import Data.Map (Map)
5352import qualified Data.Map as Map
5453import qualified Data.Semigroupoid as Cat
55−import Data.Some (Some(Some))
54+import Data.Some (Some, mkSome)
5655import Data.These
5756import Data.Type.Equality ((:~:)(Refl))
5857import Data.Typeable hiding (Refl)
@@ -140,7 +139,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
140139-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
141140-- the keys, using a function to remove the wrapping 'Functor'
142141weakenDMapWith :: (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
144143
145144--------------------------------------------------------------------------------
146145-- 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+
17-- TODO upstream somwhere else?
28module Data.Monoid.DecidablyEmpty where
39
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.
438class 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 (..))
1818import Data.Functor.Identity
1919import Data.Map.Monoidal (MonoidalMap)
2020import Data.Proxy
21−#if !MIN_VERSION_base(4,9,0)
22−import Data.Semigroup (Semigroup (..), (<>))
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
2323#endif
2424import GHC.Generics
2525
@@ -27,7 +27,7 @@ import Data.Patch.Class as X
2727import Data.Patch.DMap as X hiding (getDeletions)
2828import Data.Patch.DMapWithMove as X
2929 ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
30− , patchDMapWithMoveToPatchMapWithMoveWith
30+ , patchDMapWithMoveToPatchMapWithPatchingMoveWith
3131 , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
3232 , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
3333 )
src/Data/Patch/Class.hs+10−5
@@ -1,9 +1,11 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DefaultSignatures #-}
23{-# LANGUAGE DerivingStrategies #-}
34{-# LANGUAGE GeneralizedNewtypeDeriving #-}
45{-# LANGUAGE FlexibleInstances #-}
56{-# LANGUAGE MultiParamTypeClasses #-}
67{-# LANGUAGE PolyKinds #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
79{-# LANGUAGE TypeFamilies #-}
810{-# LANGUAGE TypeOperators #-}
911-- | The interface for types which represent changes made to other types
@@ -12,14 +14,17 @@ module Data.Patch.Class where
1214import qualified Data.Semigroupoid as Cat
1315import Data.Functor.Identity
1416import Data.Functor.Misc
17+import Data.Kind (Type)
1518import Data.Maybe
19+#if !MIN_VERSION_base(4,11,0)
1620import Data.Semigroup (Semigroup(..))
21+#endif
1722import Data.Proxy
1823import Data.Type.Equality ((:~:) (..))
1924
2025class PatchHet p where
21− type PatchSource p :: *
22− type PatchTarget p :: *
26+ type PatchSource p :: Type
27+ type PatchTarget p :: Type
2328 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
2429 -- 'Nothing'.
2530 applyHet
@@ -63,11 +68,11 @@ instance PatchHet (Identity a) where
6368instance Patch (Identity a) where
6469 apply (Identity a) _ = Just a
6570
66−-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
67−instance PatchHet (Proxy (a :: *)) where
71+-- | 'Proxy' can be used as a 'Patch' that does nothing.
72+instance forall (a :: Type). PatchHet (Proxy a) where
6873 type PatchSource (Proxy a) = a
6974 type PatchTarget (Proxy a) = a
70−instance Patch (Proxy (a :: *)) where
75+instance forall (a :: Type). Patch (Proxy a) where
7176 apply ~Proxy _ = Nothing
7277
7378-- | Like '(.)', but composes functions that return patches rather than
src/Data/Patch/DMap.hs+12−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23{-# LANGUAGE LambdaCase #-}
34{-# LANGUAGE PolyKinds #-}
@@ -5,6 +6,7 @@
56{-# LANGUAGE ScopedTypeVariables #-}
67{-# LANGUAGE StandaloneDeriving #-}
78{-# LANGUAGE TypeFamilies #-}
9+
810-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
911module Data.Patch.DMap where
1012
@@ -12,13 +14,18 @@ import Data.Patch.Class
1214import Data.Patch.IntMap
1315import Data.Patch.Map
1416
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 (..))
1620import qualified Data.Dependent.Map as DMap
1721import Data.Functor.Constant
1822import Data.Functor.Misc
1923import qualified Data.IntMap as IntMap
2024import qualified Data.Map as Map
25+import Data.Monoid.DecidablyEmpty
26+#if !MIN_VERSION_base(4,11,0)
2127import Data.Semigroup (Semigroup (..))
28+#endif
2229import Data.Some (Some)
2330
2431-- | 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)
3037
3138deriving instance GCompare k => Monoid (PatchDMap k v)
3239
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+
3344-- | Apply the insertions or deletions to a given 'DMap'.
3445instance GCompare k => PatchHet (PatchDMap k v) where
3546 type PatchSource (PatchDMap k v) = DMap k v
src/Data/Patch/DMapWithMove.hs+59−47
@@ -21,6 +21,7 @@ module Data.Patch.DMapWithMove where
2121
2222import qualified Control.Category as Cat
2323--import qualified Control.Category.DecidablyEmpty as Cat
24+
2425import Data.Constraint.Extras (Has')
2526import Data.Dependent.Map (DMap)
2627import Data.Dependent.Sum (DSum (..))
@@ -36,9 +37,12 @@ import Data.GADT.Compare (GEq (..), GCompare (..))
3637import Data.GADT.Show (GRead, GShow, gshow)
3738import qualified Data.Map as Map
3839import Data.Maybe
39−import Data.Semigroup (Semigroup (..), (<>))
40+import Data.Monoid.DecidablyEmpty
4041import Data.Semigroupoid as Cat
41−import Data.Some (Some(Some))
42+#if !MIN_VERSION_base(4,11,0)
43+import Data.Semigroup (Semigroup (..), (<>))
44+#endif
45+import Data.Some (Some, mkSome)
4246import Data.Proxy (Proxy (..))
4347import Data.These (These (..))
4448
@@ -47,10 +51,10 @@ import Data.Patch.Class
4751 , PatchHet2 (..), PatchSource1, PatchTarget1
4852 , applyAlwaysHet2
4953 )
50−import Data.Patch.MapWithMove (PatchMapWithMove (..))
51−import qualified Data.Patch.MapWithMove as MapWithMove
54+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove (..))
55+import qualified Data.Patch.MapWithPatchingMove as MapWithPatchingMove
5256
53−-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
57+-- | Like 'PatchMapWithPatchingMove', but for 'DMap'. Each key carries a 'NodeInfo'
5458-- which describes how it will be changed by the patch and connects move sources
5559-- and destinations.
5660--
@@ -80,6 +84,14 @@ newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
8084-- , Has' Ord k (PatchTarget1 p)
8185-- ) => Ord (PatchDMapWithMove k p)
8286
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+
8395-- | Structure which represents what changes apply to a particular key.
8496-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
8597-- 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
360372-- @
361373-- let aMay = DMap.lookup a dmap
362374-- bMay = DMap.lookup b dmap
363−-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
364−-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
375+-- in maybe id (DMap.insert a) (bMay <> aMay)
376+-- . maybe id (DMap.insert b) (aMay <> bMay)
365377-- . DMap.delete a . DMap.delete b $ dmap
366378-- @
367379swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
@@ -416,8 +428,8 @@ unsafePatchDMapWithMove = PatchDMapWithMove
416428-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while
417429-- checking invariants. If the invariants are satisfied, @Right p@ is returned
418430-- otherwise @Left errors@.
419−patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
420−patchDMapWithMove dm =
431+patchDMapWithPatchingMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
432+patchDMapWithPatchingMove dm =
421433 case validationErrorsForPatchDMapWithMove dm of
422434 [] -> Right $ unsafePatchDMapWithMove dm
423435 errs -> Left errs
@@ -507,7 +519,7 @@ nodeInfoMapFromM f g ni = NodeInfo
507519 <$> f (_nodeInfo_from ni)
508520 <*> g (_nodeInfo_to ni)
509521
510−-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
522+-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithPatchingMove' by weakening the keys
511523-- from @k a@ to @'Some' k@ and applying a given weakening function
512524-- @PatchTarget1 p a -> v'@ to values.
513525weakenPatchDMapWithMoveWith
@@ -515,65 +527,65 @@ weakenPatchDMapWithMoveWith
515527 . (forall a. PatchTarget1 p a -> PatchTarget p')
516528 -> (forall from to. p from to -> p')
517529 -> PatchDMapWithMove k p
518− -> PatchMapWithMove (Some k) p'
530+ -> PatchMapWithPatchingMove (Some k) p'
519531weakenPatchDMapWithMoveWith f g (PatchDMapWithMove m) =
520− PatchMapWithMove $ weakenDMapWith h m
521− where h :: forall a. NodeInfo k p a -> MapWithMove.NodeInfo (Some k) p'
522− h ni = MapWithMove.NodeInfo
523− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
524− From_Insert v -> MapWithMove.From_Insert $ f v
525− From_Delete -> MapWithMove.From_Delete
526− From_Move (k :=> Flip p) -> MapWithMove.From_Move (Some k) $ g p
527− , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
528− To_NonMove -> MapWithMove.To_NonMove
529− To_Move (k :=> p) -> MapWithMove.To_Move (Some k) $ g p
532+ PatchMapWithPatchingMove $ weakenDMapWith h m
533+ where h :: forall a. NodeInfo k p a -> MapWithPatchingMove.NodeInfo (Some k) p'
534+ h ni = MapWithPatchingMove.NodeInfo
535+ { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
536+ From_Insert v -> MapWithPatchingMove.From_Insert $ f v
537+ From_Delete -> MapWithPatchingMove.From_Delete
538+ From_Move (k :=> Flip p) -> MapWithPatchingMove.From_Move (mkSome k) $ g p
539+ , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
540+ To_NonMove -> Nothing
541+ To_Move (k :=> _) -> Just (mkSome k)
530542 }
531543
532−-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
544+-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithPatchingMove' k
533545-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
534546-- dependency in the typing and all points are already @a@, hence the function
535547-- to map each value to @v'@ is not higher rank.
536−patchDMapWithMoveToPatchMapWithMoveWith
548+patchDMapWithMoveToPatchMapWithPatchingMoveWith
537549 :: forall k p p' a
538550 . (PatchTarget1 p a -> PatchTarget p')
539551 -> (p a a -> p')
540552 -> PatchDMapWithMove (Const2 k a) p
541− -> PatchMapWithMove k p'
542−patchDMapWithMoveToPatchMapWithMoveWith f g (PatchDMapWithMove m) =
543− PatchMapWithMove $ dmapToMapWith h m
544− where h :: NodeInfo (Const2 k a) p a -> MapWithMove.NodeInfo k p'
545− h ni = MapWithMove.NodeInfo
546− { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
547− From_Insert v -> MapWithMove.From_Insert $ f v
548− From_Delete -> MapWithMove.From_Delete
549− From_Move (Const2 k :=> Flip p) -> MapWithMove.From_Move k $ g p
550− , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
551− To_NonMove -> MapWithMove.To_NonMove
552− To_Move (Const2 k :=> p) -> MapWithMove.To_Move k $ g p
553+ -> PatchMapWithPatchingMove k p'
554+patchDMapWithMoveToPatchMapWithPatchingMoveWith f g (PatchDMapWithMove m) =
555+ PatchMapWithPatchingMove $ dmapToMapWith h m
556+ where h :: NodeInfo (Const2 k a) p a -> MapWithPatchingMove.NodeInfo k p'
557+ h ni = MapWithPatchingMove.NodeInfo
558+ { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
559+ From_Insert v -> MapWithPatchingMove.From_Insert $ f v
560+ From_Delete -> MapWithPatchingMove.From_Delete
561+ From_Move (Const2 k :=> Flip p) -> MapWithPatchingMove.From_Move k $ g p
562+ , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
563+ To_NonMove -> Nothing
564+ To_Move (Const2 k :=> _) -> Just k
553565 }
554566
555−-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
567+-- | "Strengthen" a @'PatchMapWithPatchingMove' k v@ into a @'PatchDMapWithMove
556568-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
557569-- dependently typed one but which always has a constant key type represented by
558570-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
559−-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
571+-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithPatchingMoveWith'
560572const2PatchDMapWithMoveWith
561573 :: forall k v v' a
562574 . (v -> v' a)
563− -> PatchMapWithMove k (Proxy v)
575+ -> PatchMapWithPatchingMove k (Proxy v)
564576 -> PatchDMapWithMove (Const2 k a) (Proxy3 v')
565−const2PatchDMapWithMoveWith f (PatchMapWithMove p) =
577+const2PatchDMapWithMoveWith f (PatchMapWithPatchingMove p) =
566578 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))
568580 -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
569581 g (k, ni) = Const2 k :=> NodeInfo
570− { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
571− MapWithMove.From_Insert v -> From_Insert $ f v
572− MapWithMove.From_Delete -> From_Delete
573− MapWithMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
574− , _nodeInfo_to = case MapWithMove._nodeInfo_to ni of
575− MapWithMove.To_NonMove -> To_NonMove
576− MapWithMove.To_Move toKey Proxy -> To_Move $ Const2 toKey :=> Proxy3
582+ { _nodeInfo_from = case MapWithPatchingMove._nodeInfo_from ni of
583+ MapWithPatchingMove.From_Insert v -> From_Insert $ f v
584+ MapWithPatchingMove.From_Delete -> From_Delete
585+ MapWithPatchingMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
586+ , _nodeInfo_to = case MapWithPatchingMove._nodeInfo_to ni of
587+ Nothing -> To_NonMove
588+ Just toKey -> To_Move $ Const2 toKey :=> Proxy3
577589 }
578590
579591-- | Apply the insertions, deletions, and moves to a given 'DMap'.
src/Data/Patch/IntMap.hs+10−7
@@ -1,7 +1,9 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveTraversable #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE GeneralizedNewtypeDeriving #-}
45{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE StandaloneDeriving #-}
57{-# LANGUAGE TemplateHaskell #-}
68{-# LANGUAGE TypeFamilies #-}
79
@@ -13,7 +15,10 @@ import Control.Lens
1315import Data.IntMap.Strict (IntMap)
1416import qualified Data.IntMap.Strict as IntMap
1517import 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
1722import Data.Patch.Class
1823
1924-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
@@ -21,16 +26,14 @@ import Data.Patch.Class
2126-- and @Nothing@ means delete.
2227newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
2328 deriving ( Show, Read, Eq, Ord
24− , Functor, Foldable, Traversable, Monoid
29+ , Functor, Foldable, Traversable
30+ , Monoid, DecidablyEmpty
2531 )
2632
2733-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
2834-- If the same key is modified by both patches, the one on the left will take
2935-- precedence.
30−instance Semigroup (PatchIntMap v) where
31− PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
32− -- PatchMap is idempotent, so stimes n is id for every n
33− stimes = stimesIdempotentMonoid
36+deriving instance Semigroup (PatchIntMap v)
3437
3538makeWrapped ''PatchIntMap
3639
@@ -58,7 +61,7 @@ mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k m
5861-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
5962-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
6063traverseIntMapPatchWithKey :: 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
6265
6366-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
6467patchIntMapNewElements :: PatchIntMap a -> [a]
src/Data/Patch/Map.hs+9−8
@@ -1,5 +1,7 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveTraversable #-}
23{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
35{-# LANGUAGE LambdaCase #-}
46{-# LANGUAGE MultiParamTypeClasses #-}
57{-# LANGUAGE StandaloneDeriving #-}
@@ -16,7 +18,8 @@ import Control.Lens
1618import Data.Map (Map)
1719import qualified Data.Map as Map
1820import Data.Maybe
19−import Data.Semigroup
21+import Data.Monoid.DecidablyEmpty
22+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
2023
2124-- | A set of changes to a 'Map'. Any element may be inserted/updated or
2225-- deleted. Insertions are represented as values wrapped in 'Just', while
@@ -24,11 +27,14 @@ import Data.Semigroup
2427newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
2528 deriving ( Show, Read, Eq, Ord
2629 , Foldable, Traversable
30+ , DecidablyEmpty
2731 )
2832
2933-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
3034-- Deletions are unaffected.
3135deriving instance Functor (PatchMap k)
36+-- | The empty 'PatchMap' contains no insertions or deletions
37+deriving instance Ord k => Monoid (PatchMap k v)
3238
3339-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
3440-- 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
3844 -- PatchMap is idempotent, so stimes n is id for every n
3945 stimes = stimesIdempotentMonoid
4046
41−makeWrapped ''PatchMap
42−
4347-- | Apply the insertions or deletions to a given 'Map'.
4448instance Ord k => PatchHet (PatchMap k v) where
4549 type PatchSource (PatchMap k v) = Map k v
@@ -59,11 +63,6 @@ instance TraversableWithIndex k (PatchMap k) where
5963 itraverse = itraversed . Indexed
6064 itraversed = _Wrapped .> itraversed <. traversed
6165
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−
6766-- | Returns all the new elements that will be added to the 'Map'
6867patchMapNewElements :: PatchMap k v -> [v]
6968patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
@@ -71,3 +70,5 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
7170-- | Returns all the new elements that will be added to the 'Map'
7271patchMapNewElementsMap :: PatchMap k v -> Map k v
7372patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
73+
74+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+237−287
@@ -2,364 +2,314 @@
22{-# LANGUAGE DeriveTraversable #-}
33{-# LANGUAGE FlexibleContexts #-}
44{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
56{-# LANGUAGE LambdaCase #-}
67{-# LANGUAGE MultiParamTypeClasses #-}
78{-# LANGUAGE PatternGuards #-}
9+{-# LANGUAGE PatternSynonyms #-}
810{-# LANGUAGE ScopedTypeVariables #-}
911{-# LANGUAGE StandaloneDeriving #-}
1012{-# LANGUAGE TemplateHaskell #-}
1113{-# LANGUAGE TypeApplications #-}
1214{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE UndecidableInstances #-}
15+{-# LANGUAGE ViewPatterns #-}
1416
1517-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
1618-- 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
1837
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)
1965import Data.Patch.Class
66+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67+import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
2068
21−import Control.Arrow
22−import Control.Lens hiding (from, to)
23−import Control.Monad.Trans.State
24−import Data.Foldable
25−import Data.Function
69+import Control.Lens
2670import Data.List
2771import Data.Map (Map)
2872import qualified Data.Map as Map
29−import Data.Maybe
30−--import Data.Proxy
31−#if !MIN_VERSION_base(4,9,0)
32−import Data.Semigroup (Semigroup (..), (<>))
73+import Data.Proxy
74+#if !MIN_VERSION_base(4,11,0)
75+import Data.Semigroup (Semigroup (..))
3376#endif
34−import Data.Monoid.DecidablyEmpty
35−import qualified Data.Set as Set
36−import Data.These (These (..))
37−import Data.Tuple
77+import Data.Traversable (foldMapDefault)
3878
3979-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
4080-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
4181-- and vice versa. There should never be any unpaired From/To keys.
42−newtype PatchMapWithMove k p = PatchMapWithMove
43− { -- | Extract the internal representation of the 'PatchMapWithMove'
44− unPatchMapWithMove :: Map k (NodeInfo k p)
82+newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
83+ { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
84+ unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
4585 }
46−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithMove k p)
47−deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithMove k p)
48−deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithMove k p)
49−deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithMove k p)
86+ deriving ( Show, Read, Eq, Ord
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
96+ )
5097
51−-- -- | Skips the patch so it's 'Functor' and friends
52−-- newtype PatchMapWithMove' k v = PatchMapWithMove' { unPatchMapWithMove' :: PatchMapWithMove k (Proxy v) }
53−--
54−-- deriving instance Functor (PatchMapWithMove' k)
55−-- deriving instance Foldable (PatchMapWithMove' k)
56−-- deriving instance Traversable (PatchMapWithMove' k)
98+pattern Coerce :: Coercible a b => a -> b
99+pattern Coerce x <- (coerce -> x)
100+ where Coerce x = coerce x
57101
58−-- | Holds the information about each key: where its new value should come from,
59−-- and where its old value should go to
60−data NodeInfo k p = NodeInfo
61− { _nodeInfo_from :: !(From k p)
62− -- ^ Where do we get the new value for this key?
63− , _nodeInfo_to :: !(To k p)
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)
102+{-# COMPLETE PatchMapWithMove #-}
103+pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
104+-- | Extract the representation of the 'PatchMapWithMove' as a map of
105+-- 'NodeInfo'.
106+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
107+pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
71108
72−-- | Describe how a key's new value should be produced
73−data From k p
74− = From_Insert (PatchTarget p) -- ^ Insert the given value here
75− | From_Delete -- ^ Delete the existing value, if any, from here
76− | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
77−
78−deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
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− )
109+_PatchMapWithMove
110+ :: Iso
111+ (PatchMapWithMove k0 v0)
112+ (PatchMapWithMove k1 v1)
113+ (Map k0 (NodeInfo k0 v0))
114+ (Map k1 (NodeInfo k1 v1))
115+_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
92116
93−makeWrapped ''PatchMapWithMove
94−--makeWrapped ''PatchMapWithMove'
117+instance Functor (PatchMapWithMove k) where
118+ fmap f = runIdentity . traverse (Identity . f)
119+
120+instance Foldable (PatchMapWithMove k) where
121+ foldMap = foldMapDefault
95122
96−-- instance FunctorWithIndex k (PatchMapWithMove k)
97−-- instance FoldableWithIndex k (PatchMapWithMove k)
98−-- instance TraversableWithIndex k (PatchMapWithMove k) where
99−-- itraverse = itraversed . Indexed
100−-- itraversed = _Wrapped .> itraversed <. traversed
123+instance Traversable (PatchMapWithMove k) where
124+ traverse =
125+ _PatchMapWithMove .
126+ traverse .
127+ traverse
128+
129+instance FunctorWithIndex k (PatchMapWithMove k)
130+instance FoldableWithIndex k (PatchMapWithMove k)
131+instance TraversableWithIndex k (PatchMapWithMove k) where
132+ itraverse = itraversed . Indexed
133+ itraversed =
134+ _PatchMapWithMove .>
135+ itraversed <.
136+ traverse
101137
102138-- | Create a 'PatchMapWithMove', validating it
103−patchMapWithMove
104− :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithMove k p)
105−patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
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
139+patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
140+patchMapWithMove = fmap coerce . PM.patchMapWithPatchingMove . coerce
114141
115142-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
116−patchMapWithMoveInsertAll
117− :: Map k (PatchTarget p) -> PatchMapWithMove k p
118−patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
119− { _nodeInfo_from = From_Insert v
120− , _nodeInfo_to = To_NonMove
121− }
143+patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
144+patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
122145
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'.
124−insertMapKey
125− :: k -> PatchTarget p -> PatchMapWithMove k p
126−insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) To_NonMove
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'.
147+insertMapKey :: k -> v -> PatchMapWithMove k v
148+insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
127149
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:
129151--
130152-- @
131153-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
132154-- @
133−moveMapKey
134− :: (DecidablyEmpty p, Patch p) => Ord k => k -> k -> PatchMapWithMove k p
135−moveMapKey src dst
136− | src == dst = mempty
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:
155+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
156+moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
157+
158+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
144159--
145160-- @
146161-- let aMay = Map.lookup a map
147162-- bMay = Map.lookup b map
148−-- in maybe id (Map.insert a) (bMay `mplus` aMay)
149−-- . maybe id (Map.insert b) (aMay `mplus` bMay)
163+-- in maybe id (Map.insert a) (bMay <> aMay)
164+-- . maybe id (Map.insert b) (aMay <> bMay)
150165-- . Map.delete a . Map.delete b $ map
151166-- @
152−swapMapKey
153− :: (DecidablyEmpty p, Patch p)
154− => Ord k => k -> k -> PatchMapWithMove k p
155−swapMapKey src dst
156− | src == dst = mempty
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
164−-- the mapping, equivalent to 'Map.delete'.
165−deleteMapKey
166− :: k -> PatchMapWithMove k v
167−deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete To_NonMove
167+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
168+swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
169+
170+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
171+deleteMapKey :: k -> PatchMapWithMove k v
172+deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
168173
169174-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
170175--
171176-- __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
174−unsafePatchMapWithMove = PatchMapWithMove
177+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
178+unsafePatchMapWithMove = PatchMapWithMove' . PM.unsafePatchMapWithPatchingMove . coerce
175179
176180-- | Apply the insertions, deletions, and moves to a given 'Map'
177−instance (Ord k, Patch p) => PatchHet (PatchMapWithMove k p) where
178− type PatchSource (PatchMapWithMove k p) = Map k (PatchSource p)
179− type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
180−
181−instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) 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)
187− where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
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'
196−patchMapWithMoveNewElements
197− :: PatchMapWithMove k p -> [PatchTarget p]
198−patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
181+instance Ord k => PatchHet (PatchMapWithMove k v) where
182+ type PatchSource (PatchMapWithMove k v) = Map k v
183+ type PatchTarget (PatchMapWithMove k v) = Map k v
184+
185+instance Ord k => Patch (PatchMapWithMove k v) where
186+ apply (PatchMapWithMove' p) = apply p
187+
188+-- | Returns all the new elements that will be added to the 'Map'.
189+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
190+patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
199191
200192-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
201−patchMapWithMoveNewElementsMap
202− :: PatchMapWithMove k p -> Map k (PatchTarget p)
203−patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
204− where f ni = case _nodeInfo_from ni of
205− From_Insert v -> Just v
206− From_Move _ _ -> Nothing
207− From_Delete -> Nothing
193+patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
194+patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
208195
209196-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
210197-- its values using the given ordering function. The set keys of the 'Map' is
211198-- not changed.
212−patchThatSortsMapWith
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
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)
199+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
200+patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
225201
226202-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
227203-- will produce a 'Map' with the same values as the second 'Map' but with the
228204-- values sorted with the given ordering function.
229−patchThatChangesAndSortsMapWith
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
205+patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
233206patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
234207 where newList = Map.toList newByIndexUnsorted
235208 newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
236209
237210-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
238211-- will produce the second 'Map'.
239−patchThatChangesMap
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
243− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
244− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
245− let f k v = do
246− remainingValues <- get
247− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
248− then Map.delete v remainingValues
249− else Map.insert v remainingKeys remainingValues
250− case Map.lookup v remainingValues of
251− Nothing -> return $ NodeInfo (From_Insert v) $ To_Move undefined undefined -- There's no existing value we can take
252− Just fromKs ->
253− if k `Set.member` fromKs
254− then do
255− putRemainingKeys $ Set.delete k fromKs
256− return $ NodeInfo (From_Move k mempty) $ To_Move undefined undefined -- There's an existing value, and it's here, so no patch necessary
257− else do
258− (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
259− putRemainingKeys remainingKeys
260− return $ NodeInfo (From_Move fromK mempty) $ To_Move undefined undefined
261− Map.traverseWithKey f newByIndex
262− unusedOldKeys = fold unusedValuesByValue
263− pointlessMove k = \case
264− From_Move k' _ | k == k' -> True
265− _ -> False
266− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
267− then To_Move undefined undefined
268− else To_NonMove
269− patch = unsafePatchMapWithMove $ Map.filterWithKey
270− (\k -> not . pointlessMove k . _nodeInfo_from)
271− $ Map.mergeWithKey
272− (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a)
273− (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k)
274− (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k)
275− insertsAndMoves
276− oldByIndex
212+patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
213+patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
214+ PM.patchThatChangesMap oldByIndex newByIndex
215+
216+--
217+-- NodeInfo
218+--
219+
220+-- | Holds the information about each key: where its new value should come from,
221+-- and where its old value should go to
222+newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
223+
224+deriving instance (Show k, Show p) => Show (NodeInfo k p)
225+deriving instance (Read k, Read p) => Read (NodeInfo k p)
226+deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
227+deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
228+
229+{-# COMPLETE NodeInfo #-}
230+pattern NodeInfo :: To k -> From k v -> NodeInfo k v
231+_nodeInfo_to :: NodeInfo k v -> To k
232+_nodeInfo_from :: NodeInfo k v -> From k v
233+pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
234+ PM.NodeInfo
235+ { PM._nodeInfo_to = _nodeInfo_to
236+ , PM._nodeInfo_from = Coerce _nodeInfo_from
237+ }
238+
239+_NodeInfo
240+ :: Iso
241+ (NodeInfo k0 v0)
242+ (NodeInfo k1 v1)
243+ (PM.NodeInfo k0 (Proxy v0))
244+ (PM.NodeInfo k1 (Proxy v1))
245+_NodeInfo = iso unNodeInfo' NodeInfo'
246+
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
277264
278265-- | Change the 'From' value of a 'NodeInfo'
279−nodeInfoMapFrom
280− :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
281−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
266+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
267+nodeInfoMapFrom f = coerce . PM.nodeInfoMapFrom (unFrom' . f . From') . coerce
282268
283269-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
284270-- 'Applicative', 'Monad', etc.) action to get the new value
285271nodeInfoMapMFrom
286− :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
287−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
272+ :: Functor f
273+ => (From k v -> f (From k v))
274+ -> NodeInfo k v -> f (NodeInfo k v)
275+nodeInfoMapMFrom f = fmap coerce
276+ . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
277+ . coerce
288278
289279-- | Set the 'To' field of a 'NodeInfo'
290−nodeInfoSetTo
291− :: To k p -> NodeInfo k p -> NodeInfo k p
292−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
293−
294−-- | Helper data structure used for composing patches using the monoid instance.
295−data Fixup k p
296− = Fixup_Delete
297− | Fixup_Update (These (From k p) (To k p))
298−
299−-- | Compose patches having the same effect as applying the patches in turn:
300−-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
301−instance ( Ord k
302− , DecidablyEmpty p
303− , Patch p
304− ) => Semigroup (PatchMapWithMove k p) where
305− PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
306− where
307− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
308− h :: (k, (To k p, From k p)) -> [(k, Fixup k p)]
309− h (_, (editAfter, editBefore)) = case (editAfter, editBefore) of
310− (To_Move toAfter p1, From_Move fromBefore p0)
311− | fromBefore == toAfter && isNull p0
312− -> [(toAfter, Fixup_Delete)]
313− | otherwise
314− -> [ (toAfter, Fixup_Update (This $ From_Move fromBefore $ p1 <> p0))
315− , (fromBefore, Fixup_Update (That $ To_Move toAfter $ p1 <> p0))
316− ]
317− (To_NonMove, From_Move fromBefore _) ->
318− -- The item is destroyed in the second patch, so indicate that it is
319− -- destroyed in the source map
320− [(fromBefore, Fixup_Update (That editAfter))]
321− (To_Move toAfter p, From_Insert val) ->
322− [(toAfter, Fixup_Update $ This $ From_Insert $ applyAlways p val)]
323− (To_Move toAfter _, From_Delete) ->
324− [(toAfter, Fixup_Update $ This From_Delete)]
325− (To_NonMove, _) -> []
326− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
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 = (<>)
280+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
281+nodeInfoSetTo = coerce . PM.nodeInfoSetTo
282+
283+--
284+-- From
285+--
286+
287+-- | Describe how a key's new value should be produced
288+newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
289+
290+{-# COMPLETE From_Insert, From_Delete, From_Move #-}
291+
292+-- | Insert the given value here
293+pattern From_Insert :: v -> From k v
294+pattern From_Insert v = From' (PM.From_Insert v)
295+
296+-- | Delete the existing value, if any, from here
297+pattern From_Delete :: From k v
298+pattern From_Delete = From' PM.From_Delete
299+
300+-- | Move the value here from the given key
301+pattern From_Move :: k -> From k v
302+pattern From_Move k = From' (PM.From_Move k Proxy)
303+
304+bitraverseFrom
305+ :: Applicative f
306+ => (k0 -> f k1)
307+ -> (v0 -> f v1)
308+ -> From k0 v0 -> f (From k1 v1)
309+bitraverseFrom fk fv = fmap coerce
310+ . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
311+ . coerce
312+
313+makeWrapped ''PatchMapWithMove
314+makeWrapped ''NodeInfo
315+makeWrapped ''From
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 @@
11module Main where
22
33import Control.Monad
4−import Language.Haskell.HLint3 (hlint)
4+import Language.Haskell.HLint (hlint)
55import System.Directory
66import System.Exit (exitFailure, exitSuccess)
77import System.FilePath
@@ -23,6 +23,7 @@ main = do
2323 , "--ignore=Reduce duplication"
2424 , "--cpp-define=USE_TEMPLATE_HASKELL"
2525 , "--ignore=Use tuple-section"
26+ , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
2627 ]
2728 recurseInto = and <$> sequence
2829 [ fileType ==? Directory