on develop help sign in
Merge pull request #1 from reflex-frp/patch-map-inner-patch

Add PatchMapWithPatchingMove

commit946fae73563bd1c14c6bd56db3424a97cf1285b6 authorJohn Ericson <John.Ericson@Obsidian.Systems> dateWed 5 Feb 2020, 14:10 (7 years ago) parente92162615c4787
11 files changed+571−30
ChangeLog.md+6 patch.cabal+2 src/Data/Monoid/DecidablyEmpty.hs+115 src/Data/Patch.hs+22−14 src/Data/Patch/Class.hs+9 src/Data/Patch/DMap.hs+9 src/Data/Patch/DMapWithMove.hs+8−1 src/Data/Patch/IntMap.hs+9−6 src/Data/Patch/Map.hs+9−8 src/Data/Patch/MapWithMove.hs+4−1 src/Data/Patch/MapWithPatchingMove.hs+378
ChangeLog.md+6
@@ -1,5 +1,11 @@
11# Revision history for patch
22
3+## Unreleased
4+
5+* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
6+
7+* Create `DecidablyEmpty` subclass of `Monoid`.
8+
39## 0.0.2.0
410
511* Consistently provide:
patch.cabal+2
@@ -41,6 +41,7 @@ library
4141 , witherable >= 0.3 && < 0.3.2
4242
4343 exposed-modules: Data.Functor.Misc
44+ , Data.Monoid.DecidablyEmpty
4445 , Data.Patch
4546 , Data.Patch.Class
4647 , Data.Patch.DMap
@@ -48,6 +49,7 @@ library
4849 , Data.Patch.IntMap
4950 , Data.Patch.Map
5051 , Data.Patch.MapWithMove
52+ , Data.Patch.MapWithPatchingMove
5153
5254 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
5355
src/Data/Monoid/DecidablyEmpty.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+{-# LANGUAGE TypeOperators #-}
6+
7+-- TODO upstream somwhere else?
8+module Data.Monoid.DecidablyEmpty where
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.
38+class Monoid a => DecidablyEmpty a where
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+22−14
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23{-# LANGUAGE StandaloneDeriving #-}
34{-# LANGUAGE TypeFamilies #-}
@@ -12,24 +13,31 @@ module Data.Patch
1213 , module X
1314 ) where
1415
16+import Control.Applicative
17+import Data.Functor.Const (Const (..))
18+import Data.Functor.Identity
19+import Data.Map.Monoidal (MonoidalMap)
20+import Data.Proxy
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
23+#endif
24+import GHC.Generics
25+
1526import Data.Patch.Class as X
1627import Data.Patch.DMap as X hiding (getDeletions)
17−import Data.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove,
18− patchDMapWithMoveToPatchMapWithMoveWith,
19− traversePatchDMapWithMoveWithKey, unPatchDMapWithMove,
20− unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith)
28+import Data.Patch.DMapWithMove as X
29+ ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
30+ , patchDMapWithMoveToPatchMapWithMoveWith
31+ , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
32+ , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
33+ )
2134import Data.Patch.IntMap as X hiding (getDeletions)
2235import Data.Patch.Map as X
23−import Data.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
24− patchMapWithMoveNewElementsMap, unPatchMapWithMove,
25− unsafePatchMapWithMove)
26−import Data.Map.Monoidal (MonoidalMap)
27−import Data.Semigroup (Semigroup (..), (<>))
28−import GHC.Generics
29−import Data.Functor.Identity
30−import Data.Functor.Const
31−import Data.Proxy
32−import Control.Applicative
36+import Data.Patch.MapWithMove as X
37+ ( PatchMapWithMove, patchMapWithMoveNewElements
38+ , patchMapWithMoveNewElementsMap, unPatchMapWithMove
39+ , unsafePatchMapWithMove
40+ )
3341
3442-- | A 'Group' is a 'Monoid' where every element has an inverse.
3543class (Semigroup q, Monoid q) => Group q where
src/Data/Patch/Class.hs+9
@@ -1,10 +1,14 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE TypeFamilies #-}
23-- | The interface for types which represent changes made to other types
34module Data.Patch.Class where
45
56import Data.Functor.Identity
67import Data.Maybe
8+#if !MIN_VERSION_base(4,11,0)
79import Data.Semigroup (Semigroup(..))
10+#endif
11+import Data.Proxy
812
913-- | A 'Patch' type represents a kind of change made to a datastructure.
1014--
@@ -25,6 +29,11 @@ instance Patch (Identity a) where
2529 type PatchTarget (Identity a) = a
2630 apply (Identity a) _ = Just a
2731
32+-- | 'Proxy' can be used as a 'Patch' that does nothing.
33+instance Patch (Proxy a) where
34+ type PatchTarget (Proxy a) = a
35+ apply ~Proxy _ = Nothing
36+
2837-- | Like '(.)', but composes functions that return patches rather than
2938-- functions that return new values. The Semigroup instance for patches must
3039-- apply patches right-to-left, like '(.)'.
src/Data/Patch/DMap.hs+9
@@ -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
@@ -18,7 +20,10 @@ import Data.Functor.Constant
1820import Data.Functor.Misc
1921import qualified Data.IntMap as IntMap
2022import qualified Data.Map as Map
23+import Data.Monoid.DecidablyEmpty
24+#if !MIN_VERSION_base(4,11,0)
2125import Data.Semigroup (Semigroup (..))
26+#endif
2227import Data.Some (Some)
2328
2429-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
@@ -30,6 +35,10 @@ deriving instance GCompare k => Semigroup (PatchDMap k v)
3035
3136deriving instance GCompare k => Monoid (PatchDMap k v)
3237
38+-- It won't let me derive for some reason
39+instance GCompare k => DecidablyEmpty (PatchDMap k v) where
40+ isEmpty (PatchDMap m) = DMap.null m
41+
3342-- | Apply the insertions or deletions to a given 'DMap'.
3443instance GCompare k => Patch (PatchDMap k v) where
3544 type PatchTarget (PatchDMap k v) = DMap k v
src/Data/Patch/DMapWithMove.hs+8−1
@@ -29,7 +29,10 @@ import Data.GADT.Compare (GEq (..))
2929import Data.GADT.Show (GShow, gshow)
3030import qualified Data.Map as Map
3131import Data.Maybe
32−import Data.Semigroup (Semigroup (..), (<>))
32+import Data.Monoid.DecidablyEmpty
33+#if !MIN_VERSION_base(4,11,0)
34+import Data.Semigroup (Semigroup (..))
35+#endif
3336import Data.Some (Some(Some))
3437import Data.These
3538
@@ -42,6 +45,10 @@ import Data.These
4245-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
4346newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
4447
48+-- It won't let me derive for some reason
49+instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
50+ isEmpty (PatchDMapWithMove m) = DMap.null m
51+
4552-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
4653-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
4754data NodeInfo k v a = NodeInfo
src/Data/Patch/IntMap.hs+9−6
@@ -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
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 => Patch (PatchMap k v) where
4549 type PatchTarget (PatchMap k v) = Map k v
@@ -57,11 +61,6 @@ instance TraversableWithIndex k (PatchMap k) where
5761 itraverse = itraversed . Indexed
5862 itraversed = _Wrapped .> itraversed <. traversed
5963
60−-- | The empty 'PatchMap' contains no insertions or deletions
61−instance Ord k => Monoid (PatchMap k v) where
62− mempty = PatchMap mempty
63− mappend = (<>)
64−
6564-- | Returns all the new elements that will be added to the 'Map'
6665patchMapNewElements :: PatchMap k v -> [v]
6766patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
@@ -69,3 +68,5 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
6968-- | Returns all the new elements that will be added to the 'Map'
7069patchMapNewElementsMap :: PatchMap k v -> Map k v
7170patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71+
72+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hs+4−1
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveTraversable #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE FlexibleInstances #-}
@@ -24,7 +25,9 @@ import Data.List
2425import Data.Map (Map)
2526import qualified Data.Map as Map
2627import Data.Maybe
27−import Data.Semigroup (Semigroup (..), (<>))
28+#if !MIN_VERSION_base(4,11,0)
29+import Data.Semigroup (Semigroup (..))
30+#endif
2831import qualified Data.Set as Set
2932import Data.These (These(..))
3033import Data.Tuple
src/Data/Patch/MapWithPatchingMove.hsnew file+378
@@ -0,0 +1,378 @@
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 where
17+
18+import Data.Patch.Class
19+
20+import Control.Arrow
21+import Control.Lens.TH (makeWrapped)
22+import Control.Monad.Trans.State
23+import Data.Foldable
24+import Data.Function
25+import Data.List
26+import Data.Map (Map)
27+import qualified Data.Map as Map
28+import Data.Maybe
29+#if !MIN_VERSION_base(4,11,0)
30+import Data.Semigroup (Semigroup (..))
31+#endif
32+import Data.Monoid.DecidablyEmpty
33+import qualified Data.Set as Set
34+import Data.These (These (..))
35+import Data.Tuple
36+
37+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
38+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
39+-- and vice versa. There should never be any unpaired From/To keys.
40+newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
41+ { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
42+ unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
43+ }
44+
45+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
46+deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
47+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
48+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
49+
50+deriving instance ( Ord k
51+#if !MIN_VERSION_base(4,11,0)
52+ , Semigroup p
53+#endif
54+ , DecidablyEmpty p
55+ , Patch p
56+ ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
57+
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)
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+
72+bitraverseNodeInfo
73+ :: Applicative f
74+ => (k0 -> f k1)
75+ -> (p0 -> f p1)
76+ -> (PatchTarget p0 -> f (PatchTarget p1))
77+ -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
78+bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
79+ <$> bitraverseFrom fk fp fpt from
80+ <*> traverse fk to
81+
82+-- | Describe how a key's new value should be produced
83+data From k p
84+ = From_Insert (PatchTarget p) -- ^ Insert the given value here
85+ | From_Delete -- ^ Delete the existing value, if any, from here
86+ | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
87+
88+bitraverseFrom
89+ :: Applicative f
90+ => (k0 -> f k1)
91+ -> (p0 -> f p1)
92+ -> (PatchTarget p0 -> f (PatchTarget p1))
93+ -> From k0 p0 -> f (From k1 p1)
94+bitraverseFrom fk fp fpt = \case
95+ From_Insert pt -> From_Insert <$> fpt pt
96+ From_Delete -> pure From_Delete
97+ From_Move k p -> From_Move <$> fk k <*> fp p
98+
99+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
100+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
101+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
102+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
103+
104+-- | Describe where a key's old value will go. If this is 'Just', that means
105+-- the key's old value will be moved to the given other key; if it is 'Nothing',
106+-- that means it will be deleted.
107+type To = Maybe
108+
109+-- | Create a 'PatchMapWithPatchingMove', validating it
110+patchMapWithPatchingMove
111+ :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
112+patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
113+ where valid = forwardLinks == backwardLinks
114+ forwardLinks = Map.mapMaybe _nodeInfo_to m
115+ backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
116+ case _nodeInfo_from p of
117+ From_Move from _ -> Just (from, to)
118+ _ -> Nothing
119+
120+-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
121+patchMapWithPatchingMoveInsertAll
122+ :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
123+patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
124+ { _nodeInfo_from = From_Insert v
125+ , _nodeInfo_to = Nothing
126+ }
127+
128+-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
129+insertMapKey
130+ :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
131+insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
132+
133+-- |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:
134+--
135+-- @
136+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
137+-- @
138+moveMapKey
139+ :: ( DecidablyEmpty p
140+#if !MIN_VERSION_base(4,11,0)
141+ , Semigroup p
142+#endif
143+ , Patch p
144+ )
145+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
146+moveMapKey src dst
147+ | src == dst = mempty
148+ | otherwise =
149+ PatchMapWithPatchingMove $ Map.fromList
150+ [ (dst, NodeInfo (From_Move src mempty) Nothing)
151+ , (src, NodeInfo From_Delete (Just dst))
152+ ]
153+
154+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
155+--
156+-- @
157+-- let aMay = Map.lookup a map
158+-- bMay = Map.lookup b map
159+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
160+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
161+-- . Map.delete a . Map.delete b $ map
162+-- @
163+swapMapKey
164+ :: ( DecidablyEmpty p
165+#if !MIN_VERSION_base(4,11,0)
166+ , Semigroup p
167+#endif
168+ , Patch p
169+ )
170+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
171+swapMapKey src dst
172+ | src == dst = mempty
173+ | otherwise =
174+ PatchMapWithPatchingMove $ Map.fromList
175+ [ (dst, NodeInfo (From_Move src mempty) (Just src))
176+ , (src, NodeInfo (From_Move dst mempty) (Just dst))
177+ ]
178+
179+-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
180+-- the mapping, equivalent to 'Map.delete'.
181+deleteMapKey
182+ :: k -> PatchMapWithPatchingMove k v
183+deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
184+
185+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
186+--
187+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
188+unsafePatchMapWithPatchingMove
189+ :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
190+unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
191+
192+-- | Apply the insertions, deletions, and moves to a given 'Map'
193+instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
194+ type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
195+ -- TODO: return Nothing sometimes
196+ -- Note: the strict application here is critical to ensuring that incremental
197+ -- merges don't hold onto all their prerequisite events forever; can we make
198+ -- this more robust?
199+ apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
200+ where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
201+ From_Insert v -> Just v
202+ From_Move k p -> applyAlways p <$> Map.lookup k old
203+ From_Delete -> Nothing
204+ deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
205+ From_Delete -> Just ()
206+ _ -> Nothing
207+
208+-- | Returns all the new elements that will be added to the 'Map'
209+patchMapWithPatchingMoveNewElements
210+ :: PatchMapWithPatchingMove k p -> [PatchTarget p]
211+patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
212+
213+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
214+patchMapWithPatchingMoveNewElementsMap
215+ :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
216+patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
217+ where f ni = case _nodeInfo_from ni of
218+ From_Insert v -> Just v
219+ From_Move _ _ -> Nothing
220+ From_Delete -> Nothing
221+
222+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
223+-- its values using the given ordering function. The set keys of the 'Map' is
224+-- not changed.
225+patchThatSortsMapWith
226+ :: (Ord k, Monoid p)
227+ => (PatchTarget p -> PatchTarget p -> Ordering)
228+ -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
229+patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
230+ where unsorted = Map.toList m
231+ sorted = sortBy (cmp `on` snd) unsorted
232+ f (to, _) (from, _) = if to == from then Nothing else
233+ Just (from, to)
234+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
235+ g (to, _) (from, _) = if to == from then Nothing else
236+ let Just movingTo = Map.lookup from reverseMapping
237+ in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
238+
239+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
240+-- will produce a 'Map' with the same values as the second 'Map' but with the
241+-- values sorted with the given ordering function.
242+patchThatChangesAndSortsMapWith
243+ :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
244+ => (PatchTarget p -> PatchTarget p -> Ordering)
245+ -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
246+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
247+ where newList = Map.toList newByIndexUnsorted
248+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
249+
250+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
251+-- will produce the second 'Map'.
252+patchThatChangesMap
253+ :: (Ord k, Ord (PatchTarget p), Monoid p)
254+ => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
255+patchThatChangesMap oldByIndex newByIndex = patch
256+ where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
257+ (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
258+ let f k v = do
259+ remainingValues <- get
260+ let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
261+ then Map.delete v remainingValues
262+ else Map.insert v remainingKeys remainingValues
263+ case Map.lookup v remainingValues of
264+ Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
265+ Just fromKs ->
266+ if k `Set.member` fromKs
267+ then do
268+ putRemainingKeys $ Set.delete k fromKs
269+ return $ NodeInfo (From_Move k mempty) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
270+ else do
271+ (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
272+ putRemainingKeys remainingKeys
273+ return $ NodeInfo (From_Move fromK mempty) $ Just undefined
274+ Map.traverseWithKey f newByIndex
275+ unusedOldKeys = fold unusedValuesByValue
276+ pointlessMove k = \case
277+ From_Move k' _ | k == k' -> True
278+ _ -> False
279+ keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
280+ then Just undefined
281+ else Nothing
282+ 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
283+
284+-- | Change the 'From' value of a 'NodeInfo'
285+nodeInfoMapFrom
286+ :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
287+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
288+
289+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
290+-- 'Applicative', 'Monad', etc.) action to get the new value
291+nodeInfoMapMFrom
292+ :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
293+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
294+
295+-- | Set the 'To' field of a 'NodeInfo'
296+nodeInfoSetTo
297+ :: To k -> NodeInfo k v -> NodeInfo k v
298+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
299+
300+-- | Helper data structure used for composing patches using the monoid instance.
301+data Fixup k v
302+ = Fixup_Delete
303+ | Fixup_Update (These (From k v) (To k))
304+
305+-- | Compose patches having the same effect as applying the patches in turn:
306+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
307+instance ( Ord k
308+#if !MIN_VERSION_base(4,11,0)
309+ , Semigroup p
310+#endif
311+ , DecidablyEmpty p
312+ , Patch p
313+ ) => Semigroup (PatchMapWithPatchingMove k p) where
314+ PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
315+ where
316+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
317+ h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
318+ h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
319+ (Just toAfter, From_Move fromBefore p)
320+ | fromBefore == toAfter && isEmpty p
321+ -> [(toAfter, Fixup_Delete)]
322+ | otherwise
323+ -> [ (toAfter, Fixup_Update (This editBefore))
324+ , (fromBefore, Fixup_Update (That mToAfter))
325+ ]
326+ (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
327+ (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
328+ (Nothing, _) -> []
329+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
330+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
331+ | This x <- a, That y <- b
332+ = Fixup_Update $ These x y
333+ | That y <- a, This x <- b
334+ = Fixup_Update $ These x y
335+ mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
336+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
337+ combineNodeInfos _ nia nib = NodeInfo
338+ { _nodeInfo_from = _nodeInfo_from nia
339+ , _nodeInfo_to = _nodeInfo_to nib
340+ }
341+ applyFixup _ ni = \case
342+ Fixup_Delete -> Nothing
343+ Fixup_Update u -> Just $ NodeInfo
344+ { _nodeInfo_from = case _nodeInfo_from ni of
345+ f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
346+ Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
347+ Just (From_Insert v) -> From_Insert $ applyAlways p' v
348+ Just From_Delete -> From_Delete
349+ Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
350+ _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
351+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
352+ }
353+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
354+ getHere :: These a b -> Maybe a
355+ getHere = \case
356+ This a -> Just a
357+ These a _ -> Just a
358+ That _ -> Nothing
359+ getThere :: These a b -> Maybe b
360+ getThere = \case
361+ This _ -> Nothing
362+ These _ b -> Just b
363+ That b -> Just b
364+
365+--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
366+-- | Compose patches having the same effect as applying the patches in turn:
367+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
368+instance ( Ord k
369+#if !MIN_VERSION_base(4,11,0)
370+ , Semigroup p
371+#endif
372+ , DecidablyEmpty p
373+ , Patch p
374+ ) => Monoid (PatchMapWithPatchingMove k p) where
375+ mempty = PatchMapWithPatchingMove mempty
376+ mappend = (<>)
377+
378+makeWrapped ''PatchMapWithPatchingMove