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 @'PatchDMapWithMove' 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.DMapWithMove where
22import qualified Control.Category as Cat
23--import qualified Control.Category.DecidablyEmpty as Cat
24import Data.Constraint.Extras (Has')
25import Data.Dependent.Map (DMap)
26import Data.Dependent.Sum (DSum (..))
27import qualified Data.Dependent.Map as DMap
28import Data.Functor.Constant (Constant (..))
29import Data.Functor.Misc
30 ( Const2 (..), Proxy3 (..)
34import Data.Functor.Product (Product (..))
35import Data.GADT.Compare (GEq (..), GCompare (..))
36import Data.GADT.Show (GRead, GShow, gshow)
37import qualified Data.Map as Map
39import Data.Semigroup (Semigroup (..), (<>))
40import Data.Semigroupoid as Cat
41import Data.Some (Some(Some))
42import Data.Proxy (Proxy (..))
43import Data.These (These (..))
45import Data.Patch.Class
46 ( Patch (..), PatchHet (..)
47 , PatchHet2 (..), PatchSource1, PatchTarget1
50import Data.Patch.MapWithMove (PatchMapWithMove (..))
51import qualified Data.Patch.MapWithMove as MapWithMove
53-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo'
54-- which describes how it will be changed by the patch and connects move sources
59-- * A key should not move to itself.
61-- * A move should always be represented with both the destination key (as a
62-- 'From_Move') and the source key (as a @'ComposeMaybe' ('Just'
64newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
66--deriving instance ( GShow k
68-- , Has' Show k (PatchTarget1 p)
69-- ) => Show (PatchDMapWithMove k p)
70--deriving instance ( GRead k
72-- , Has' Read k (PatchTarget1 p)
73-- ) => Read (PatchDMapWithMove k p)
74--deriving instance ( GEq k
76-- , Has' Eq k (PatchTarget1 p)
77-- ) => Eq (PatchDMapWithMove k p)
78--deriving instance ( GCompare k
80-- , Has' Ord k (PatchTarget1 p)
81-- ) => Ord (PatchDMapWithMove k p)
83-- | Structure which represents what changes apply to a particular key.
84-- @_nodeInfo_from@ specifies what happens to this key, and in particular what
85-- other key the current key is moving from, while @_nodeInfo_to@ specifies what
86-- key the current key is moving to if involved in a move.
87data NodeInfo k p a = NodeInfo
88 { _nodeInfo_from :: !(From k p a)
89 -- ^ Change applying to the current key, be it an insert, move, or delete.
90 , _nodeInfo_to :: !(To k p a)
91 -- ^ Where this key is moving to, if involved in a move. Should only be
92 -- @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
95--deriving instance ( Show (k a)
97-- , Show (PatchTarget1 p a)
98-- ) => Show (NodeInfo k p a)
99--deriving instance ( Read (k a)
101-- , Read (PatchTarget1 p a)
102-- ) => Read (NodeInfo k p a)
103--deriving instance ( Eq (k a)
105-- , Eq (PatchTarget1 p a)
106-- ) => Eq (NodeInfo k p a)
107--deriving instance ( Ord (k a)
109-- , Ord (PatchTarget1 p a)
110-- ) => Ord (NodeInfo k p a)
112-- | Structure describing a particular change to a key, be it inserting a new
113-- key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting
114-- a key (@From_Delete@), or moving a key (@From_Move@).
116-- This type isn't used directly as the from field patch, but is instead wrapped
117-- in an existential. However, it is nice to be able to reason about this in
118-- isolation as it is itself a @Semigroupoid@ when the underlying patch is.
119data From (k :: a -> *) (p :: a -> a -> *) :: a -> * where
120 -- | Insert a new or update an existing key with the given value @PatchTarget1
122 From_Insert :: PatchTarget1 p to -> From k p to
123 -- | Delete the existing key
124 From_Delete :: From k p to
125 -- | Move the value from the given key @k a@ to this key. The source key
126 -- should also have an entry in the patch giving the current key as
127 -- @_nodeInfo_to@, usually but not necessarily with @From_Delete@.
128 From_Move :: !(DSum k (Flip p to)) -> From k p to
130deriving instance ( Show (k a), GShow k
131 , Has' Show k (Flip p a)
132 , Show (PatchTarget1 p a)
133 ) => Show (From k p a)
134deriving instance ( Read (k a), GRead k
135 , Has' Read k (Flip p a)
136 , Read (PatchTarget1 p a)
137 ) => Read (From k p a)
138deriving instance ( GEq k
139 , Has' Eq k (Flip p a)
140 , Eq (PatchTarget1 p a)
142deriving instance ( GCompare k
143 , Has' Eq k (Flip p a) -- superclass bug
144 , Has' Ord k (Flip p a)
145 , Ord (PatchTarget1 p a)
146 ) => Ord (From k p a)
148newtype Flip p to from = Flip (p from to)
150instance Cat.Category p => Cat.Category (Flip (p :: k -> k -> *)) where
152 Flip y . Flip x = Flip $ x Cat.. y
154-- | The "to" part of a 'NodeInfo'. Rather than be built out of @From@ like @From@
155-- is, we store just the information necessary to compose a @To@ and @From@ like
156-- @oLocal@ composes two @From@s.
157data To (k :: a -> *) (p :: a -> a -> *) :: a -> * where
158 -- | Delete or leave in place
159 To_NonMove :: To k p from
160 -- | Move the value from the given key @k a@ to this key. The target key
161 -- should also have an entry in the patch giving the current key in
162 -- @_nodeInfo_from@, usually but not necessarily with @To_Delete@.
163 To_Move :: !(DSum k (p from)) -> To k p from
165deriving instance ( Show (k a), GShow k
167 , Show (PatchTarget1 p a)
169deriving instance ( Read (k a), GRead k
171 , Read (PatchTarget1 p a)
173deriving instance ( GEq k
175 , Eq (PatchTarget1 p a)
177deriving instance ( GCompare k
178 , Has' Eq k (p a) -- superclass bug
180 , Ord (PatchTarget1 p a)
183-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
184validPatchDMapWithMove
186 . (GCompare k, GShow k)
187 => DMap k (NodeInfo k v)
189validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
191-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
192validationErrorsForPatchDMapWithMove
194 . (GCompare k, GShow k)
195 => DMap k (NodeInfo k v)
197validationErrorsForPatchDMapWithMove m =
198 noSelfMoves <> movesBalanced
200 noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
201 selfMove (dst :=> NodeInfo (From_Move (src :=> _)) _)
202 | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
203 selfMove (src :=> NodeInfo _ (To_Move (dst :=> _)))
204 | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
206 movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
207 unbalancedMove (dst :=> NodeInfo (From_Move (src :=> _)) _) =
208 case DMap.lookup src m of
209 Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
210 Just (NodeInfo _ (To_Move (dst' :=> _))) ->
211 if isNothing (dst' `geq` dst)
212 then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
215 Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
216 unbalancedMove (src :=> NodeInfo _ (To_Move (dst :=> _))) =
217 case DMap.lookup dst m of
218 Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
219 Just (NodeInfo (From_Move (src' :=> _)) _) ->
220 if isNothing (src' `geq` src)
221 then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
225 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
226 unbalancedMove _ = Nothing
228-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
229data Pair1 f g a = Pair1 (f a) (g a)
231-- |Helper data structure used for composing patches using the monoid instance.
234 | Fixup_Update (These (From k p a) (To k p a))
236-- | Compose patches having the same effect as applying the patches in turn:
237-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
240 -- , Cat.DecidablyEmpty p
242 , PatchSource1 p ~ PatchTarget1 p
243 ) => Semigroup (PatchDMapWithMove k p) where
244 PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
246 connections :: [DSum k (Pair1 (To k p) (From k p))]
247 connections = DMap.toList $ DMap.intersectionWithKey
248 (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b))
251 h :: DSum k (Pair1 (To k p) (From k p)) -> [DSum k (Fixup k p)]
252 h ((_ :: k between) :=> Pair1 editAfter editBefore) = case (editAfter, editBefore) of
253 (To_Move ((toAfter :: k after) :=> p1), From_Move ((fromBefore :: k before) :=> Flip p0)) ->
254 --case toAfter `geq` fromBefore of
255 -- Just Refl | Just Refl <- Cat.isId p0 ->
256 -- [ toAfter :=> Fixup_Delete ]
258 [ toAfter :=> Fixup_Update (This $ From_Move $ fromBefore :=> (Flip $ p1 `o` p0))
259 , fromBefore :=> Fixup_Update (That $ To_Move $ toAfter :=> (p1 `o` p0))
261 (To_NonMove, From_Move (fromBefore :=> _)) ->
262 -- The item is destroyed in the second patch, so indicate that it is
263 -- destroyed in the source map
264 [fromBefore :=> Fixup_Update (That To_NonMove)]
265 (To_Move (toAfter :=> p), From_Insert val) ->
266 [toAfter :=> Fixup_Update (This $ From_Insert $ applyAlwaysHet2 p val)]
267 (To_Move (toAfter :=> _), From_Delete) ->
268 [toAfter :=> Fixup_Update (This From_Delete)]
271 mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
272 mergeFixups _ (Fixup_Update a) (Fixup_Update b)
273 | This x <- a, That y <- b
274 = Fixup_Update $ These x y
275 | That y <- a, This x <- b
276 = Fixup_Update $ These x y
277 mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups"
278 fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
279 combineNodeInfos _ nia nib = NodeInfo
280 { _nodeInfo_from = _nodeInfo_from nia
281 , _nodeInfo_to = _nodeInfo_to nib
283 applyFixup _ ni = \case
284 Fixup_Delete -> Nothing
285 Fixup_Update u -> Just $ NodeInfo
286 { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
287 , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
289 m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
290 getHere :: These a b -> Maybe a
295 getThere :: These a b -> Maybe b
301-- | Compose patches having the same effect as applying the patches in turn:
302-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
305 -- , DecidablyEmpty p
307 , PatchSource1 p ~ PatchTarget1 p
308 ) => Monoid (PatchDMapWithMove k p) where
309 mempty = PatchDMapWithMove mempty
313mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
314PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
316 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
317 removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
318 removeRedundantDst _ a = Just a
319 f :: forall a. k a -> From k v a -> Maybe (From k v a)
320 f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
321 From_Move fromKey -> getDstAction fromKey dstBefore
323 dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
324 getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
325 removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
326 removeRedundantSrc _ a = Just a
327 g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
328 g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
329 ComposeMaybe Nothing -> ComposeMaybe Nothing
330 ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
331 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
334-- | Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or
335-- updating a value @PatchTarget1 p a@ to the given key @k a@, like
337insertDMapKey :: k a -> PatchTarget1 p a -> PatchDMapWithMove k p
339 PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) To_NonMove
341-- | Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value
342-- from the first key @k a@ to the second key @k a@, equivalent to:
345-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
349 => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
350moveDMapKey src dst = case src `geq` dst of
351 Nothing -> PatchDMapWithMove $ DMap.fromList
352 [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) To_NonMove
353 , src :=> NodeInfo From_Delete (To_Move $ dst :=> Proxy3)
355 Just _ -> PatchDMapWithMove DMap.empty
357-- | Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys
358-- in the mapping, equivalent to:
361-- let aMay = DMap.lookup a dmap
362-- bMay = DMap.lookup b dmap
363-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
364-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
365-- . DMap.delete a . DMap.delete b $ dmap
367swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k (Proxy3 v)
368swapDMapKey src dst = case src `geq` dst of
369 Nothing -> PatchDMapWithMove $ DMap.fromList
370 [ dst :=> NodeInfo (From_Move (src :=> Flip Proxy3)) (To_Move $ src :=> Proxy3)
371 , src :=> NodeInfo (From_Move (dst :=> Flip Proxy3)) (To_Move $ dst :=> Proxy3)
373 Just _ -> PatchDMapWithMove DMap.empty
375-- | Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in
376-- the mapping, equivalent to 'DMap.delete'.
377deleteDMapKey :: k a -> PatchDMapWithMove k v
378deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete To_NonMove
381k1, k2 :: Const2 Int () ()
384p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity
385p1 = moveDMapKey k1 k2
386p2 = moveDMapKey k2 k1
389p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2
390p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1
392testPatchDMapWithMove = do
395 print $ p12 == deleteDMapKey k1
396 print $ p21 == deleteDMapKey k2
397 print $ p12Slow == deleteDMapKey k1
398 print $ p21Slow == deleteDMapKey k2
400dst (PatchDMapWithMove x _) = x
401src (PatchDMapWithMove _ x) = x
404-- | Extract the 'DMap' representing the patch changes from the
405-- 'PatchDMapWithMove'.
406unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
407unPatchDMapWithMove (PatchDMapWithMove p) = p
409-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove',
410-- without checking any invariants.
412-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
413unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
414unsafePatchDMapWithMove = PatchDMapWithMove
416-- | Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while
417-- checking invariants. If the invariants are satisfied, @Right p@ is returned
418-- otherwise @Left errors@.
419patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
420patchDMapWithMove dm =
421 case validationErrorsForPatchDMapWithMove dm of
422 [] -> Right $ unsafePatchDMapWithMove dm
425-- | Map a natural transform @v -> v'@ over the given patch, transforming
426-- @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
429 . (forall a. PatchTarget1 p a -> PatchTarget1 p' a)
430 -> (forall from to. p from to -> p' from to)
431 -> PatchDMapWithMove k p
432 -> PatchDMapWithMove k p'
433mapPatchDMapWithMove f g (PatchDMapWithMove m) =
434 PatchDMapWithMove $ DMap.map (\ni -> NodeInfo
435 { _nodeInfo_from = h $ _nodeInfo_from ni
436 , _nodeInfo_to = j $ _nodeInfo_to ni
438 where h :: forall a. From k p a -> From k p' a
440 From_Insert v -> From_Insert $ f v
441 From_Delete -> From_Delete
442 From_Move (k :=> Flip p) -> From_Move $ k :=> Flip (g p)
443 j :: forall a. To k p a -> To k p' a
445 To_NonMove -> To_NonMove
446 To_Move (k :=> p) -> To_Move $ k :=> g p
448-- | Traverse an effectful function @forall a. PatchTarget1 p a -> m (v ' a)@
449-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
450-- ('PatchDMapWithMove' k v')@.
451traversePatchDMapWithMove
454 => (forall a. PatchTarget1 p a -> m (PatchTarget1 p' a))
455 -> (forall from to. p from to -> m (p' from to))
456 -> PatchDMapWithMove k p
457 -> m (PatchDMapWithMove k p')
458traversePatchDMapWithMove f g = traversePatchDMapWithMoveWithKey
462-- | Map an effectful function @forall a. k a -> PatchTarget1 p a -> m (v ' a)@
463-- over the given patch, transforming @'PatchDMapWithMove' k v@ into @m
464-- ('PatchDMapWithMove' k v')@.
465traversePatchDMapWithMoveWithKey
468 => (forall a. k a -> PatchTarget1 p a -> m (PatchTarget1 p' a))
469 -> (forall from to. k from -> k to -> p from to -> m (p' from to))
470 -> PatchDMapWithMove k p
471 -> m (PatchDMapWithMove k p')
472traversePatchDMapWithMoveWithKey f g (PatchDMapWithMove m) =
473 PatchDMapWithMove <$> DMap.traverseWithKey (\k ni -> NodeInfo
474 <$> h k (_nodeInfo_from ni)
475 <*> j k (_nodeInfo_to ni)) m
476 where h :: forall a. k a -> From k p a -> m (From k p' a)
478 From_Insert v -> From_Insert <$> f k v
479 From_Delete -> pure From_Delete
480 From_Move (fromKey :=> Flip p) -> From_Move . (fromKey :=>) . Flip <$> g fromKey k p
481 j :: forall a. k a -> To k p a -> m (To k p' a)
483 To_NonMove -> pure To_NonMove
484 To_Move (toKey :=> p) -> To_Move . (toKey :=>) <$> g k toKey p
486-- | Map a function which transforms @'From k PatchTarget1 p a@ into a @'From k
487-- PatchTarget1 p' a@ over a @'NodeInfo' k PatchTarget1 p a@.
489 :: (From k p a -> From k p' a)
490 -> (To k p a -> To k p' a)
493nodeInfoMapFrom f g ni = NodeInfo
494 { _nodeInfo_from = f $ _nodeInfo_from ni
495 , _nodeInfo_to = g $ _nodeInfo_to ni
498-- | Map an effectful function which transforms @'From k PatchTarget1 p a@ into
499-- a @f ('From k PatchTarget1 p' a)@ over a @'NodeInfo' k PatchTarget1 p a@.
502 => (From k p a -> f (From k p' a))
503 -> (To k p a -> f (To k p' a))
505 -> f (NodeInfo k p' a)
506nodeInfoMapFromM f g ni = NodeInfo
507 <$> f (_nodeInfo_from ni)
508 <*> g (_nodeInfo_to ni)
510-- | Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys
511-- from @k a@ to @'Some' k@ and applying a given weakening function
512-- @PatchTarget1 p a -> v'@ to values.
513weakenPatchDMapWithMoveWith
515 . (forall a. PatchTarget1 p a -> PatchTarget p')
516 -> (forall from to. p from to -> p')
517 -> PatchDMapWithMove k p
518 -> PatchMapWithMove (Some k) p'
519weakenPatchDMapWithMoveWith f g (PatchDMapWithMove m) =
520 PatchMapWithMove $ weakenDMapWith h m
521 where h :: forall a. NodeInfo k p a -> MapWithMove.NodeInfo (Some k) p'
522 h ni = MapWithMove.NodeInfo
523 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
524 From_Insert v -> MapWithMove.From_Insert $ f v
525 From_Delete -> MapWithMove.From_Delete
526 From_Move (k :=> Flip p) -> MapWithMove.From_Move (Some k) $ g p
527 , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
528 To_NonMove -> MapWithMove.To_NonMove
529 To_Move (k :=> p) -> MapWithMove.To_Move (Some k) $ g p
532-- | "Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k
533-- v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
534-- dependency in the typing and all points are already @a@, hence the function
535-- to map each value to @v'@ is not higher rank.
536patchDMapWithMoveToPatchMapWithMoveWith
538 . (PatchTarget1 p a -> PatchTarget p')
540 -> PatchDMapWithMove (Const2 k a) p
541 -> PatchMapWithMove k p'
542patchDMapWithMoveToPatchMapWithMoveWith f g (PatchDMapWithMove m) =
543 PatchMapWithMove $ dmapToMapWith h m
544 where h :: NodeInfo (Const2 k a) p a -> MapWithMove.NodeInfo k p'
545 h ni = MapWithMove.NodeInfo
546 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
547 From_Insert v -> MapWithMove.From_Insert $ f v
548 From_Delete -> MapWithMove.From_Delete
549 From_Move (Const2 k :=> Flip p) -> MapWithMove.From_Move k $ g p
550 , MapWithMove._nodeInfo_to = case _nodeInfo_to ni of
551 To_NonMove -> MapWithMove.To_NonMove
552 To_Move (Const2 k :=> p) -> MapWithMove.To_Move k $ g p
555-- | "Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove
556-- ('Const2' k a)@; that is, turn a non-dependently-typed patch into a
557-- dependently typed one but which always has a constant key type represented by
558-- 'Const2'. Apply the given function to each @v@ to produce a @PatchTarget1 p'
559-- a@. Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
560const2PatchDMapWithMoveWith
563 -> PatchMapWithMove k (Proxy v)
564 -> PatchDMapWithMove (Const2 k a) (Proxy3 v')
565const2PatchDMapWithMoveWith f (PatchMapWithMove p) =
566 PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
567 where g :: (k, MapWithMove.NodeInfo k (Proxy v))
568 -> DSum (Const2 k a) (NodeInfo (Const2 k a) (Proxy3 v'))
569 g (k, ni) = Const2 k :=> NodeInfo
570 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
571 MapWithMove.From_Insert v -> From_Insert $ f v
572 MapWithMove.From_Delete -> From_Delete
573 MapWithMove.From_Move fromKey Proxy -> From_Move $ Const2 fromKey :=> Flip Proxy3
574 , _nodeInfo_to = case MapWithMove._nodeInfo_to ni of
575 MapWithMove.To_NonMove -> To_NonMove
576 MapWithMove.To_Move toKey Proxy -> To_Move $ Const2 toKey :=> Proxy3
579-- | Apply the insertions, deletions, and moves to a given 'DMap'.
582 , PatchSource1 p ~ PatchTarget1 p
583 ) => PatchHet (PatchDMapWithMove k p) where
584 type PatchSource (PatchDMapWithMove k p) = DMap k (PatchSource1 p)
585 type PatchTarget (PatchDMapWithMove k p) = DMap k (PatchTarget1 p)
589 , PatchSource1 p ~ PatchTarget1 p
590 ) => Patch (PatchDMapWithMove k p) where
591 apply (PatchDMapWithMove m) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions)
592 -- TODO: return Nothing sometimes --Note: the strict application here is
593 -- critical to ensuring that incremental merges don't hold onto all their
594 -- prerequisite events forever; can we make this more robust?
595 where insertions = DMap.mapMaybeWithKey insertFunc m
596 insertFunc :: forall a. k a -> NodeInfo k p a -> Maybe (PatchTarget1 p a)
597 insertFunc _ ni = case _nodeInfo_from ni of
598 From_Insert v -> Just v
599 From_Move (k :=> Flip p) -> applyAlwaysHet2 p <$> DMap.lookup k old
600 From_Delete -> Nothing
601 deletions = DMap.mapMaybeWithKey deleteFunc m
602 deleteFunc :: forall a. k a -> NodeInfo k p a -> Maybe (Constant () a)
603 deleteFunc _ ni = case _nodeInfo_from ni of
604 From_Delete -> Just $ Constant ()
607-- | Get the values that will be replaced, deleted, or moved if the given patch
608-- is applied to the given 'DMap'.
611 , PatchSource1 p ~ PatchTarget1 p
613 => PatchDMapWithMove k p
614 -> DMap k (PatchSource1 p)
615 -> DMap k (Product (PatchTarget1 p) (To k p))
616getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
617 where f _ v ni = Pair v $ _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