2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE PatternGuards #-}
10{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE Rank2Types #-}
12{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE StandaloneDeriving #-}
14{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE UndecidableInstances #-}
16{-# LANGUAGE UndecidableSuperClasses #-}
18-- |Module containing @'PatchDMapWithPatchingMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
19-- move values between keys.
20module Data.Patch.DMapWithPatchingMove where
22import qualified Control.Category as Cat
23import qualified Control.Category.DecidablyEmpty as Cat
25import Data.Constraint.Extras (Has')
26import Data.Dependent.Map (DMap)
27import Data.Dependent.Sum (DSum (..))
28import qualified Data.Dependent.Map as DMap
29import Data.Functor.Constant (Constant (..))
30import Data.Functor.Misc
31 ( Const2 (..), Proxy3 (..)
35import Data.Functor.Const (Const (..))
36import Data.Functor.Product (Product (..))
37import Data.GADT.Compare (GEq (..), GCompare (..))
38import Data.GADT.Show (GRead, GShow, gshow)
39import qualified Data.Map as Map
41import Data.Monoid.DecidablyEmpty
42import Data.Semigroupoid as Cat
43#if !MIN_VERSION_base(4,11,0)
44import Data.Semigroup (Semigroup (..), (<>))
46import Data.Some (Some (Some), mkSome)
47import Data.Proxy (Proxy (..))
48import Data.These (These (..))
49import Data.Type.Equality ((:~:)(..))
51import Data.Patch.Class
52 ( Patch (..), PatchHet (..)
53 , PatchHet2 (..), PatchSource1, PatchTarget1
56import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove (..))
57import qualified Data.Patch.MapWithPatchingMove as MapWithPatchingMove
59-- | Like 'PatchMapWithPatchingMove', but for 'DMap'. Each key carries a 'NodeInfo'
60-- which describes how it will be changed by the patch and connects move sources
65-- * A key should not move to itself.
67-- * A move should always be represented with both the destination key (as a
68-- 'From_Move') and the source key (as a @'ComposeMaybe' ('Just'
70newtype PatchDMapWithPatchingMove k v = PatchDMapWithPatchingMove (DMap k (NodeInfo k v))
72--deriving instance ( GShow k
74-- , Has' Show k (PatchTarget1 p)
75-- ) => Show (PatchDMapWithPatchingMove k p)
76--deriving instance ( GRead k
78-- , Has' Read k (PatchTarget1 p)
79-- ) => Read (PatchDMapWithPatchingMove k p)
80--deriving instance ( GEq k
82-- , Has' Eq k (PatchTarget1 p)
83-- ) => Eq (PatchDMapWithPatchingMove k p)
84--deriving instance ( GCompare k
86-- , Has' Ord k (PatchTarget1 p)
87-- ) => Ord (PatchDMapWithPatchingMove k p)
89-- It won't let me derive for some reason
91 , Cat.DecidablyEmpty v
94 , PatchSource1 v ~ PatchTarget1 v
95 ) => DecidablyEmpty (PatchDMapWithPatchingMove k v) where
96 isEmpty (PatchDMapWithPatchingMove m) = DMap.null m
98-- | Structure which represents what changes apply to a particular key.
99-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
100-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
101-- key the current key is moving to if involved in a move.
102data NodeInfo k p a = NodeInfo
103 { _nodeInfo_from :: !(From k p a)
104 -- ^ Change applying to the current key, be it an insert, move, or delete.
105 , _nodeInfo_to :: !(To k)
106 -- ^ Where this key is moving to, if involved in a move. Should only be
107 -- @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
110--deriving instance ( Show (k a)
112-- , Show (PatchTarget1 p a)
113-- ) => Show (NodeInfo k p a)
114--deriving instance ( Read (k a)
116-- , Read (PatchTarget1 p a)
117-- ) => Read (NodeInfo k p a)
118--deriving instance ( Eq (k a)
120-- , Eq (PatchTarget1 p a)
121-- ) => Eq (NodeInfo k p a)
122--deriving instance ( Ord (k a)
124-- , Ord (PatchTarget1 p a)
125-- ) => Ord (NodeInfo k p a)
127-- | Structure describing a particular change to a key, be it inserting a new
128-- key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting
129-- a key (@From_Delete@), or moving a key (@From_Move@).
131-- This type isn't used directly as the from field patch, but is instead wrapped
132-- in an existential. However, it is nice to be able to reason about this in
133-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
134data From (k :: a -> *) (p :: a -> a -> *) :: a -> * where
135 -- | Insert a new or update an existing key with the given value @PatchTarget1
137 From_Insert :: PatchTarget1 p to -> From k p to
138 -- | Delete the existing key
139 From_Delete :: From k p to
140 -- | Move the value from the given key @k a@ to this key. The source key
141 -- should also have an entry in the patch giving the current key as
142 -- @_nodeInfo_to@, usually but not necessarily with @From_Delete@.
143 From_Move :: !(DSum k (Flip p to)) -> From k p to
145deriving instance ( Show (k a), GShow k
146 , Has' Show k (Flip p a)
147 , Show (PatchTarget1 p a)
148 ) => Show (From k p a)
149deriving instance ( Read (k a), GRead k
150 , Has' Read k (Flip p a)
151 , Read (PatchTarget1 p a)
152 ) => Read (From k p a)
153deriving instance ( GEq k
154 , Has' Eq k (Flip p a)
155 , Eq (PatchTarget1 p a)
157deriving instance ( GCompare k
158 , Has' Eq k (Flip p a) -- superclass bug
159 , Has' Ord k (Flip p a)
160 , Ord (PatchTarget1 p a)
161 ) => Ord (From k p a)
163newtype Flip p to from = Flip (p from to)
165instance Cat.Category p => Cat.Category (Flip (p :: k -> k -> *)) where
167 Flip y . Flip x = Flip $ x Cat.. y
169-- | The "to" part of a 'NodeInfo'. Rather than be built out of @From@ like @From@
170-- is, we store just the information necessary to compose a @To@ and @From@ like
171-- @oLocal@ composes two @From@s.
172data To (k :: a -> *) where
173 -- | Delete or leave in place
175 -- | Move the value from the given key @k a@ to this key. The target key
176 -- should also have an entry in the patch giving the current key in
177 -- @_nodeInfo_from@, usually but not necessarily with @To_Delete@.
178 To_Move :: !(Some k) -> To k
180deriving instance GShow k => Show (To k)
181deriving instance GRead k => Read (To k)
182deriving instance GEq k => Eq (To k)
183deriving instance GCompare k => Ord (To k)
185-- |Test whether a 'PatchDMapWithPatchingMove' satisfies its invariants.
186validPatchDMapWithPatchingMove
188 . (GCompare k, GShow k)
189 => DMap k (NodeInfo k v)
191validPatchDMapWithPatchingMove = not . null . validationErrorsForPatchDMapWithPatchingMove
193-- |Enumerate what reasons a 'PatchDMapWithPatchingMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
194validationErrorsForPatchDMapWithPatchingMove
196 . (GCompare k, GShow k)
197 => DMap k (NodeInfo k v)
199validationErrorsForPatchDMapWithPatchingMove m =
200 noSelfMoves <> movesBalanced
202 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
203 selfMove (dst :=> NodeInfo (From_Move (src :=> _)) _)
204 | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
205 selfMove (src :=> NodeInfo _ (To_Move (Some dst)))
206 | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
208 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
209 unbalancedMove (dst :=> NodeInfo (From_Move (src :=> _)) _) =
210 case DMap.lookup src m of
211 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
212 Just (NodeInfo _ (To_Move (Some dst'))) ->
213 if isNothing (dst' `geq` dst)
214 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
217 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
218 unbalancedMove (src :=> NodeInfo _ (To_Move (Some dst))) =
219 case DMap.lookup dst m of
220 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
221 Just (NodeInfo (From_Move (src' :=> _)) _) ->
222 if isNothing (src' `geq` src)
223 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
227 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
228 unbalancedMove _ = Nothing
231data ToFrom k p a = ToFrom (To k) (From k p a)
233-- | Helper data structure used for composing patches using the monoid instance.
236 | Fixup_Update (These (DSum k (From k p)) (To k))
238-- | Compose patches having the same effect as applying the patches in turn:
239-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
243 , Cat.DecidablyEmpty p
245 , PatchSource1 p ~ PatchTarget1 p
247 => Semigroup (PatchDMapWithPatchingMove k p) where
248 PatchDMapWithPatchingMove ma <> PatchDMapWithPatchingMove mb = PatchDMapWithPatchingMove m
250 connections :: [DSum k (ToFrom k p)]
251 connections = DMap.toList $ DMap.intersectionWithKey
252 (\_ a b -> ToFrom (_nodeInfo_to a) (_nodeInfo_from b))
255 h :: DSum k (ToFrom k p) -> [DSum k (Const (Fixup k p))]
256 h ((between :: k between) :=> ToFrom editAfter editBefore) = case (editAfter, editBefore) of
257 (To_Move (Some (toAfter :: k after)), From_Move ((fromBefore :: k before) :=> Flip p) :: From k p between) ->
260 [ toAfter :=> Const Fixup_Delete ]
262 [ toAfter :=> Const (Fixup_Update $ This $ between :=> editBefore)
263 , fromBefore :=> Const (Fixup_Update $ That editAfter)
265 (To_NonMove, From_Move (fromBefore :=> _)) ->
266 -- The item is destroyed in the second patch, so indicate that it is
267 -- destroyed in the source map
268 [fromBefore :=> Const (Fixup_Update $ That To_NonMove)]
269 (To_Move (Some toAfter), _) ->
270 [toAfter :=> Const (Fixup_Update $ This $ between :=> editBefore)]
273 mergeFixups _ (Const Fixup_Delete) (Const Fixup_Delete) = Const $ Fixup_Delete
274 mergeFixups _ (Const (Fixup_Update a)) (Const (Fixup_Update b))
275 | This x <- a, That y <- b
276 = Const $ Fixup_Update $ These x y
277 | That y <- a, This x <- b
278 = Const $ Fixup_Update $ These x y
279 mergeFixups _ _ _ = error "PatchDMapWithPatchingMove: incompatible fixups"
280 fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
281 combineNodeInfos _ nia nib = NodeInfo
282 { _nodeInfo_from = _nodeInfo_from nia
283 , _nodeInfo_to = _nodeInfo_to nib
285 applyFixup :: k a -> NodeInfo k p a -> Const (Fixup k p) a -> Maybe (NodeInfo k p a)
286 applyFixup _ ni (Const fixup) = case fixup of
287 Fixup_Delete -> Nothing
288 Fixup_Update u -> Just $ NodeInfo
289 { _nodeInfo_from = case _nodeInfo_from ni of
290 f@(From_Move ((between0 :: k between0) :=> Flip (p' :: p between0 a))) -> case getHere u of -- The `from` fixup comes from the "old" patch
291 Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
292 Just ((between1 :: k between1) :=> frm) -> case geq between0 between1 of
293 Nothing -> error "fixup joined-on key did not match"
294 Just Refl -> case frm of
295 From_Insert v -> From_Insert $ applyAlwaysHet2 p' v
296 From_Delete -> From_Delete
297 From_Move (oldKey :=> Flip (p :: p oldKey between1)) -> From_Move $ oldKey :=> Flip (p' `o` p :: p oldKey a)
298 _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
299 , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
301 m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
302 getHere :: These a b -> Maybe a
307 getThere :: These a b -> Maybe b
313-- | Compose patches having the same effect as applying the patches in turn:
314-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
317 , Cat.DecidablyEmpty p
319 , PatchSource1 p ~ PatchTarget1 p
320 ) => Monoid (PatchDMapWithPatchingMove k p) where
321 mempty = PatchDMapWithPatchingMove mempty
325mappendPatchDMapWithPatchingMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithPatchingMove k v -> PatchDMapWithPatchingMove k v -> PatchDMapWithPatchingMove k v
326PatchDMapWithPatchingMove dstAfter srcAfter `mappendPatchDMapWithPatchingMoveSlow` PatchDMapWithPatchingMove dstBefore srcBefore = PatchDMapWithPatchingMove dst src
328 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
329 removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
330 removeRedundantDst _ a = Just a
331 f :: forall a. k a -> From k v a -> Maybe (From k v a)
332 f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
333 From_Move fromKey -> getDstAction fromKey dstBefore
335 dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
336 getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
337 removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
338 removeRedundantSrc _ a = Just a
339 g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
340 g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
341 ComposeMaybe Nothing -> ComposeMaybe Nothing
342 ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
343 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
346-- | Make a @'PatchDMapWithPatchingMove' k v@ which has the effect of inserting or
347-- updating a value @PatchTarget1 p a@ to the given key @k a@, like
349insertDMapKey :: k a -> PatchTarget1 p a -> PatchDMapWithPatchingMove k p
351 PatchDMapWithPatchingMove . DMap.singleton k $ NodeInfo (From_Insert v) To_NonMove
353-- | Make a @'PatchDMapWithPatchingMove' k v@ which has the effect of moving the value
354-- from the first key @k a@ to the second key @k a@, equivalent to:
357-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
361 => k a -> k a -> PatchDMapWithPatchingMove k (Proxy3 v)
362moveDMapKey src dst = case src `geq` dst of
363 Nothing -> PatchDMapWithPatchingMove $ DMap.fromList
364 [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) To_NonMove
365 , src :=> NodeInfo From_Delete (To_Move $ Some dst)
367 Just _ -> PatchDMapWithPatchingMove DMap.empty
369-- | Make a @'PatchDMapWithPatchingMove' k v@ which has the effect of swapping two keys
370-- in the mapping, equivalent to:
373-- let aMay = DMap.lookup a dmap
374-- bMay = DMap.lookup b dmap
375-- in maybe id (DMap.insert a) (bMay <> aMay)
376-- . maybe id (DMap.insert b) (aMay <> bMay)
377-- . DMap.delete a . DMap.delete b $ dmap
379swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithPatchingMove k (Proxy3 v)
380swapDMapKey src dst = case src `geq` dst of
381 Nothing -> PatchDMapWithPatchingMove $ DMap.fromList
382 [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) (To_Move $ Some src)
383 , src :=> NodeInfo (From_Move (dst :=> Flip Proxy3)) (To_Move $ Some dst)
385 Just _ -> PatchDMapWithPatchingMove DMap.empty
387-- | Make a @'PatchDMapWithPatchingMove' k v@ which has the effect of deleting a key in
388-- the mapping, equivalent to 'DMap.delete'.
389deleteDMapKey :: k a -> PatchDMapWithPatchingMove k v
390deleteDMapKey k = PatchDMapWithPatchingMove $ DMap.singleton k $ NodeInfo From_Delete To_NonMove
393k1, k2 :: Const2 Int () ()
396p1, p2 :: PatchDMapWithPatchingMove (Const2 Int ()) Identity
397p1 = moveDMapKey k1 k2
398p2 = moveDMapKey k2 k1
401p12Slow = p1 `mappendPatchDMapWithPatchingMoveSlow` p2
402p21Slow = p2 `mappendPatchDMapWithPatchingMoveSlow` p1
404testPatchDMapWithPatchingMove = do
407 print $ p12 == deleteDMapKey k1
408 print $ p21 == deleteDMapKey k2
409 print $ p12Slow == deleteDMapKey k1
410 print $ p21Slow == deleteDMapKey k2
412dst (PatchDMapWithPatchingMove x _) = x
413src (PatchDMapWithPatchingMove _ x) = x
416-- | Extract the 'DMap' representing the patch changes from the
417-- 'PatchDMapWithPatchingMove'.
418unPatchDMapWithPatchingMove :: PatchDMapWithPatchingMove k v -> DMap k (NodeInfo k v)
419unPatchDMapWithPatchingMove (PatchDMapWithPatchingMove p) = p
421-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithPatchingMove',
422-- without checking any invariants.
424-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithPatchingMove' are preserved; they will not be checked.
425unsafePatchDMapWithPatchingMove :: DMap k (NodeInfo k v) -> PatchDMapWithPatchingMove k v
426unsafePatchDMapWithPatchingMove = PatchDMapWithPatchingMove
428-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithPatchingMove' while
429-- checking invariants. If the invariants are satisfied, @Right p@ is returned
430-- otherwise @Left errors@.
431patchDMapWithPatchingMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithPatchingMove k v)
432patchDMapWithPatchingMove dm =
433 case validationErrorsForPatchDMapWithPatchingMove dm of
434 [] -> Right $ unsafePatchDMapWithPatchingMove dm
437-- | Map a natural transform @v -> v'@ over the given patch, transforming
438-- @'PatchDMapWithPatchingMove' k v@ into @'PatchDMapWithPatchingMove' k v'@.
439mapPatchDMapWithPatchingMove
441 . (forall a. PatchTarget1 p a -> PatchTarget1 p' a)
442 -> (forall from to. p from to -> p' from to)
443 -> PatchDMapWithPatchingMove k p
444 -> PatchDMapWithPatchingMove k p'
445mapPatchDMapWithPatchingMove f g (PatchDMapWithPatchingMove m) =
446 PatchDMapWithPatchingMove $ DMap.map (\ni -> NodeInfo
447 { _nodeInfo_from = h $ _nodeInfo_from ni
448 , _nodeInfo_to = _nodeInfo_to ni
450 where h :: forall a. From k p a -> From k p' a
452 From_Insert v -> From_Insert $ f v
453 From_Delete -> From_Delete
454 From_Move (k :=> Flip p) -> From_Move $ k :=> Flip (g p)
456-- | Traverse an effectful function @forall a. PatchTarget1 p a -> m (v ' a)@
457-- over the given patch, transforming @'PatchDMapWithPatchingMove' k v@ into @m
458-- ('PatchDMapWithPatchingMove' k v')@.
459traversePatchDMapWithPatchingMove
462 => (forall a. PatchTarget1 p a -> m (PatchTarget1 p' a))
463 -> (forall from to. p from to -> m (p' from to))
464 -> PatchDMapWithPatchingMove k p
465 -> m (PatchDMapWithPatchingMove k p')
466traversePatchDMapWithPatchingMove f g = traversePatchDMapWithPatchingMoveWithKey
470-- | Map an effectful function @forall a. k a -> PatchTarget1 p a -> m (v ' a)@
471-- over the given patch, transforming @'PatchDMapWithPatchingMove' k v@ into @m
472-- ('PatchDMapWithPatchingMove' k v')@.
473traversePatchDMapWithPatchingMoveWithKey
476 => (forall a. k a -> PatchTarget1 p a -> m (PatchTarget1 p' a))
477 -> (forall from to. k from -> k to -> p from to -> m (p' from to))
478 -> PatchDMapWithPatchingMove k p
479 -> m (PatchDMapWithPatchingMove k p')
480traversePatchDMapWithPatchingMoveWithKey f g (PatchDMapWithPatchingMove m) =
481 PatchDMapWithPatchingMove <$> DMap.traverseWithKey (\k ni -> NodeInfo
482 <$> h k (_nodeInfo_from ni)
483 <*> pure (_nodeInfo_to ni)) m
484 where h :: forall a. k a -> From k p a -> m (From k p' a)
486 From_Insert v -> From_Insert <$> f k v
487 From_Delete -> pure From_Delete
488 From_Move (fromKey :=> Flip p) -> From_Move . (fromKey :=>) . Flip <$> g fromKey k p
490-- | Map a function which transforms @'From k PatchTarget1 p a@ into a @'From k
491-- PatchTarget1 p' a@ over a @'NodeInfo' k PatchTarget1 p a@.
493 :: (From k p a -> From k p' a)
497nodeInfoMapFrom f g ni = NodeInfo
498 { _nodeInfo_from = f $ _nodeInfo_from ni
499 , _nodeInfo_to = g $ _nodeInfo_to ni
502-- | Map an effectful function which transforms @'From k PatchTarget1 p a@ into
503-- a @f ('From k PatchTarget1 p' a)@ over a @'NodeInfo' k PatchTarget1 p a@.
506 => (From k p a -> f (From k p' a))
507 -> (To k -> f (To k))
509 -> f (NodeInfo k p' a)
510nodeInfoMapFromM f g ni = NodeInfo
511 <$> f (_nodeInfo_from ni)
512 <*> g (_nodeInfo_to ni)
514-- | Weaken a 'PatchDMapWithPatchingMove' to a 'PatchMapWithPatchingMove' by weakening the keys
515-- from @k a@ to @'Some' k@ and applying a given weakening function
516-- @PatchTarget1 p a -> v'@ to values.
517weakenPatchDMapWithPatchingMoveWith
519 . (forall a. PatchTarget1 p a -> PatchTarget p')
520 -> (forall from to. p from to -> p')
521 -> PatchDMapWithPatchingMove k p
522 -> PatchMapWithPatchingMove (Some k) p'
523weakenPatchDMapWithPatchingMoveWith f g (PatchDMapWithPatchingMove m) =
524 PatchMapWithPatchingMove $ weakenDMapWith h m
525 where h :: forall a. NodeInfo k p a -> MapWithPatchingMove.NodeInfo (Some k) p'
526 h ni = MapWithPatchingMove.NodeInfo
527 { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
528 From_Insert v -> MapWithPatchingMove.From_Insert $ f v
529 From_Delete -> MapWithPatchingMove.From_Delete
530 From_Move (k :=> Flip p) -> MapWithPatchingMove.From_Move (mkSome k) $ g p
531 , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
532 To_NonMove -> Nothing
533 To_Move (Some k) -> Just (mkSome k)
536-- | "Weaken" a @'PatchDMapWithPatchingMove' (Const2 k a) v@ to a @'PatchMapWithPatchingMove' k
537-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
538-- dependency in the typing and all points are already @a@, hence the function
539-- to map each value to @v'@ is not higher rank.
540patchDMapWithPatchingMoveToPatchMapWithPatchingMoveWith
542 . (PatchTarget1 p a -> PatchTarget p')
544 -> PatchDMapWithPatchingMove (Const2 k a) p
545 -> PatchMapWithPatchingMove k p'
546patchDMapWithPatchingMoveToPatchMapWithPatchingMoveWith f g (PatchDMapWithPatchingMove m) =
547 PatchMapWithPatchingMove $ dmapToMapWith h m
548 where h :: NodeInfo (Const2 k a) p a -> MapWithPatchingMove.NodeInfo k p'
549 h ni = MapWithPatchingMove.NodeInfo
550 { MapWithPatchingMove._nodeInfo_from = case _nodeInfo_from ni of
551 From_Insert v -> MapWithPatchingMove.From_Insert $ f v
552 From_Delete -> MapWithPatchingMove.From_Delete
553 From_Move (Const2 k :=> Flip p) -> MapWithPatchingMove.From_Move k $ g p
554 , MapWithPatchingMove._nodeInfo_to = case _nodeInfo_to ni of
555 To_NonMove -> Nothing
556 To_Move (Some (Const2 k)) -> Just k
559-- | "Strengthen" a @'PatchMapWithPatchingMove' k v@ into a @'PatchDMapWithPatchingMove
560-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
561-- dependently typed one but which always has a constant key type represented by
562-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
563-- a@. Completemented by 'patchDMapWithPatchingMoveToPatchMapWithPatchingMoveWith'
564const2PatchDMapWithPatchingMoveWith
567 -> PatchMapWithPatchingMove k (Proxy v)
568 -> PatchDMapWithPatchingMove (Const2 k a) (Proxy3 v')
569const2PatchDMapWithPatchingMoveWith f (PatchMapWithPatchingMove p) =
570 PatchDMapWithPatchingMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
571 where g :: (k, MapWithPatchingMove.NodeInfo k (Proxy v))
572 -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
573 g (k, ni) = Const2 k :=> NodeInfo
574 { _nodeInfo_from = case MapWithPatchingMove._nodeInfo_from ni of
575 MapWithPatchingMove.From_Insert v -> From_Insert $ f v
576 MapWithPatchingMove.From_Delete -> From_Delete
577 MapWithPatchingMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
578 , _nodeInfo_to = case MapWithPatchingMove._nodeInfo_to ni of
579 Nothing -> To_NonMove
580 Just toKey -> To_Move $ Some (Const2 toKey)
583-- | Apply the insertions, deletions, and moves to a given 'DMap'.
586 , PatchSource1 p ~ PatchTarget1 p
587 ) => PatchHet (PatchDMapWithPatchingMove k p) where
588 type PatchSource (PatchDMapWithPatchingMove k p) = DMap k (PatchSource1 p)
589 type PatchTarget (PatchDMapWithPatchingMove k p) = DMap k (PatchTarget1 p)
593 , PatchSource1 p ~ PatchTarget1 p
594 ) => Patch (PatchDMapWithPatchingMove k p) where
595 apply (PatchDMapWithPatchingMove m) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions)
596 -- TODO: return Nothing sometimes --Note: the strict application here is
597 -- critical to ensuring that incremental merges don't hold onto all their
598 -- prerequisite events forever; can we make this more robust?
599 where insertions = DMap.mapMaybeWithKey insertFunc m
600 insertFunc :: forall a. k a -> NodeInfo k p a -> Maybe (PatchTarget1 p a)
601 insertFunc _ ni = case _nodeInfo_from ni of
602 From_Insert v -> Just v
603 From_Move (k :=> Flip p) -> applyAlwaysHet2 p <$> DMap.lookup k old
604 From_Delete -> Nothing
605 deletions = DMap.mapMaybeWithKey deleteFunc m
606 deleteFunc :: forall a. k a -> NodeInfo k p a -> Maybe (Constant () a)
607 deleteFunc _ ni = case _nodeInfo_from ni of
608 From_Delete -> Just $ Constant ()
611-- | Get the values that will be replaced, deleted, or moved if the given patch
612-- is applied to the given 'DMap'.
615 , PatchSource1 p ~ PatchTarget1 p
617 => PatchDMapWithPatchingMove k p
618 -> DMap k (PatchSource1 p)
619 -> DMap k (Product (PatchTarget1 p) (Const (To k)))
620getDeletionsAndMoves (PatchDMapWithPatchingMove p) m = DMap.intersectionWithKey f m p
621 where f _ v ni = Pair v $ Const $ _nodeInfo_to ni
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list