on 69a7ab8 help sign in
ChangeLog.md+4
since splitsnapshots
unifiedsplit
@@ -4,6 +4,10 @@
4
4
5* `PatchMapWithMove` supports moves with a patch.
5* `PatchMapWithMove` supports moves with a patch.
6
6
7+* Add `PatchDMapWithReset`
8+
9+* Add `Patchable`
10+
7## 0.0.1.0
11## 0.0.1.0
8
12
9* Support older GHCs with `split-these` flag.
13* Support older GHCs with `split-these` flag.
patch.cabal+3
@@ -44,14 +44,17 @@ library
44 , Data.Patch.Class
44 , Data.Patch.Class
45 , Data.Patch.DMap
45 , Data.Patch.DMap
46 , Data.Patch.DMapWithMove
46 , Data.Patch.DMapWithMove
47+ , Data.Patch.DMapWithReset
47 , Data.Patch.IntMap
48 , Data.Patch.IntMap
48 , Data.Patch.Map
49 , Data.Patch.Map
49 , Data.Patch.MapWithMove
50 , Data.Patch.MapWithMove
51+ , Data.Patch.Patchable
50
52
51 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
53 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
52
54
53 if flag(split-these)
55 if flag(split-these)
54 build-depends: these >= 1 && <1.1
56 build-depends: these >= 1 && <1.1
57+ , these-lens >= 1 && <1.1
55 , semialign >=1 && <1.2
58 , semialign >=1 && <1.2
56 , monoidal-containers >= 0.6 && < 0.7
59 , monoidal-containers >= 0.6 && < 0.7
57 else
60 else
src/Data/Patch.hs+7−5
@@ -51,8 +51,10 @@ class Semigroup q => Additive q where
51-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
51-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
52newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
52newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
53
53
54−instance Additive p => Patch (AdditivePatch p) where
54+instance Additive p => PatchHet (AdditivePatch p) where
55+ type PatchSource (AdditivePatch p) = p
55 type PatchTarget (AdditivePatch p) = p
56 type PatchTarget (AdditivePatch p) = p
57+instance Additive p => Patch (AdditivePatch p) where
56 apply (AdditivePatch p) q = Just $ p <> q
58 apply (AdditivePatch p) q = Just $ p <> q
57
59
58instance (Ord k, Group q) => Group (MonoidalMap k q) where
60instance (Ord k, Group q) => Group (MonoidalMap k q) where
@@ -62,8 +64,8 @@ instance (Ord k, Additive q) => Additive (MonoidalMap k q)
62
64
63-- | Trivial group.
65-- | Trivial group.
64instance Group () where
66instance Group () where
65− negateG _ = ()
67+ negateG ~() = ()
66− _ ~~ _ = ()
68+ ~() ~~ ~() = ()
67instance Additive ()
69instance Additive ()
68
70
69-- | Product group. A Pair of groups gives rise to a group
71-- | Product group. A Pair of groups gives rise to a group
@@ -88,8 +90,8 @@ instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
88
90
89-- | Trivial group, Functor style
91-- | Trivial group, Functor style
90instance Group (Proxy x) where
92instance Group (Proxy x) where
91− negateG _ = Proxy
93+ negateG ~Proxy = Proxy
92− _ ~~ _ = Proxy
94+ ~Proxy ~~ ~Proxy = Proxy
93instance Additive (Proxy x)
95instance Additive (Proxy x)
94
96
95-- | Const lifts groups into a functor.
97-- | Const lifts groups into a functor.
src/Data/Patch/Class.hs+134−6
@@ -1,4 +1,10 @@
1+{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE PolyKinds #-}
1{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
7+{-# LANGUAGE TypeOperators #-}
2-- | The interface for types which represent changes made to other types
8-- | The interface for types which represent changes made to other types
3module Data.Patch.Class where
9module Data.Patch.Class where
4
10
@@ -6,13 +12,39 @@ import Data.Functor.Identity
6import Data.Maybe
12import Data.Maybe
7import Data.Semigroup (Semigroup(..))
13import Data.Semigroup (Semigroup(..))
8import Data.Proxy
14import Data.Proxy
15+import Data.Type.Equality ((:~:) (..))
16+
17+class PatchHet p where
18+ type PatchSource p :: *
19+ type PatchTarget p :: *
20+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
21+ -- 'Nothing'.
22+ applyHet
23+ :: p
24+ -> PatchSource p
25+ -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
26+ default applyHet
27+ :: Patch p
28+ => p
29+ -> PatchSource p
30+ -> Either (PatchSource p :~: PatchTarget p) (PatchTarget p)
31+ applyHet p a = case apply p a of
32+ Nothing -> Left Refl
33+ Just a' -> Right a'
34+
35+-- | Apply a 'PatchHet'; if it does nothing, return the original value
36+applyAlwaysHet :: PatchHet p => p -> PatchSource p -> PatchTarget p
37+applyAlwaysHet p t = case applyHet p t of
38+ Left Refl -> t
39+ Right t' -> t'
9
40
10-- | A 'Patch' type represents a kind of change made to a datastructure.
41-- | A 'Patch' type represents a kind of change made to a datastructure.
11--
42--
12-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
43-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
13-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
44-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
45+class ( PatchHet p
46+ , PatchSource p ~ PatchTarget p
14−class Patch p where
47+ ) => Patch p where
15− type PatchTarget p :: *
16 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
48 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
17 -- 'Nothing'.
49 -- 'Nothing'.
18 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
50 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -22,19 +54,115 @@ applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
22applyAlways p t = fromMaybe t $ apply p t
54applyAlways p t = fromMaybe t $ apply p t
23
55
24-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
56-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
25−instance Patch (Identity a) where
57+instance PatchHet (Identity a) where
58+ type PatchSource (Identity a) = a
26 type PatchTarget (Identity a) = a
59 type PatchTarget (Identity a) = a
60+instance Patch (Identity a) where
27 apply (Identity a) _ = Just a
61 apply (Identity a) _ = Just a
28
62
29−-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
63+-- | 'Proxy' can be used as a 'Patch' that always fully replaces the value
30−instance Patch (Proxy a) where
64+instance PatchHet (Proxy (a :: *)) where
65+ type PatchSource (Proxy a) = a
31 type PatchTarget (Proxy a) = a
66 type PatchTarget (Proxy a) = a
67+instance Patch (Proxy (a :: *)) where
32 apply ~Proxy _ = Nothing
68 apply ~Proxy _ = Nothing
33
69
34-- | Like '(.)', but composes functions that return patches rather than
70-- | Like '(.)', but composes functions that return patches rather than
35-- functions that return new values. The Semigroup instance for patches must
71-- functions that return new values. The Semigroup instance for patches must
36-- apply patches right-to-left, like '(.)'.
72-- apply patches right-to-left, like '(.)'.
37−composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
73+composePatchFunctions
74+ :: (Patch p, Semigroup p)
75+ => (PatchTarget p -> p)
76+ -> (PatchTarget p -> p)
77+ -> PatchTarget p -> p
38composePatchFunctions g f a =
78composePatchFunctions g f a =
39 let fp = f a
79 let fp = f a
40 in g (applyAlways fp a) <> fp
80 in g (applyAlways fp a) <> fp
81+
82+
83+class PatchHet2Base (p :: k -> k -> *) where
84+ type PatchSource1 p :: k -> *
85+ type PatchTarget1 p :: k -> *
86+
87+class ( PatchHet2Base p
88+ , PatchHet (p from to)
89+ , PatchSource1 p from ~ PatchSource (p from to)
90+ , PatchTarget1 p to ~ PatchTarget (p from to)
91+ ) => PatchHet2Locally (p :: k -> k -> *) from to where
92+instance ( PatchHet2Base p
93+ , PatchHet (p from to)
94+ , PatchSource1 p from ~ PatchSource (p from to)
95+ , PatchTarget1 p to ~ PatchTarget (p from to)
96+ ) => PatchHet2Locally (p :: k -> k -> *) from to where
97+
98+applyHet2Locally
99+ :: PatchHet2Locally p from to
100+ => p from to
101+ -> PatchSource1 p from
102+ -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
103+applyHet2Locally = applyHet
104+
105+applyAlwaysHet2Locally
106+ :: PatchHet2Locally p from to
107+ => p from to
108+ -> PatchSource1 p from
109+ -> PatchTarget1 p to
110+applyAlwaysHet2Locally = applyAlwaysHet
111+
112+-- TODO once we can use quantified constraints, perhaps combine PatchHet2Base and
113+-- PatchHet2Locally, or at least get rid of this.
114+class PatchHet2Base p => PatchHet2 (p :: k -> k -> *) where
115+ applyHet2
116+ :: p from to
117+ -> PatchSource1 p from
118+ -> Either (PatchSource1 p from :~: PatchTarget1 p to) (PatchTarget1 p to)
119+
120+applyAlwaysHet2
121+ :: PatchHet2 p
122+ => p from to
123+ -> PatchSource1 p from
124+ -> PatchTarget1 p to
125+applyAlwaysHet2 p t = case applyHet2 p t of
126+ Left Refl -> t
127+ Right t' -> t'
128+
129+-- | Connect the classes without quanitified constraints
130+newtype ProjectLocal p from to = ProjectLocal { unProjectLocal :: p from to }
131+
132+instance PatchHet2 p => PatchHet (ProjectLocal p from to) where
133+ type PatchSource (ProjectLocal p from to) = PatchSource1 p from
134+ type PatchTarget (ProjectLocal p from to) = PatchTarget1 p to
135+ applyHet (ProjectLocal p) src = applyHet2 p src
136+
137+instance PatchHet2 p => PatchHet2Base (ProjectLocal p) where
138+ type PatchSource1 (ProjectLocal p) = PatchSource1 p
139+ type PatchTarget1 (ProjectLocal p) = PatchTarget1 p
140+
141+class ( PatchHet2Base p
142+ , PatchSource1 p ~ PatchTarget1 p
143+ ) => Patch2 p
144+instance ( PatchHet2Base p
145+ , PatchSource1 p ~ PatchTarget1 p
146+ ) => Patch2 p
147+
148+newtype Replace2 (t :: k -> *) (a :: k) (b :: k) = Replace2 (t b)
149+ deriving ( Show, Read, Eq, Ord
150+ , Functor, Foldable, Traversable
151+ )
152+
153+data Proxy2 t a b = Proxy2
154+ deriving ( Show, Read, Eq, Ord
155+ , Functor, Foldable, Traversable
156+ )
157+
158+-- | 'Replace2' can be used as a 'Patch' that always fully replaces the value
159+instance PatchHet (Replace2 (t :: k -> *) (from :: k) (to :: k)) where
160+ type PatchSource (Replace2 t from to) = t from
161+ type PatchTarget (Replace2 t from to) = t to
162+ applyHet (Replace2 val) _ = Right val
163+
164+-- | 'Proxy2' can be used as a 'Patch' that always does nothing
165+instance PatchHet (Proxy2 (t :: k -> *) (a :: k) (a :: k)) where
166+ type PatchSource (Proxy2 t a a) = t a
167+ type PatchTarget (Proxy2 t a a) = t a
168+ applyHet ~Proxy2 _ = Left Refl
src/Data/Patch/DMap.hs+3−1
@@ -31,8 +31,10 @@ deriving instance GCompare k => Semigroup (PatchDMap k v)
31deriving instance GCompare k => Monoid (PatchDMap k v)
31deriving instance GCompare k => Monoid (PatchDMap k v)
32
32
33-- | Apply the insertions or deletions to a given 'DMap'.
33-- | Apply the insertions or deletions to a given 'DMap'.
34−instance GCompare k => Patch (PatchDMap k v) where
34+instance GCompare k => PatchHet (PatchDMap k v) where
35+ type PatchSource (PatchDMap k v) = DMap k v
35 type PatchTarget (PatchDMap k v) = DMap k v
36 type PatchTarget (PatchDMap k v) = DMap k v
37+instance GCompare k => Patch (PatchDMap k v) where
36 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
38 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
37 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
39 where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
38 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
40 deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
src/Data/Patch/DMapWithMove.hs+383−108
@@ -1,101 +1,305 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE GADTs #-}
5{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE InstanceSigs #-}
5{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE PatternGuards #-}
9{-# LANGUAGE PatternGuards #-}
8{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE Rank2Types #-}
11{-# LANGUAGE Rank2Types #-}
10{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE StandaloneDeriving #-}
14+{-# LANGUAGE TypeApplications #-}
11{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE UndecidableInstances #-}
17+{-# LANGUAGE UndecidableSuperClasses #-}
13
18
14-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
19-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
15-- move values between keys.
20-- move values between keys.
16module Data.Patch.DMapWithMove where
21module Data.Patch.DMapWithMove where
17
22
18−import Data.Patch.Class
23+import qualified Control.Category as Cat
19−import Data.Patch.MapWithMove (PatchMapWithMove (..))
20−import qualified Data.Patch.MapWithMove as MapWithMove
21−
22import Data.Constraint.Extras
24import Data.Constraint.Extras
25+import Data.Constraint.Compose
23import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
26import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
24import qualified Data.Dependent.Map as DMap
27import qualified Data.Dependent.Map as DMap
25import Data.Functor.Constant
28import Data.Functor.Constant
26import Data.Functor.Misc
29import Data.Functor.Misc
27import Data.Functor.Product
30import Data.Functor.Product
28−import Data.GADT.Compare (GEq (..))
31+import Data.GADT.Compare (GEq (..), GCompare)
29−import Data.GADT.Show (GShow, gshow)
32+import Data.GADT.Show (GRead, GShow, gshow)
30import qualified Data.Map as Map
33import qualified Data.Map as Map
31import Data.Maybe
34import Data.Maybe
35+import Data.Monoid.DecidablyEmpty
32import Data.Semigroup (Semigroup (..), (<>))
36import Data.Semigroup (Semigroup (..), (<>))
37+import Data.Semigroupoid as Cat
33import Data.Some (Some(Some))
38import Data.Some (Some(Some))
34import Data.Proxy
39import Data.Proxy
35import Data.These
40import Data.These
41+import Data.Type.Equality ((:~:) (..))
42+import Data.Kind (Constraint)
43+
44+import Data.Patch.Class
45+import Data.Patch.MapWithMove (PatchMapWithMove (..))
46+import qualified Data.Patch.MapWithMove as MapWithMove
47+
48+-- | Composition for constraints.
49+class p (f a a) => DupC (p :: k2 -> Constraint) (f :: k1 -> k1 -> k2) (a :: k1)
50+instance p (f a a) => DupC p f a
36
51
52+type ConstraintsForZip f (c :: k -> Constraint) (g :: k' -> k' -> k) =
53+ ConstraintsFor f (DupC c g)
54+
55+type HasZip (c :: k -> Constraint) f (g :: k' -> k' -> k) =
56+ (ArgDict (DupC c g) f, ConstraintsForZip f c g)
57+
58+-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
37−-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
59+-- which describes how it will be changed by the patch and connects move sources
38−-- destinations.
60+-- and destinations.
39--
61--
40-- Invariants:
62-- Invariants:
41--
63--
42−-- * A key should not move to itself.
64+-- * A key should not move to itself.
65+--
43−-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
66+-- * A move should always be represented with both the destination key (as a
67+-- 'From_Move') and the source key (as a @'ComposeMaybe' ('Just'
68+-- destination)@)
44newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
69newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
45
70
71+deriving instance ( GShow k
72+ , HasZip Show k p
73+ , Has' Show k (PatchTarget1 p)
74+ ) => Show (PatchDMapWithMove k p)
75+deriving instance ( GRead k
76+ , HasZip Read k p
77+ , Has' Read k (PatchTarget1 p)
78+ ) => Read (PatchDMapWithMove k p)
79+deriving instance ( GEq k
80+ , HasZip Eq k p
81+ , Has' Eq k (PatchTarget1 p)
82+ ) => Eq (PatchDMapWithMove k p)
83+deriving instance ( GCompare k
84+ , HasZip Ord k p
85+ , Has' Ord k (PatchTarget1 p)
86+ ) => Ord (PatchDMapWithMove k p)
87+
88+-- | Structure which represents what changes apply to a particular key.
46−-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
89+-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
47−-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
90+-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
91+-- key the current key is moving to if involved in a move.
48−data NodeInfo k v a = NodeInfo
92+data NodeInfo k p a = NodeInfo
49− { _nodeInfo_from :: !(From k v a)
93+ { _nodeInfo_from :: !(From k p a)
50− -- ^Change applying to the current key, be it an insert, move, or delete.
94+ -- ^ Change applying to the current key, be it an insert, move, or delete.
51− , _nodeInfo_to :: !(To k a)
95+ , _nodeInfo_to :: !(To k p a)
52− -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
96+ -- ^ Where this key is moving to, if involved in a move. Should only be
97+ -- @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
53 }
98 }
54− deriving (Show)
99+
55−
100+deriving instance ( Show (k a)
101+ , Show (p a a)
102+ , Show (PatchTarget1 p a)
103+ ) => Show (NodeInfo k p a)
104+deriving instance ( Read (k a)
105+ , Read (p a a)
106+ , Read (PatchTarget1 p a)
107+ ) => Read (NodeInfo k p a)
108+deriving instance ( Eq (k a)
109+ , Eq (p a a)
110+ , Eq (PatchTarget1 p a)
111+ ) => Eq (NodeInfo k p a)
112+deriving instance ( Ord (k a)
113+ , Ord (p a a)
114+ , Ord (PatchTarget1 p a)
115+ ) => Ord (NodeInfo k p a)
116+
117+-- | Structure describing a particular change to a key, be it inserting a new
118+-- key (@By_Insert@), updating an existing key (@By_Insert@ again), deleting
119+-- a key (@By_Delete@), or moving a key (@By_Move@).
120+--
121+-- This type isn't used directly as the from field patch, but is instead wrapped
122+-- in an existential. However, it is nice to be able to reason about this in
123+-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
124+data By (k :: a -> *) (p :: a -> a -> *) :: a -> a -> * where
125+ -- | Insert a new or update an existing key with the given value @PatchTarget1
126+ -- p a@
127+ By_Insert :: PatchTarget1 p to -> By k p from to
128+ -- | Delete the existing key
129+ By_Delete :: By k p from to
130+ -- | Move the value from the given key @k a@ to this key. The source key
131+ -- should also have an entry in the patch giving the current key as
132+ -- @_nodeInfo_to@, usually but not necessarily with @By_Delete@.
133+ By_Move :: !(k from) -> p from to -> By k p from to
134+
135+deriving instance ( Show (k to)
136+ , Show (p from to)
137+ , Show (PatchTarget1 p to)
138+ ) => Show (By k p from to)
139+deriving instance ( Read (k to)
140+ , Read (p from to)
141+ , Read (PatchTarget1 p to)
142+ ) => Read (By k p from to)
143+deriving instance ( Eq (k to)
144+ , Eq (p from to)
145+ , Eq (PatchTarget1 p to)
146+ ) => Eq (By k p from to)
147+deriving instance ( Ord (k to)
148+ , Ord (p from to)
149+ , Ord (PatchTarget1 p to)
150+ ) => Ord (By k p from to)
151+
152+mapByPatch
153+ :: PatchTarget1 p0 ~ PatchTarget1 p1
154+ => ((p0 from to) -> (p1 from to))
155+ -> By k p0 from to
156+ -> By k p1 from to
157+mapByPatch f = \case
158+ By_Insert v -> By_Insert v
159+ By_Delete -> By_Delete
160+ By_Move k p -> By_Move k $ f p
161+
162+-- | Compose patches having the same effect as applying the patches in turn:
163+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
164+instance ( PatchSource1 p ~ PatchTarget1 p
165+ , Cat.Semigroupoid p
166+ , PatchHet2 p
167+ ) => Cat.Semigroupoid (By k p) where
168+ o p0 p1 = mapByPatch unProjectLocal $
169+ oLocal (mapByPatch ProjectLocal p0) (mapByPatch ProjectLocal p1)
170+
171+oLocal
172+ :: ( PatchSource1 p ~ PatchTarget1 p
173+ , PatchHet2Locally p between after
174+ )
175+ => By k p between after
176+ -> By k p before between
177+ -> By k p before after
178+By_Insert new `oLocal` _ = By_Insert new
179+By_Delete `oLocal` _ = By_Delete
180+By_Move _ x `oLocal` By_Insert y = By_Insert $ applyAlwaysHet2 x y
181+By_Move _ x `oLocal` By_Move src y = By_Move src $ x `o` y
182+By_Move _ _ `oLocal` By_Delete = By_Delete
183+
184+newtype Flip p to from = Flip (p from to)
185+
56−-- |Structure describing a particular change to a key, be it inserting a new key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting a
186+-- | Structure describing a particular change to a key, be it inserting a new
187+-- key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting
57−-- key (@From_Delete@), or moving a key (@From_Move@).
188+-- a key (@From_Delete@), or moving a key (@From_Move@).
189+--
190+-- This type isn't used directly as the from field patch, but is instead wrapped
191+-- in an existential. However, it is nice to be able to reason about this in
192+-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
58−data From (k :: a -> *) (v :: a -> *) :: a -> * where
193+data From (k :: a -> *) (p :: a -> a -> *) :: a -> * where
59− -- |Insert a new or update an existing key with the given value @v a@
194+ -- | Insert a new or update an existing key with the given value @PatchTarget1
195+ -- p a@
60− From_Insert :: v a -> From k v a
196+ From_Insert :: PatchTarget1 p to -> From k p to
61− -- |Delete the existing key
197+ -- | Delete the existing key
62− From_Delete :: From k v a
198+ From_Delete :: From k p to
199+ -- | Move the value from the given key @k a@ to this key. The source key
63− -- |Move the value from the given key @k a@ to this key. The source key should also have an entry in the patch giving the current key as @_nodeInfo_to@,
200+ -- should also have an entry in the patch giving the current key as
64− -- usually but not necessarily with @From_Delete@.
201+ -- @_nodeInfo_to@, usually but not necessarily with @From_Delete@.
202+ From_Move :: !(DSum k (Flip p to)) -> From k p to
203+
204+deriving instance ( Show (k a)
205+ , Show (p a a)
206+ , Show (PatchTarget1 p a)
65− From_Move :: !(k a) -> From k v a
207+ ) => Show (From k p a)
66− deriving (Show, Read, Eq, Ord)
208+deriving instance ( Read (k a)
67−
209+ , Read (p a a)
68−-- |Type alias for the "to" part of a 'NodeInfo'. @'ComposeMaybe' ('Just' k)@ means the key is moving to another key, @ComposeMaybe Nothing@ for any other
210+ , Read (PatchTarget1 p a)
69−-- operation.
211+ ) => Read (From k p a)
70−type To = ComposeMaybe
212+
213+deriving instance ( GEq k
214+ , HasZip Eq k p
215+ , Eq (PatchTarget1 p a)
216+ ) => Eq (From k p a)
217+
218+deriving instance ( GCompare k
219+ , HasZip Ord k p
220+ , Ord (PatchTarget1 p a)
221+ ) => Ord (From k p a)
222+
223+-- | The "to" part of a 'NodeInfo'. Rather than be built out of @From@ like @From@
224+-- is, we store just the information necessary to compose a @To@ and @From@ like
225+-- @oLocal@ composes two @From@s.
226+data To (k :: a -> *) (p :: a -> a -> *) :: a -> * where
227+ -- | Delete or leave in place
228+ To_NonMove :: To k p from
229+ -- | Move the value from the given key @k a@ to this key. The target key
230+ -- should also have an entry in the patch giving the current key in
231+ -- @_nodeInfo_from@, usually but not necessarily with @To_Delete@.
232+ To_Move :: !(DSum k (p from)) -> To k p from
233+
234+deriving instance ( Show (k a)
235+ , Show (p a a)
236+ , Show (PatchTarget1 p a)
237+ ) => Show (To k p a)
238+deriving instance ( Read (k a)
239+ , Read (p a a)
240+ , Read (PatchTarget1 p a)
241+ ) => Read (To k p a)
242+
243+deriving instance ( GEq k
244+ , HasZip Eq k p
245+ , Eq (PatchTarget1 p a)
246+ ) => Eq (To k p a)
247+
248+deriving instance ( GCompare k
249+ , HasZip Ord k p
250+ , Ord (PatchTarget1 p a)
251+ ) => Ord (To k p a)
252+
253+--oToFrom
254+-- :: ( PatchSource1 p ~ PatchTarget1 p
255+-- , PatchHet2 p
256+-- )
257+-- => To k p between
258+-- -> From k p between
259+-- -> DSum k (From k p)
260+--From_Insert new `oToFrom` _ = From_Insert new
261+--From_Delete `oToFrom` _ = From_Delete
262+--From_Move _ x `oToFrom` From_Insert y = From_Insert $ applyAlwaysHet2 x y
263+--From_Move _ x `oToFrom` From_Move src y = From_Move src $ x `o` y
264+--From_Move _ _ `oToFrom` From_Delete = From_Delete
71
265
72-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
266-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
73−validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
267+validPatchDMapWithMove
268+ :: forall k v
269+ . (GCompare k, GShow k)
270+ => DMap k (NodeInfo k v)
271+ -> Bool
74validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
272validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
75
273
76-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
274-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
77−validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
275+validationErrorsForPatchDMapWithMove
276+ :: forall k v
277+ . (GCompare k, GShow k)
278+ => DMap k (NodeInfo k v)
279+ -> [String]
78validationErrorsForPatchDMapWithMove m =
280validationErrorsForPatchDMapWithMove m =
79 noSelfMoves <> movesBalanced
281 noSelfMoves <> movesBalanced
80 where
282 where
81 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
283 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
284+ selfMove (dst :=> NodeInfo (From_Move (src :=> _)) _)
82− selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
285+ | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
286+ selfMove (src :=> NodeInfo _ (To_Move (dst :=> _)))
83− selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
287+ | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
84 selfMove _ = Nothing
288 selfMove _ = Nothing
85 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
289 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
86− unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
290+ unbalancedMove (dst :=> NodeInfo (From_Move (src :=> _)) _) =
87 case DMap.lookup src m of
291 case DMap.lookup src m of
88 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
292 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
89− Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
293+ Just (NodeInfo _ (To_Move (dst' :=> _))) ->
90 if isNothing (dst' `geq` dst)
294 if isNothing (dst' `geq` dst)
91 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
295 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
92 else Nothing
296 else Nothing
93 _ ->
297 _ ->
94 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
298 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
95− unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
299+ unbalancedMove (src :=> NodeInfo _ (To_Move (dst :=> _))) =
96 case DMap.lookup dst m of
300 case DMap.lookup dst m of
97 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
301 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
98− Just (NodeInfo (From_Move src') _) ->
302+ Just (NodeInfo (From_Move (src' :=> _)) _) ->
99 if isNothing (src' `geq` src)
303 if isNothing (src' `geq` src)
100 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
304 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
101 else Nothing
305 else Nothing
@@ -104,35 +308,49 @@ validationErrorsForPatchDMapWithMove m =
104 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
308 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
105 unbalancedMove _ = Nothing
309 unbalancedMove _ = Nothing
106
310
107−-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
108−instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
109− PatchDMapWithMove a == PatchDMapWithMove b = a == b
110−
111-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
311-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
112data Pair1 f g a = Pair1 (f a) (g a)
312data Pair1 f g a = Pair1 (f a) (g a)
113
313
114-- |Helper data structure used for composing patches using the monoid instance.
314-- |Helper data structure used for composing patches using the monoid instance.
115−data Fixup k v a
315+data Fixup k p a
116 = Fixup_Delete
316 = Fixup_Delete
117− | Fixup_Update (These (From k v a) (To k a))
317+ | Fixup_Update (These (From k p a) (To k p a))
118−
318+
119−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
319+-- | Compose patches having the same effect as applying the patches in turn:
320+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
321+instance ( GCompare k
322+ , Cat.Semigroupoid p
323+ -- , DecidablyEmpty p
324+ , PatchHet2 p
325+ , PatchSource1 p ~ PatchTarget1 p
120−instance GCompare k => Semigroup (PatchDMapWithMove k v) where
326+ ) => Semigroup (PatchDMapWithMove k p) where
121 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
327 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
122 where
328 where
329+ connections :: [DSum k (Pair1 (To k p) (From k p))]
330+ connections = DMap.toList $ DMap.intersectionWithKey
123− connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
331+ (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b))
332+ ma
333+ mb
124− h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
334+ h :: DSum k (Pair1 (To k p) (From k p)) -> [DSum k (Fixup k p)]
125− h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
335+ h ((between :: k between) :=> Pair1 editAfter editBefore) = case (editAfter, editBefore) of
126− (Just toAfter, From_Move fromBefore)
336+ (To_Move ((toAfter :: k after) :=> p1), From_Move ((fromBefore :: k before) :=> Flip p0)) ->
127− | isJust $ fromBefore `geq` toAfter
337+ case toAfter `geq` fromBefore of
338+ Just Refl | isNull p0 ->
128− -> [toAfter :=> Fixup_Delete]
339+ [ toAfter :=> Fixup_Delete ]
129− | otherwise
340+ _ ->
130− -> [ toAfter :=> Fixup_Update (This editBefore)
341+ [ toAfter :=> Fixup_Update (This $ From_Move $ fromBefore :=> (Flip $ p1 `o` p0))
131− , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
342+ , fromBefore :=> Fixup_Update (That $ To_Move $ toAfter :=> (p1 `o` p0))
132− ]
343+ ]
344+ (To_NonMove, From_Move (fromBefore :=> _)) ->
133− (Nothing, From_Move fromBefore) -> [fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
345+ -- The item is destroyed in the second patch, so indicate that it is
346+ -- destroyed in the source map
347+ [fromBefore :=> Fixup_Update (That To_NonMove)]
348+ (To_Move (toAfter :=> p), From_Insert val) ->
349+ [toAfter :=> Fixup_Update (This $ From_Insert $ applyAlwaysHet2 p val)]
350+ (To_Move (toAfter :=> p), From_Delete) ->
134− (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
351+ [toAfter :=> Fixup_Update (This From_Delete)]
135− (Nothing, _) -> []
352+ (To_NonMove, _) ->
353+ []
136 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
354 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
137 mergeFixups _ (Fixup_Update a) (Fixup_Update b)
355 mergeFixups _ (Fixup_Update a) (Fixup_Update b)
138 | This x <- a, That y <- b
356 | This x <- a, That y <- b
@@ -163,13 +381,19 @@ instance GCompare k => Semigroup (PatchDMapWithMove k v) where
163 These _ b -> Just b
381 These _ b -> Just b
164 That b -> Just b
382 That b -> Just b
165
383
166−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
384+-- | Compose patches having the same effect as applying the patches in turn:
385+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
386+instance ( GCompare k
387+ , Cat.Semigroupoid p
388+ -- , DecidablyEmpty p
389+ , PatchHet2 p
390+ , PatchSource1 p ~ PatchTarget1 p
167−instance GCompare k => Monoid (PatchDMapWithMove k v) where
391+ ) => Monoid (PatchDMapWithMove k p) where
168 mempty = PatchDMapWithMove mempty
392 mempty = PatchDMapWithMove mempty
169 mappend = (<>)
393 mappend = (<>)
170
394
171{-
395{-
172−mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
396+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
173PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
397PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
174 where
398 where
175 getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
399 getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
@@ -190,21 +414,21 @@ PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWith
190 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
414 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
191-}
415-}
192
416
193−-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @v a@ to the given key @k a@, like 'DMap.insert'.
417+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @PatchTarget1 p a@ to the given key @k a@, like 'DMap.insert'.
194−insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
418+insertDMapKey :: k a -> PatchTarget1 p a -> PatchDMapWithMove k p
195insertDMapKey k v =
419insertDMapKey k v =
196− PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
420+ PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) To_NonMove
197
421
198-- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to:
422-- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to:
199--
423--
200-- @
424-- @
201-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
425-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
202-- @
426-- @
203−moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
427+moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy2 v)
204moveDMapKey src dst = case src `geq` dst of
428moveDMapKey src dst = case src `geq` dst of
205 Nothing -> PatchDMapWithMove $ DMap.fromList
429 Nothing -> PatchDMapWithMove $ DMap.fromList
206− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
430+ [ dst :=> NodeInfo (From_Move (src :=> Cat.id)) To_NonMove
207− , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
431+ , src :=> NodeInfo From_Delete (To_Move $ dst :=> Proxy2)
208 ]
432 ]
209 Just _ -> mempty
433 Just _ -> mempty
210
434
@@ -220,8 +444,8 @@ moveDMapKey src dst = case src `geq` dst of
220swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
444swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
221swapDMapKey src dst = case src `geq` dst of
445swapDMapKey src dst = case src `geq` dst of
222 Nothing -> PatchDMapWithMove $ DMap.fromList
446 Nothing -> PatchDMapWithMove $ DMap.fromList
223− [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
447+ [ dst :=> NodeInfo (From_Move (src :=> Cat.id)) (ComposeMaybe $ Just src)
224− , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
448+ , src :=> NodeInfo (From_Move (dst :=> Cat.id)) (To_Move $ dst :=> Proxy2)
225 ]
449 ]
226 Just _ -> mempty
450 Just _ -> mempty
227
451
@@ -272,41 +496,75 @@ patchDMapWithMove dm =
272 errs -> Left errs
496 errs -> Left errs
273
497
274-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
498-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
275−mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
499+mapPatchDMapWithMove
500+ :: forall k p p'
501+ . (forall a. PatchTarget1 p a -> PatchTarget1 p' a)
502+ -> (forall from to. p from to -> p' from to)
503+ -> PatchDMapWithMove k p
504+ -> PatchDMapWithMove k p'
276−mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
505+mapPatchDMapWithMove f g (PatchDMapWithMove p) = PatchDMapWithMove $
277 DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
506 DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
278− where g :: forall a. From k v a -> From k v' a
507+ where g :: forall a. From k PatchTarget1 p a -> From k PatchTarget1 p' a
279 g = \case
508 g = \case
280 From_Insert v -> From_Insert $ f v
509 From_Insert v -> From_Insert $ f v
281 From_Delete -> From_Delete
510 From_Delete -> From_Delete
282− From_Move k -> From_Move k
511+ From_Move k p -> From_Move k $ g p
283−
512+
513+-- | Traverse an effectful function @forall a. PatchTarget1 p a -> m (v ' a)@
284−-- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
514+-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
515+-- ('PatchDMapWithMove' k v')@.
285−traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
516+traversePatchDMapWithMove
517+ :: forall m k p p'
518+ . Applicative m
519+ => (forall a. PatchTarget1 p a -> m (PatchTarget1 p' a))
520+ -> PatchDMapWithMove k p
521+ -> m (PatchDMapWithMove k p')
286traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
522traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
287
523
524+-- | Map an effectful function @forall a. k a -> PatchTarget1 p a -> m (v ' a)@
288−-- |Map an effectful function @forall a. k a -> v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
525+-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
526+-- ('PatchDMapWithMove' k v')@.
289−traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
527+traversePatchDMapWithMoveWithKey
528+ :: forall m k p p'
529+ . Applicative m
530+ => (forall a. k a -> PatchTarget1 p a -> m (PatchTarget1 p' a))
531+ -> PatchDMapWithMove k p
532+ -> m (PatchDMapWithMove k p')
533+traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) =
290−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
534+ PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
291− where g :: forall a. k a -> From k v a -> m (From k v' a)
535+ where g :: forall a. k a -> From k PatchTarget1 p a -> m (From k PatchTarget1 p' a)
292 g k = \case
536 g k = \case
293 From_Insert v -> From_Insert <$> f k v
537 From_Insert v -> From_Insert <$> f k v
294 From_Delete -> pure From_Delete
538 From_Delete -> pure From_Delete
295 From_Move fromKey -> pure $ From_Move fromKey
539 From_Move fromKey -> pure $ From_Move fromKey
296
540
297−-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
541+-- | Map a function which transforms @'From k PatchTarget1 p a@ into a @'From k
542+-- PatchTarget1 p' a@ over a @'NodeInfo' k PatchTarget1 p a@.
298−nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
543+nodeInfoMapFrom
544+ :: (From k p a -> From k p' a)
545+ -> NodeInfo k p a
546+ -> NodeInfo k p' a
299nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
547nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
300
548
301−-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
549+-- | Map an effectful function which transforms @'From k PatchTarget1 p a@ into
550+-- a @f ('From k PatchTarget1 p' a)@ over a @'NodeInfo' k PatchTarget1 p a@.
302−nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
551+nodeInfoMapFromM
552+ :: Functor f
553+ => (From k p a -> f (From k p' a))
554+ -> NodeInfo k p a
555+ -> f (NodeInfo k p' a)
303nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
556nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
304
557
305−-- |Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys from @k a@ to @'Some' k@ and applying a given weakening function @v a -> v'@ to
558+-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
559+-- from @k a@ to @'Some' k@ and applying a given weakening function
306−-- values.
560+-- @PatchTarget1 p a -> v'@ to values.
307−weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) (Proxy v')
561+weakenPatchDMapWithMoveWith
562+ :: forall k p v'
563+ . (forall a. PatchTarget1 p a -> v')
564+ -> PatchDMapWithMove k p
565+ -> PatchMapWithMove (Some k) (Proxy v')
308weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
566weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
309− where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) (Proxy v')
567+ where g :: forall a. NodeInfo k PatchTarget1 p a -> MapWithMove.NodeInfo (Some k) (Proxy v')
310 g ni = MapWithMove.NodeInfo
568 g ni = MapWithMove.NodeInfo
311 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
569 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
312 From_Insert v -> MapWithMove.From_Insert $ f v
570 From_Insert v -> MapWithMove.From_Insert $ f v
@@ -315,11 +573,16 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
315 , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
573 , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
316 }
574 }
317
575
576+-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
318−-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
577+-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
319−-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
578+-- dependency in the typing and all points are already @a@, hence the function
579+-- to map each value to @v'@ is not higher rank.
320−patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k (Proxy v')
580+patchDMapWithMoveToPatchMapWithMoveWith
581+ :: forall k p v' a. (PatchTarget1 p a -> v')
582+ -> PatchDMapWithMove (Const2 k a) p
583+ -> PatchMapWithMove k (Proxy v')
321patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
584patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
322− where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k (Proxy v')
585+ where g :: NodeInfo (Const2 k a) PatchTarget1 p a -> MapWithMove.NodeInfo k (Proxy v')
323 g ni = MapWithMove.NodeInfo
586 g ni = MapWithMove.NodeInfo
324 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
587 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
325 From_Insert v -> MapWithMove.From_Insert $ f v
588 From_Insert v -> MapWithMove.From_Insert $ f v
@@ -328,10 +591,16 @@ patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMo
328 , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
591 , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
329 }
592 }
330
593
331−-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
594+-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
595+-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
332−-- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@.
596+-- dependently typed one but which always has a constant key type represented by
597+-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
333−-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
598+-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
334−const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k (Proxy v) -> PatchDMapWithMove (Const2 k a) v'
599+const2PatchDMapWithMoveWith
600+ :: forall k v p' a
601+ . (v -> PatchTarget1 p' a)
602+ -> PatchMapWithMove k (Proxy v)
603+ -> PatchDMapWithMove (Const2 k a) p'
335const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
604const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
336 where g :: (k, MapWithMove.NodeInfo k (Proxy v)) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
605 where g :: (k, MapWithMove.NodeInfo k (Proxy v)) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
337 g (k, ni) = Const2 k :=> NodeInfo
606 g (k, ni) = Const2 k :=> NodeInfo
@@ -343,22 +612,28 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
343 }
612 }
344
613
345-- | Apply the insertions, deletions, and moves to a given 'DMap'.
614-- | Apply the insertions, deletions, and moves to a given 'DMap'.
615+instance GCompare k => PatchHet (PatchDMapWithMove k p) where
616+ type PatchSource (PatchDMapWithMove k p) = DMap k (PatchSource1 p)
617+ type PatchTarget (PatchDMapWithMove k p) = DMap k (PatchTarget1 p)
346−instance GCompare k => Patch (PatchDMapWithMove k v) where
618+instance GCompare k => Patch (PatchDMapWithMove k p) where
347− type PatchTarget (PatchDMapWithMove k v) = DMap k v
348− apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
619+ apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions)
620+ -- TODO: return Nothing sometimes --Note: the strict application here is
621+ -- critical to ensuring that incremental merges don't hold onto all their
622+ -- prerequisite events forever; can we make this more robust?
349 where insertions = DMap.mapMaybeWithKey insertFunc p
623 where insertions = DMap.mapMaybeWithKey insertFunc p
350− insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
624+ insertFunc :: forall a. k a -> NodeInfo k p a -> Maybe (PatchTarget1 p a)
351 insertFunc _ ni = case _nodeInfo_from ni of
625 insertFunc _ ni = case _nodeInfo_from ni of
352 From_Insert v -> Just v
626 From_Insert v -> Just v
353 From_Move k -> DMap.lookup k old
627 From_Move k -> DMap.lookup k old
354 From_Delete -> Nothing
628 From_Delete -> Nothing
355 deletions = DMap.mapMaybeWithKey deleteFunc p
629 deletions = DMap.mapMaybeWithKey deleteFunc p
356− deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
630+ deleteFunc :: forall a. k a -> NodeInfo k p a -> Maybe (Constant () a)
357 deleteFunc _ ni = case _nodeInfo_from ni of
631 deleteFunc _ ni = case _nodeInfo_from ni of
358 From_Delete -> Just $ Constant ()
632 From_Delete -> Just $ Constant ()
359 _ -> Nothing
633 _ -> Nothing
360
634
361−-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
635+-- | Get the values that will be replaced, deleted, or moved if the given patch
636+-- is applied to the given 'DMap'.
362getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
637getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
363getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
638getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
364 where f _ v ni = Pair v $ _nodeInfo_to ni
639 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Data/Patch/DMapWithReset.hsnew file+122
@@ -0,0 +1,122 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE PolyKinds #-}
6+{-# LANGUAGE Rank2Types #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE StandaloneDeriving #-}
9+{-# LANGUAGE TypeApplications #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE UndecidableInstances #-}
12+
13+{-# OPTIONS_GHC -Wall #-}
14+
15+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
16+module Data.Patch.DMapWithReset where
17+
18+import Data.Patch.Class
19+
20+import Data.Dependent.Map (DMap, GCompare (..))
21+import qualified Data.Dependent.Map as DMap
22+import Data.Semigroup (Semigroup (..))
23+import Data.Constraint.Extras
24+
25+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
26+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
27+-- while deletions are represented as @'ComposeMaybe' Nothing@.
28+newtype PatchDMapWithReset k p = PatchDMapWithReset { unPatchDMapWithReset :: DMap k (By p) }
29+
30+-- | Holds the information about each key: where its new value should come from,
31+-- and where its old value should go to
32+data By p a
33+ = By_Insert (PatchTarget (p a)) -- ^ Insert the given value here
34+ | By_Delete -- ^ Delete the existing value, if any, from here
35+ | By_Patch (p a) -- ^ Patch the value here with the given patch
36+
37+instance (Semigroup (p a), Patch (p a)) => Semigroup (By p a) where
38+ x@(By_Insert _) <> _ = x
39+ By_Delete <> _ = By_Delete
40+ By_Patch x <> By_Insert y = By_Insert (applyAlways x y)
41+ By_Patch x <> By_Patch y = By_Patch (x <> y)
42+ By_Patch _ <> By_Delete = By_Delete
43+
44+instance (Monoid (p a), Patch (p a)) => Monoid (By p a) where
45+ mappend = (<>)
46+ mempty = By_Patch mempty
47+
48+instance
49+ ( GCompare k
50+ , Has' Semigroup k p
51+ , Has' Patch k p
52+ )
53+ => Semigroup (PatchDMapWithReset k p) where
54+ PatchDMapWithReset xs <> PatchDMapWithReset ys = PatchDMapWithReset $ DMap.unionWithKey
55+ (\k -> has' @Patch @p k
56+ $ has' @Semigroup @p k
57+ $ (<>)) xs ys
58+
59+instance
60+ ( GCompare k
61+ , Has' Semigroup k p
62+ , Has' Patch k p
63+ )
64+ => Monoid (PatchDMapWithReset k p) where
65+ mappend = (<>)
66+ mempty = PatchDMapWithReset DMap.empty
67+
68+class (Patch (p a), PatchTarget (p a) ~ Patches1LocallyTarget p a) => Patches1Locally p a where
69+ type Patches1LocallyTarget p :: k -> *
70+
71+data These1 f g x
72+ = This1 (f x)
73+ | That1 (g x)
74+ | These1 (f x) (g x)
75+
76+mergeWithKey
77+ :: forall k v1 v2 v.
78+ (GCompare k)
79+ => (forall x. k x -> v1 x -> Maybe (v x))
80+ -> (forall x. k x -> v2 x -> Maybe (v x))
81+ -> (forall x. k x -> v1 x -> v2 x -> Maybe (v x))
82+ -> DMap k v1 -> DMap k v2 -> DMap k v
83+mergeWithKey f g fg = \xs ys -> DMap.mapMaybeWithKey onlyThat $ DMap.unionWithKey doIt (DMap.map This1 xs) (DMap.map That1 ys)
84+ where
85+ doIt _ (This1 xs) (That1 ys) = These1 xs ys
86+ doIt _ _ _ = error "mergeWithKey misalligned keys"
87+
88+ onlyThat :: forall x. k x -> These1 v1 v2 x -> Maybe (v x)
89+ onlyThat k = \case
90+ This1 xs -> f k xs
91+ That1 ys -> g k ys
92+ These1 xs ys -> fg k xs ys
93+{-# INLINE mergeWithKey #-}
94+
95+-- | Apply the insertions or deletions to a given 'DMap'.
96+instance (GCompare k, Has (Patches1Locally p) k) => PatchHet (PatchDMapWithReset k p) where
97+
98+ type PatchSource (PatchDMapWithReset k p) = DMap k (Patches1LocallyTarget p)
99+ type PatchTarget (PatchDMapWithReset k p) = DMap k (Patches1LocallyTarget p)
100+
101+instance (GCompare k, Has (Patches1Locally p) k) => Patch (PatchDMapWithReset k p) where
102+ apply = go
103+ where
104+ go :: PatchDMapWithReset k p -> DMap k (Patches1LocallyTarget p) -> Maybe (DMap k (Patches1LocallyTarget p))
105+ go (PatchDMapWithReset diff) old = Just $! mergeWithKey (\_ -> Just) inserts updates old diff
106+ where
107+ updates :: forall x. k x -> Patches1LocallyTarget p x -> By p x -> Maybe (Patches1LocallyTarget p x)
108+ updates k ys = has @(Patches1Locally p) k $ \case
109+ By_Insert x -> Just x
110+ By_Delete -> Nothing
111+ By_Patch x -> Just $ applyAlways x ys
112+
113+ inserts :: forall x. k x -> By p x -> Maybe (Patches1LocallyTarget p x)
114+ inserts k = has @(Patches1Locally p) k $ \case
115+ By_Insert x -> Just x
116+ By_Delete -> Nothing
117+ By_Patch _ -> Nothing
118+
119+deriving instance (Patch (p a), Eq (p a), Eq (PatchTarget (p a))) => Eq (By p a)
120+deriving instance (Patch (p a), Show (p a), Show (PatchTarget (p a))) => Show (By p a)
121+deriving instance (Eq (DMap k (By p))) => Eq (PatchDMapWithReset k p)
122+deriving instance (Show (DMap k (By p))) => Show (PatchDMapWithReset k p)
src/Data/Patch/IntMap.hs+3−1
@@ -19,8 +19,10 @@ import Data.Patch.Class
19newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
19newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
20
20
21-- | Apply the insertions or deletions to a given 'IntMap'.
21-- | Apply the insertions or deletions to a given 'IntMap'.
22−instance Patch (PatchIntMap a) where
22+instance PatchHet (PatchIntMap a) where
23+ type PatchSource (PatchIntMap a) = IntMap a
23 type PatchTarget (PatchIntMap a) = IntMap a
24 type PatchTarget (PatchIntMap a) = IntMap a
25+instance Patch (PatchIntMap a) where
24 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
26 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
25 let removes = IntMap.filter isNothing p
27 let removes = IntMap.filter isNothing p
26 adds = IntMap.mapMaybe id p
28 adds = IntMap.mapMaybe id p
src/Data/Patch/Map.hs+3−1
@@ -18,8 +18,10 @@ newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
18 deriving (Show, Read, Eq, Ord)
18 deriving (Show, Read, Eq, Ord)
19
19
20-- | Apply the insertions or deletions to a given 'Map'.
20-- | Apply the insertions or deletions to a given 'Map'.
21−instance Ord k => Patch (PatchMap k v) where
21+instance Ord k => PatchHet (PatchMap k v) where
22+ type PatchSource (PatchMap k v) = Map k v
22 type PatchTarget (PatchMap k v) = Map k v
23 type PatchTarget (PatchMap k v) = Map k v
24+instance Ord k => Patch (PatchMap k v) where
23 {-# INLINABLE apply #-}
25 {-# INLINABLE apply #-}
24 apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
26 apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
25 where insertions = Map.mapMaybeWithKey (const id) p
27 where insertions = Map.mapMaybeWithKey (const id) p
src/Data/Patch/MapWithMove.hs+8−3
@@ -142,9 +142,15 @@ unsafePatchMapWithMove
142unsafePatchMapWithMove = PatchMapWithMove
142unsafePatchMapWithMove = PatchMapWithMove
143
143
144-- | Apply the insertions, deletions, and moves to a given 'Map'
144-- | Apply the insertions, deletions, and moves to a given 'Map'
145−instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) where
145+instance (Ord k, Patch p) => PatchHet (PatchMapWithMove k p) where
146+ type PatchSource (PatchMapWithMove k p) = Map k (PatchSource p)
146 type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
147 type PatchTarget (PatchMapWithMove k p) = Map k (PatchTarget p)
148+instance (Ord k, Patch p) => Patch (PatchMapWithMove k p) where
149+ -- TODO: return Nothing sometimes
147− apply (PatchMapWithMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
150+ -- Note: the strict application here is critical to ensuring that incremental
151+ -- merges don't hold onto all their prerequisite events forever; can we make
152+ -- this more robust?
153+ apply (PatchMapWithMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
148 where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
154 where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
149 From_Insert v -> Just v
155 From_Insert v -> Just v
150 From_Move k p -> applyAlways p <$> Map.lookup k old
156 From_Move k p -> applyAlways p <$> Map.lookup k old
@@ -252,7 +258,6 @@ data Fixup k v
252
258
253-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
259-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
254instance ( Ord k
260instance ( Ord k
255− , Monoid p
256 , DecidablyEmpty p
261 , DecidablyEmpty p
257 , Patch p
262 , Patch p
258 ) => Semigroup (PatchMapWithMove k p) where
263 ) => Semigroup (PatchMapWithMove k p) where
src/Data/Patch/Patchable.hsnew file+45
@@ -0,0 +1,45 @@
1+{-# LANGUAGE DeriveGeneric #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+{-# LANGUAGE TypeFamilies #-}
6+
7+-- The derived instances are undecidable in the case of a pathological instance like
8+-- instance Patch x where
9+-- type PatchTarget x = Patchable x
10+{-# LANGUAGE UndecidableInstances #-}
11+
12+module Data.Patch.Patchable where
13+
14+-- import Data.Aeson
15+import GHC.Generics
16+
17+import Data.Patch
18+
19+-- | Like SemiMap/PartialMap but for anything patchable
20+data Patchable p
21+ = Patchable_Patch p
22+ | Patchable_Complete (PatchTarget p)
23+ deriving (Generic)
24+
25+completePatchable :: Patchable p -> Maybe (PatchTarget p)
26+completePatchable = \case
27+ Patchable_Complete t -> Just t
28+ Patchable_Patch _ -> Nothing
29+
30+deriving instance (Eq p, Eq (PatchTarget p)) => Eq (Patchable p)
31+deriving instance (Ord p, Ord (PatchTarget p)) => Ord (Patchable p)
32+deriving instance (Show p, Show (PatchTarget p)) => Show (Patchable p)
33+deriving instance (Read p, Read (PatchTarget p)) => Read (Patchable p)
34+-- instance (ToJSON p, ToJSON (PatchTarget p)) => ToJSON (Patchable p)
35+-- instance (FromJSON p, FromJSON (PatchTarget p)) => FromJSON (Patchable p)
36+
37+instance (Monoid p, Patch p) => Monoid (Patchable p) where
38+ mempty = Patchable_Patch mempty
39+ mappend = (<>)
40+
41+instance (Semigroup p, Patch p) => Semigroup (Patchable p) where
42+ (<>) = curry $ \case
43+ (Patchable_Patch a, Patchable_Patch b) -> Patchable_Patch $ a <> b
44+ (Patchable_Patch a, Patchable_Complete b) -> Patchable_Complete $ applyAlways a b
45+ (Patchable_Complete a, _) -> Patchable_Complete a