on develop help sign in
Merge pull request #195 from reflex-frp/rmm-docallthethings2

Another batch of doc comments

commit53bfa668d7aa64460e8e3b3b5d1696aa6b91fb00 authorLuigy Leon <luichi.19@gmail.com> dateTue 22 Jan 2019, 18:31 (8 years ago) parentfcdfb5a4f96b76
7 files changed+182−43
src/Data/Map/Misc.hs+24−9 src/Reflex/FastWeak.hs+55−5 src/Reflex/Patch/DMap.hs+22−8 src/Reflex/Patch/DMapWithMove.hs+3−2 src/Reflex/Patch/IntMap.hs+19−1 src/Reflex/Patch/Map.hs+1−2 src/Reflex/Patch/MapWithMove.hs+58−16
src/Data/Map/Misc.hs+24−9
@@ -1,4 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
2+-- | Additional functions for manipulating 'Map's.
23module Data.Map.Misc
34 (
45 -- * Working with Maps
@@ -10,7 +11,6 @@ module Data.Map.Misc
1011 ) where
1112
1213import Data.Align
13−import Data.Either (isLeft)
1414import Data.Map (Map)
1515import qualified Data.Map as Map
1616import Data.Maybe
@@ -18,12 +18,21 @@ import Data.Set (Set)
1818import qualified Data.Set as Set
1919import Data.These
2020
21+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new@ respectively. @Just@ represents an association present in @new@ and @Nothing@
22+-- represents an association only present in @old@ but no longer present in @new@.
23+--
24+-- Similar to 'diffMap' but doesn't require 'Eq' on the values, thus can't tell if a value has changed or not.
2125diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
2226diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
2327 This _ -> Just Nothing
2428 These _ new -> Just $ Just new
2529 That new -> Just $ Just new
2630
31+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new respectively. @Just@ represents an association present in @new@ and either not
32+-- present in @old@ or where the value has changed. @Nothing@ represents an association only present in @old@ but no longer present in @new@.
33+--
34+-- See also 'diffMapNoEq' for a similar but weaker version which does not require 'Eq' on the values but thus can't indicated a value not changing between
35+-- @old@ and @new@ with @Nothing@.
2736diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
2837diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
2938 This _ -> Just Nothing
@@ -32,6 +41,9 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
3241 | otherwise -> Just $ Just new
3342 That new -> Just $ Just new
3443
44+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new map from the given input @'Map' k v@.
45+--
46+-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
3547applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
3648applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
3749 where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
@@ -39,16 +51,19 @@ applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
3951 Nothing -> Left ()
4052 Just r -> Right r
4153
54+-- |Split a @'Map' k (Either a b)@ into @Map k a@ and @Map k b@, equivalent to @'Map.mapEither' id@
4255mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
43−mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
44− where (ls, rs) = Map.partition isLeft m
45− fromLeft (Left l) = l
46− fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
47− fromRight (Right r) = r
48− fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
56+mapPartitionEithers = Map.mapEither id
4957
50−-- | Apply a map patch to a set
51−-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
58+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new @'Set' k@ from the given input set.
59+--
60+-- Equivalent to:
61+--
62+-- @
63+-- applyMapKeysSet patch ('Map.keysSet' m) == 'Map.keysSet' ('applyMap' patch m)
64+-- @
65+--
66+-- but avoids the intervening @Map@ and needs no values.
5267applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
5368applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
5469 where (insertions, deletions) = Map.partition isJust patch
src/Reflex/FastWeak.hs+55−5
@@ -5,6 +5,15 @@
55{-# LANGUAGE ForeignFunctionInterface #-}
66{-# LANGUAGE JavaScriptFFI #-}
77#endif
8+
9+-- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
10+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
11+--
12+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
13+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
14+--
15+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
16+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
817module Reflex.FastWeak
918 ( FastWeakTicket
1019 , FastWeak
@@ -35,29 +44,57 @@ import System.Mem.Weak
3544
3645
3746#ifdef GHCJS_FAST_WEAK
47+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
48+-- can be used to get the referred to value without fear of @Nothing,
49+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
50+--
51+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeakTicket@.
3852newtype FastWeakTicket a = FastWeakTicket JSVal
3953
54+-- | A reference to some value which can be garbage collected if there are only
55+-- weak references to the value left.
56+--
57+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
58+--
59+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using
60+-- 'getFastWeakTicket' if the value hasn't been collected yet.
61+--
62+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeak@.
4063newtype FastWeak a = FastWeak JSVal
4164
4265-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
4366data Val a = Val { unVal :: a }
4467
45−-- | Coerce a JSVal that represents the heap object of a value of type 'a' into a value of type 'a'
68+-- | Coerce a JSVal that represents the heap object of a value of type @a@ into a value of type @a@
4669unsafeFromRawJSVal :: JSVal -> a
4770unsafeFromRawJSVal v = unVal (unsafeCoerce v)
4871
72+-- | Coerce a heap object of type @a@ into a 'JSVal' which represents that object.
4973unsafeToRawJSVal :: a -> JSVal
5074unsafeToRawJSVal v = unsafeCoerce (Val v)
5175#else
76+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
77+-- can be used to get the referred to value without fear of @Nothing@,
78+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
5279data FastWeakTicket a = FastWeakTicket
5380 { _fastWeakTicket_val :: !a
5481 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
5582 }
5683
84+-- | A reference to some value which can be garbage collected if there are only weak references to the value left.
85+--
86+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
87+--
88+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket'
89+-- if the value hasn't been collected yet.
90+--
91+-- Synonymous with 'Weak'.
5792type FastWeak a = Weak a
5893#endif
5994
60−-- This needs to be in IO so we know that we've relinquished the ticket
95+-- | Return the @a@ kept alive by the given 'FastWeakTicket'.
96+--
97+-- This needs to be in IO so we know that we've relinquished the ticket.
6198getFastWeakTicketValue :: FastWeakTicket a -> IO a
6299#ifdef GHCJS_FAST_WEAK
63100getFastWeakTicketValue t = do
@@ -69,6 +106,8 @@ foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a
69106getFastWeakTicketValue = return . _fastWeakTicket_val
70107#endif
71108
109+-- | Get the value referred to by a 'FastWeak' if it hasn't yet been collected,
110+-- or @Nothing@ if it has been collected.
72111getFastWeakValue :: FastWeak a -> IO (Maybe a)
73112#ifdef GHCJS_FAST_WEAK
74113getFastWeakValue w = do
@@ -84,6 +123,9 @@ foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.v
84123getFastWeakValue = deRefWeak
85124#endif
86125
126+-- | Try to create a 'FastWeakTicket' for the given 'FastWeak' which will ensure the value referred
127+-- remains alive. Returns @Just@ if the value hasn't been collected
128+-- and a ticket can therefore be obtained, @Nothing@ if it's been collected.
87129getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88130#ifdef GHCJS_FAST_WEAK
89131getFastWeakTicket w = do
@@ -103,8 +145,11 @@ getFastWeakTicket w = do
103145 }
104146#endif
105147
106−-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the associated FastWeak to actually be weak
148+-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
149+-- which can be obtained with 'getFastWeakTicketValue'.
107150mkFastWeakTicket :: a -> IO (FastWeakTicket a)
151+-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
152+-- associated FastWeak to actually be weak
108153#ifdef GHCJS_FAST_WEAK
109154mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
110155
@@ -119,11 +164,16 @@ mkFastWeakTicket v = do
119164 }
120165#endif
121166
167+-- | Demote a 'FastWeakTicket'; which ensures the value is alive, to a 'FastWeak' which doesn't.
168+-- Note that unless the ticket for the same 'FastWeak' is held in some other way
169+-- the value might be collected immediately.
170+getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
122171-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
123172#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
173+foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak' :: FastWeakTicket a -> IO (FastWeak a)
174+{-# INLINE getFastWeakTicketWeak #-}
175+getFastWeakTicketWeak = getFastWeakTicketWeak'
125176#else
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127177getFastWeakTicketWeak = return . _fastWeakTicket_weak
128178#endif
129179
src/Reflex/Patch/DMap.hs+22−8
@@ -5,6 +5,7 @@
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE StandaloneDeriving #-}
77{-# LANGUAGE TypeFamilies #-}
8+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
89module Reflex.Patch.DMap where
910
1011import Reflex.Patch.Class
@@ -20,14 +21,16 @@ import qualified Data.Map as Map
2021import Data.Semigroup (Semigroup (..))
2122import Data.Some (Some)
2223
23−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
24−-- deleted.
24+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
25+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
26+-- while deletions are represented as @'ComposeMaybe' Nothing@.
2527newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
2628
2729deriving instance GCompare k => Semigroup (PatchDMap k v)
2830
2931deriving instance GCompare k => Monoid (PatchDMap k v)
3032
33+-- | Apply the insertions or deletions to a given 'DMap'.
3134instance GCompare k => Patch (PatchDMap k v) where
3235 type PatchTarget (PatchDMap k v) = DMap k v
3336 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,32 +40,43 @@ instance GCompare k => Patch (PatchDMap k v) where
3740 Nothing -> Just $ Constant ()
3841 Just _ -> Nothing
3942
43+-- | Map a function @v a -> v' a@ over any inserts/updates in the given
44+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
4045mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
4146mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
4247
43−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
48+-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
49+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
50+traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
4451traversePatchDMap f = traversePatchDMapWithKey $ const f
4552
53+-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
54+-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
4655traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
4756traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
4857
58+-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
59+-- @v a -> v'@ to weaken each value contained in the patch.
4960weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
5061weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
5162
52−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
63+-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
64+-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
65+patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
5366patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
5467
55−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
68+-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
69+const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
5670const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
57− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
71+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
5872 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
5973
74+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
6075const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
6176const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
6277 where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
6378 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
6479
65−-- | Get the values that will be deleted if the given patch is applied to the
66−-- given 'DMap'. Includes values that will be replaced.
80+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
6781getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
6882getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hs+3−2
@@ -104,6 +104,7 @@ validationErrorsForPatchDMapWithMove m =
104104 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
105105 unbalancedMove _ = Nothing
106106
107+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
107108instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
108109 PatchDMapWithMove a == PatchDMapWithMove b = a == b
109110
@@ -341,6 +342,7 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
341342 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
342343 }
343344
345+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
344346instance GCompare k => Patch (PatchDMapWithMove k v) where
345347 type PatchTarget (PatchDMapWithMove k v) = DMap k v
346348 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?
@@ -356,8 +358,7 @@ instance GCompare k => Patch (PatchDMapWithMove k v) where
356358 From_Delete -> Just $ Constant ()
357359 _ -> Nothing
358360
359−-- | Get the values that will be deleted or moved if the given patch is applied
360−-- to the given 'DMap'.
361+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
361362getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
362363getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
363364 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Reflex/Patch/IntMap.hs+19−1
@@ -4,6 +4,8 @@
44{-# LANGUAGE DeriveTraversable #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
66{-# LANGUAGE TypeFamilies #-}
7+-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
8+-- insert/update or delete of associations.
79module Reflex.Patch.IntMap where
810
911import Data.IntMap.Strict (IntMap)
@@ -12,8 +14,12 @@ import Data.Maybe
1214import Data.Semigroup
1315import Reflex.Patch.Class
1416
17+-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
18+-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
19+-- and @Nothing@ means delete.
1520newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
1621
22+-- | Apply the insertions or deletions to a given 'IntMap'.
1723instance Patch (PatchIntMap a) where
1824 type PatchTarget (PatchIntMap a) = IntMap a
1925 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
@@ -36,14 +42,26 @@ instance Semigroup (PatchIntMap v) where
3642 GT -> x
3743#endif
3844
39−traverseIntMapPatchWithKey :: Applicative t => (Int -> a -> t b) -> PatchIntMap a -> t (PatchIntMap b)
45+-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
46+-- (that is, all inserts/updates), producing a @PatchIntMap b@.
47+mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
48+mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
49+
50+-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
51+-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
52+traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
4053traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
4154
55+-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
4256patchIntMapNewElements :: PatchIntMap a -> [a]
4357patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
4458
59+-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
60+-- the inserts/updates in the given patch.
4561patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
4662patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
4763
64+-- | Subset the given @'IntMap' a@ to contain only the keys that would be
65+-- deleted by the given @'PatchIntMap' a@.
4866getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
4967getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Patch/Map.hs+1−2
@@ -18,8 +18,7 @@ import Data.Semigroup
1818newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
1919 deriving (Show, Read, Eq, Ord)
2020
21−-- | Applying a 'PatchMap' will update the 'Map' by performing the insertions
22−-- and deletions specified
21+-- | Apply the insertions or deletions to a given 'Map'.
2322instance Ord k => Patch (PatchMap k v) where
2423 type PatchTarget (PatchMap k v) = Map k v
2524 {-# INLINABLE apply #-}
src/Reflex/Patch/MapWithMove.hs+58−16
@@ -20,7 +20,7 @@ import Data.List
2020import Data.Map (Map)
2121import qualified Data.Map as Map
2222import Data.Maybe
23−import Data.Semigroup
23+import Data.Semigroup (Semigroup (..), (<>))
2424import qualified Data.Set as Set
2525import Data.These
2626import Data.Tuple
@@ -41,6 +41,18 @@ data NodeInfo k v = NodeInfo
4141 }
4242 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
4343
44+-- | Describe how a key's new value should be produced
45+data From k v
46+ = From_Insert v -- ^ Insert the given value here
47+ | From_Delete -- ^ Delete the existing value, if any, from here
48+ | From_Move !k -- ^ Move the value here from the given key
49+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
50+
51+-- | Describe where a key's old value will go. If this is 'Just', that means
52+-- the key's old value will be moved to the given other key; if it is 'Nothing',
53+-- that means it will be deleted.
54+type To = Maybe
55+
4456-- | Create a 'PatchMapWithMove', validating it
4557patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
4658patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
@@ -62,22 +74,51 @@ patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
6274unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
6375unPatchMapWithMove (PatchMapWithMove p) = p
6476
65−-- | Warning: when using this function, you must ensure that the invariants of
66−-- 'PatchMapWithMove' are preserved; they will not be checked.
67−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
68−unsafePatchMapWithMove = PatchMapWithMove
77+-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
78+insertMapKey :: k -> v -> PatchMapWithMove k v
79+insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
6980
70−-- | Describe how a key's new value should be produced
71−data From k v
72− = From_Insert v -- ^ Insert the given value here
73− | From_Delete -- ^ Delete the existing value, if any, from here
74− | From_Move !k -- ^ Move the value here from the given key
75− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
81+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
82+--
83+-- @
84+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
85+-- @
86+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
87+moveMapKey src dst
88+ | src == dst = mempty
89+ | otherwise =
90+ PatchMapWithMove $ Map.fromList
91+ [ (dst, NodeInfo (From_Move src) Nothing)
92+ , (src, NodeInfo From_Delete (Just dst))
93+ ]
7694
77−-- | Describe where a key's old value will go. If this is 'Just', that means
78−-- the key's old value will be moved to the given other key; if it is 'Nothing',
79−-- that means it will be deleted.
80−type To = Maybe
95+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
96+--
97+-- @
98+-- let aMay = Map.lookup a map
99+-- bMay = Map.lookup b map
100+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
101+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
102+-- . Map.delete a . Map.delete b $ map
103+-- @
104+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
105+swapMapKey src dst
106+ | src == dst = mempty
107+ | otherwise =
108+ PatchMapWithMove $ Map.fromList
109+ [ (dst, NodeInfo (From_Move src) (Just src))
110+ , (src, NodeInfo (From_Move dst) (Just dst))
111+ ]
112+
113+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
114+deleteMapKey :: k -> PatchMapWithMove k v
115+deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
116+
117+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
118+--
119+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
120+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
121+unsafePatchMapWithMove = PatchMapWithMove
81122
82123-- | Apply the insertions, deletions, and moves to a given 'Map'
83124instance Ord k => Patch (PatchMapWithMove k v) where
@@ -91,10 +132,11 @@ instance Ord k => Patch (PatchMapWithMove k v) where
91132 From_Delete -> Just ()
92133 _ -> Nothing
93134
94−-- | Returns all the new elements that will be added to the 'Map'
135+-- | Returns all the new elements that will be added to the 'Map'.
95136patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
96137patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
97138
139+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
98140patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
99141patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
100142 where f ni = case _nodeInfo_from ni of