on develop help sign in
Merge branch 'develop' into triggereventt-improvements
70f445aWill Fancher
reflex.cabal+14−1
@@ -34,7 +34,12 @@ flag expose-all-unfoldings
34 manual: True
34 manual: True
35
35
36flag debug-trace-events
36flag debug-trace-events
37− description: add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
37+ description: Add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
38+ default: False
39+ manual: True
40+
41+flag experimental-dependent-sum-instances
42+ description: Use experimental incoherent instances for certain forked dependent-sum typeclasses
38 default: False
43 default: False
39 manual: True
44 manual: True
40
45
@@ -75,6 +80,11 @@ library
75 Reflex.FunctorMaybe,
80 Reflex.FunctorMaybe,
76 Reflex.Host.Class,
81 Reflex.Host.Class,
77 Reflex.Patch,
82 Reflex.Patch,
83+ Reflex.Patch.Class,
84+ Reflex.Patch.DMap,
85+ Reflex.Patch.DMapWithMove,
86+ Reflex.Patch.Map,
87+ Reflex.Patch.MapWithMove,
78 Reflex.TriggerEvent.Base,
88 Reflex.TriggerEvent.Base,
79 Reflex.TriggerEvent.Class,
89 Reflex.TriggerEvent.Class,
80 Reflex.PerformEvent.Base,
90 Reflex.PerformEvent.Base,
@@ -120,6 +130,9 @@ library
120 if flag(expose-all-unfoldings)
130 if flag(expose-all-unfoldings)
121 ghc-options: -fexpose-all-unfoldings
131 ghc-options: -fexpose-all-unfoldings
122
132
133+ if flag(experimental-dependent-sum-instances)
134+ ghc-options: -DEXPERIMENTAL_DEPENDENT_SUM_INSTANCES
135+
123test-suite semantics
136test-suite semantics
124 type: exitcode-stdio-1.0
137 type: exitcode-stdio-1.0
125 main-is: semantics.hs
138 main-is: semantics.hs
src/Data/Functor/Misc.hs+104−2
@@ -1,5 +1,6 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveFunctor #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
@@ -19,6 +20,7 @@
19module Data.Functor.Misc
20module Data.Functor.Misc
20 ( -- * Const2
21 ( -- * Const2
21 Const2 (..)
22 Const2 (..)
23+ , unConst2
22 , dmapToMap
24 , dmapToMap
23 , dmapToMapWith
25 , dmapToMapWith
24 , mapToDMap
26 , mapToDMap
@@ -40,6 +42,7 @@ module Data.Functor.Misc
40 , unwrapDMap
42 , unwrapDMap
41 , unwrapDMapMaybe
43 , unwrapDMapMaybe
42 , extractFunctorDMap
44 , extractFunctorDMap
45+ , ComposeMaybe (..)
43 ) where
46 ) where
44
47
45import Control.Applicative (Applicative, (<$>))
48import Control.Applicative (Applicative, (<$>))
@@ -66,25 +69,57 @@ data Const2 :: * -> x -> x -> * where
66 Const2 :: k -> Const2 k v v
69 Const2 :: k -> Const2 k v v
67 deriving (Typeable)
70 deriving (Typeable)
68
71
72+unConst2 :: Const2 k v v' -> k
73+unConst2 (Const2 k) = k
74+
75+deriving instance Eq k => Eq (Const2 k v v')
76+deriving instance Ord k => Ord (Const2 k v v')
69−deriving instance Show k => Show (Const2 k v v)
77+deriving instance Show k => Show (Const2 k v v')
78+deriving instance Read k => Read (Const2 k v v)
70
79
71instance Show k => GShow (Const2 k v) where
80instance Show k => GShow (Const2 k v) where
81+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
82+ gshowToShow (Const2 _) = id
83+#else
72 gshowsPrec n x@(Const2 _) = showsPrec n x
84 gshowsPrec n x@(Const2 _) = showsPrec n x
85+#endif
73
86
74instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
87instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
88+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
89+ showTagToShow (Const2 _) _ = id
90+#else
75 showTaggedPrec (Const2 _) = showsPrec
91 showTaggedPrec (Const2 _) = showsPrec
92+#endif
76
93
77instance Eq k => GEq (Const2 k v) where
94instance Eq k => GEq (Const2 k v) where
95+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
96+ geqToEq _ = id
97+ geqUnify (Const2 _) (Const2 _) _ = id
98+#else
78 geq (Const2 a) (Const2 b) =
99 geq (Const2 a) (Const2 b) =
79 if a == b
100 if a == b
80 then Just Refl
101 then Just Refl
81 else Nothing
102 else Nothing
103+#endif
82
104
83instance Ord k => GCompare (Const2 k v) where
105instance Ord k => GCompare (Const2 k v) where
106+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
107+ gcompareToOrd (Const2 _) = id
108+ gcompare (Const2 a) (Const2 b) = strengthenOrdering $ compare a b
109+#else
84 gcompare (Const2 a) (Const2 b) = case compare a b of
110 gcompare (Const2 a) (Const2 b) = case compare a b of
85 LT -> GLT
111 LT -> GLT
86 EQ -> GEQ
112 EQ -> GEQ
87 GT -> GGT
113 GT -> GGT
114+#endif
115+
116+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
117+instance {-# INCOHERENT #-} (Eq k, Eq v) => EqTag (Const2 k v) Identity where
118+ eqTagToEq (Const2 _) _ = id
119+
120+instance {-# INCOHERENT #-} Eq k => EqTag (Const2 k v) (Const2 k v) where
121+ eqTagToEq (Const2 _) _ = id
122+#endif
88
123
89-- | Convert a 'DMap' to a regular 'Map'
124-- | Convert a 'DMap' to a regular 'Map'
90dmapToMap :: DMap (Const2 k v) Identity -> Map k v
125dmapToMap :: DMap (Const2 k v) Identity -> Map k v
@@ -115,10 +150,23 @@ weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f
115data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
150data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
116 WrapArg :: f a -> WrapArg g f (g a)
151 WrapArg :: f a -> WrapArg g f (g a)
117
152
153+deriving instance Eq (f a) => Eq (WrapArg g f (g' a))
154+deriving instance Ord (f a) => Ord (WrapArg g f (g' a))
155+deriving instance Show (f a) => Show (WrapArg g f (g' a))
156+deriving instance Read (f a) => Read (WrapArg g f (g a))
157+
118instance GEq f => GEq (WrapArg g f) where
158instance GEq f => GEq (WrapArg g f) where
159+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
160+ geqToEq (WrapArg f) = geqToEq f
161+ geqUnify (WrapArg a) (WrapArg b) = geqUnify a b
162+#else
119 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
163 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
164+#endif
120
165
121instance GCompare f => GCompare (WrapArg g f) where
166instance GCompare f => GCompare (WrapArg g f) where
167+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
168+ gcompareToOrd (WrapArg f) = gcompareToOrd f
169+#endif
122 gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
170 gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
123 GLT -> GLT
171 GLT -> GLT
124 GEQ -> GEQ
172 GEQ -> GEQ
@@ -164,14 +212,40 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
164data EitherTag l r a where
212data EitherTag l r a where
165 LeftTag :: EitherTag l r l
213 LeftTag :: EitherTag l r l
166 RightTag :: EitherTag l r r
214 RightTag :: EitherTag l r r
215+ deriving (Typeable)
216+
217+deriving instance Show (EitherTag l r a)
218+deriving instance Eq (EitherTag l r a)
219+deriving instance Ord (EitherTag l r a)
220+
221+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
222+instance {-# INCOHERENT #-} (Show (f l), Show (f r)) => ShowTag (EitherTag l r) f where
223+ showTagToShow e _ = case e of
224+ LeftTag -> id
225+ RightTag -> id
226+
227+instance {-# INCOHERENT #-} (Eq (f l), Eq (f r)) => EqTag (EitherTag l r) f where
228+ eqTagToEq e _ = case e of
229+ LeftTag -> id
230+ RightTag -> id
231+#endif
167
232
168instance GEq (EitherTag l r) where
233instance GEq (EitherTag l r) where
234+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
235+ geqToEq _ = id
236+ geqUnify LeftTag LeftTag _ same = same
237+ geqUnify RightTag RightTag _ same = same
238+ geqUnify _ _ unknown _ = unknown
239+#endif
169 geq a b = case (a, b) of
240 geq a b = case (a, b) of
170 (LeftTag, LeftTag) -> Just Refl
241 (LeftTag, LeftTag) -> Just Refl
171 (RightTag, RightTag) -> Just Refl
242 (RightTag, RightTag) -> Just Refl
172 _ -> Nothing
243 _ -> Nothing
173
244
174instance GCompare (EitherTag l r) where
245instance GCompare (EitherTag l r) where
246+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
247+ gcompareToOrd _ = id
248+#endif
175 gcompare a b = case (a, b) of
249 gcompare a b = case (a, b) of
176 (LeftTag, LeftTag) -> GEQ
250 (LeftTag, LeftTag) -> GEQ
177 (LeftTag, RightTag) -> GLT
251 (LeftTag, RightTag) -> GLT
@@ -179,14 +253,24 @@ instance GCompare (EitherTag l r) where
179 (RightTag, RightTag) -> GEQ
253 (RightTag, RightTag) -> GEQ
180
254
181instance GShow (EitherTag l r) where
255instance GShow (EitherTag l r) where
256+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
257+ gshowToShow _ = id
258+#else
182 gshowsPrec _ a = case a of
259 gshowsPrec _ a = case a of
183 LeftTag -> showString "LeftTag"
260 LeftTag -> showString "LeftTag"
184 RightTag -> showString "RightTag"
261 RightTag -> showString "RightTag"
262+#endif
185
263
186instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
264instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
265+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
266+ showTagToShow t _ = case t of
267+ LeftTag -> id
268+ RightTag -> id
269+#else
187 showTaggedPrec t n (Identity a) = case t of
270 showTaggedPrec t n (Identity a) = case t of
188 LeftTag -> showsPrec n a
271 LeftTag -> showsPrec n a
189 RightTag -> showsPrec n a
272 RightTag -> showsPrec n a
273+#endif
190
274
191-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
275-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
192eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
276eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
@@ -200,12 +284,30 @@ dsumToEither = \case
200 (LeftTag :=> Identity a) -> Left a
284 (LeftTag :=> Identity a) -> Left a
201 (RightTag :=> Identity b) -> Right b
285 (RightTag :=> Identity b) -> Right b
202
286
287+--------------------------------------------------------------------------------
288+-- ComposeMaybe
289+--------------------------------------------------------------------------------
290+
291+-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
292+-- make the 'f' parameter have a nominal type role. We need f to be
293+-- representational so that we can use safe 'coerce'.
294+newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
295+
296+deriving instance Functor f => Functor (ComposeMaybe f)
297+
298+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
299+instance {-# INCOHERENT #-} EqTag f g => EqTag f (ComposeMaybe g) where
300+ eqTagToEq f _ = eqTagToEq f (Proxy :: Proxy g)
301+
302+instance {-# INCOHERENT #-} GShow k => ShowTag k (ComposeMaybe k) where
303+ showTagToShow k _ r = gshowToShow k (showTagToShow k (Proxy :: Proxy k) r)
304+#endif
203--------------------------------------------------------------------------------
305--------------------------------------------------------------------------------
204-- Deprecated functions
306-- Deprecated functions
205--------------------------------------------------------------------------------
307--------------------------------------------------------------------------------
206
308
207{-# INLINE sequenceDmap #-}
309{-# INLINE sequenceDmap #-}
208−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
310+{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ -> fmap Identity)' instead" #-}
209-- | Run the actions contained in the 'DMap'
311-- | Run the actions contained in the 'DMap'
210sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
312sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
211sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
313sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
src/Reflex/Class.hs+11−3
@@ -80,6 +80,8 @@ module Reflex.Class
80 , mapAccumMaybe_
80 , mapAccumMaybe_
81 , mapAccumMaybeM_
81 , mapAccumMaybeM_
82 , zipListWithEvent
82 , zipListWithEvent
83+ , numberOccurrences
84+ , numberOccurrencesFrom
83 , (<@>)
85 , (<@>)
84 , (<@)
86 , (<@)
85 , headE
87 , headE
@@ -918,7 +920,13 @@ zipListWithEvent f l e = do
918 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
920 _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
919 mapAccumMaybe_ f' l e
921 mapAccumMaybe_ f' l e
920
922
921−infixl 4 <@>
923+-- | Assign a number to each occurence of the given 'Event', starting from 0
924+numberOccurrences :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Event t (b, a))
925+numberOccurrences = numberOccurrencesFrom 0
926+
927+-- | Assign a number to each occurence of the given 'Event'
928+numberOccurrencesFrom :: (Reflex t, MonadHold t m, MonadFix m, Num b) => b -> Event t a -> m (Event t (b, a))
929+numberOccurrencesFrom = mapAccum_ (\n a -> (n + 1, (n, a)))
922
930
923-- | This is used to sample the value of a 'Behavior' using an 'Event'.
931-- | This is used to sample the value of a 'Behavior' using an 'Event'.
924--
932--
@@ -956,8 +964,7 @@ infixl 4 <@>
956(<@>) b = push $ \x -> do
964(<@>) b = push $ \x -> do
957 f <- sample b
965 f <- sample b
958 return . Just . f $ x
966 return . Just . f $ x
959−
960−infixl 4 <@
967+infixl 4 <@>
961
968
962-- | An version of '<@>' that does not use the value of the 'Event'.
969-- | An version of '<@>' that does not use the value of the 'Event'.
963--
970--
@@ -987,6 +994,7 @@ infixl 4 <@
987-- to act as a trigger.
994-- to act as a trigger.
988(<@) :: (Reflex t) => Behavior t b -> Event t a -> Event t b
995(<@) :: (Reflex t) => Behavior t b -> Event t a -> Event t b
989(<@) = tag
996(<@) = tag
997+infixl 4 <@
990
998
991-- | A 'Monad' that supports adjustment over time. After an action has been
999-- | A 'Monad' that supports adjustment over time. After an action has been
992-- run, if the given events fire, it will adjust itself so that its net effect
1000-- run, if the given events fire, it will adjust itself so that its net effect
src/Reflex/Dynamic.hs+13
@@ -9,6 +9,7 @@
9{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE StandaloneDeriving #-}
12{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeFamilies #-}
13{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE TypeOperators #-}
14{-# LANGUAGE UndecidableInstances #-}
15{-# LANGUAGE UndecidableInstances #-}
@@ -333,12 +334,21 @@ data FHList f l where
333 FHCons :: f e -> FHList f l -> FHList f (e ': l)
334 FHCons :: f e -> FHList f l -> FHList f (e ': l)
334
335
335instance GEq (HListPtr l) where
336instance GEq (HListPtr l) where
337+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
338+ geqToEq _ = id
339+ geqUnify HHeadPtr HHeadPtr _ same = same
340+ geqUnify (HTailPtr a) (HTailPtr b) different same = geqUnify a b different same
341+ geqUnify _ _ different _ = different
342+#endif
336 HHeadPtr `geq` HHeadPtr = Just Refl
343 HHeadPtr `geq` HHeadPtr = Just Refl
337 HHeadPtr `geq` HTailPtr _ = Nothing
344 HHeadPtr `geq` HTailPtr _ = Nothing
338 HTailPtr _ `geq` HHeadPtr = Nothing
345 HTailPtr _ `geq` HHeadPtr = Nothing
339 HTailPtr a `geq` HTailPtr b = a `geq` b
346 HTailPtr a `geq` HTailPtr b = a `geq` b
340
347
341instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
348instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
349+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
350+ gcompareToOrd _ = id
351+#endif
342 HHeadPtr `gcompare` HHeadPtr = GEQ
352 HHeadPtr `gcompare` HHeadPtr = GEQ
343 HHeadPtr `gcompare` HTailPtr _ = GLT
353 HHeadPtr `gcompare` HTailPtr _ = GLT
344 HTailPtr _ `gcompare` HHeadPtr = GGT
354 HTailPtr _ `gcompare` HHeadPtr = GGT
@@ -349,6 +359,9 @@ data HListPtr l a where
349 HHeadPtr :: HListPtr (h ': t) h
359 HHeadPtr :: HListPtr (h ': t) h
350 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
360 HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
351
361
362+deriving instance Eq (HListPtr l a)
363+deriving instance Ord (HListPtr l a)
364+
352fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
365fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
353fhlistToDMap = DMap.fromList . go
366fhlistToDMap = DMap.fromList . go
354 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
367 where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
src/Reflex/DynamicWriter.hs+10−12
@@ -38,6 +38,7 @@ import Data.Some (Some)
38import Data.These
38import Data.These
39import Reflex.Class
39import Reflex.Class
40import Reflex.Host.Class
40import Reflex.Host.Class
41+import qualified Reflex.Patch.MapWithMove as MapWithMove
41import Reflex.PerformEvent.Class
42import Reflex.PerformEvent.Class
42import Reflex.PostBuild.Class
43import Reflex.PostBuild.Class
43import Reflex.Requester.Class
44import Reflex.Requester.Class
@@ -54,29 +55,26 @@ mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< samp
54 where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
55 where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
55 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
56 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
56
57
57−mergeDynIncrementalWithMove :: (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
58+mergeDynIncrementalWithMove :: forall t k v. (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
58mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
59mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
59 where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
60 where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
60 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
61 addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
62+ f :: These (PatchMapWithMove k v) (Map k v) -> PatchMapWithMove k v
61 f x = unsafePatchMapWithMove $
63 f x = unsafePatchMapWithMove $
62 let (p, changed) = case x of
64 let (p, changed) = case x of
63 This p_ -> (unPatchMapWithMove p_, mempty)
65 This p_ -> (unPatchMapWithMove p_, mempty)
64 That c -> (mempty, c)
66 That c -> (mempty, c)
65 These p_ c -> (unPatchMapWithMove p_, c)
67 These p_ c -> (unPatchMapWithMove p_, c)
66− (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ \case
68+ (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ MapWithMove.nodeInfoMapMFrom $ \case
67− MapEdit_Insert moved v -> return $ MapEdit_Insert moved v
69+ MapWithMove.From_Insert v -> return $ MapWithMove.From_Insert v
68− MapEdit_Delete moved -> return $ MapEdit_Delete moved
70+ MapWithMove.From_Delete -> return $ MapWithMove.From_Delete
69− MapEdit_Move moved k -> case Map.lookup k changed of
71+ MapWithMove.From_Move k -> case Map.lookup k changed of
70− Nothing -> return $ MapEdit_Move moved k
72+ Nothing -> return $ MapWithMove.From_Move k
71 Just v -> do
73 Just v -> do
72 modify (k:)
74 modify (k:)
73− return $ MapEdit_Insert moved v
75+ return $ MapWithMove.From_Insert v
74 noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
76 noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
75− removeMovedFlag = \case --TODO: Check if moved was true before, in each case?
76− MapEdit_Insert _ v -> MapEdit_Insert False v
77− MapEdit_Delete _ -> MapEdit_Delete False
78− MapEdit_Move _ k -> MapEdit_Move False k
79− in Map.differenceWith (\e _ -> Just $ removeMovedFlag e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
77+ in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
80
78
81-- | A basic implementation of 'MonadDynamicWriter'.
79-- | A basic implementation of 'MonadDynamicWriter'.
82newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
80newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
src/Reflex/Patch.hs+8−362
@@ -1,168 +1,18 @@
1-- | This module defines the 'Patch' class, which is used by Reflex to manage
1-- | This module defines the 'Patch' class, which is used by Reflex to manage
2-- changes to 'Reflex.Class.Incremental' values.
2-- changes to 'Reflex.Class.Incremental' values.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DeriveFoldable #-}
5−{-# LANGUAGE DeriveFunctor #-}
6−{-# LANGUAGE DeriveTraversable #-}
7−{-# LANGUAGE FlexibleContexts #-}
8−{-# LANGUAGE GADTs #-}
9−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10−{-# LANGUAGE LambdaCase #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE Rank2Types #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE StandaloneDeriving #-}
15{-# LANGUAGE TypeFamilies #-}
3{-# LANGUAGE TypeFamilies #-}
16module Reflex.Patch
4module Reflex.Patch
17− ( Patch (..)
5+ ( module Reflex.Patch
18− , PatchDMap (..)
6+ , module X
19− , mapPatchDMap
20− , traversePatchDMap
21− , traversePatchDMapWithKey
22− , weakenPatchDMapWith
23− , patchDMapToPatchMapWith
24− , const2PatchDMapWith
25− , ComposeMaybe (..)
26− , PatchMap (..)
27− , patchMapNewElements
28− , Group (..)
29− , Additive
30− , AdditivePatch (..)
31− , PatchMapWithMove
32− , unPatchMapWithMove
33− , unsafePatchMapWithMove
34− , patchMapWithMoveNewElements
35− , MapEdit (..)
36− , mapEditMoved
37− , mapEditSetMoved
38− , PatchDMapWithMove
39− , unPatchDMapWithMove
40− , unsafePatchDMapWithMove
41− , patchThatSortsMapWith
42− , patchThatChangesAndSortsMapWith
43− , mapPatchDMapWithMove
44− , traversePatchDMapWithMove
45− , traversePatchDMapWithMoveWithKey
46− , weakenPatchDMapWithMoveWith
47− , patchDMapWithMoveToPatchMapWithMoveWith
48− , const2PatchDMapWithMoveWith
49− , DMapEdit (..)
50− , dmapEditMoved
51− -- * Test functions
52− , validPatchMapWithMove
53− , knownValidPatchMapWithMove
54− , knownInvalidPatchMapWithMove
55 ) where
7 ) where
56
8
57−import Control.Arrow
9+import Reflex.Patch.Class as X
58−import Control.Monad.Identity
10+import Reflex.Patch.DMap as X
59−import Control.Monad.State
11+import Reflex.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, patchDMapWithMoveToPatchMapWithMoveWith, mapPatchDMapWithMove, weakenPatchDMapWithMoveWith, traversePatchDMapWithMoveWithKey, unsafePatchDMapWithMove, unPatchDMapWithMove)
60−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
61−import qualified Data.Dependent.Map as DMap
62−import Data.Foldable
63−import Data.Function
64−import Data.Functor.Constant
65−import Data.Functor.Misc
66−import Data.List
67−import Data.Map (Map)
68−import qualified Data.Map as Map
12+import Reflex.Patch.Map as X
69−import Data.Maybe
13+import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements, unsafePatchMapWithMove, unPatchMapWithMove)
70−import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid, (<>))
71−import qualified Data.Set as Set
72−import Data.Some (Some)
73−import qualified Data.Some as Some
74−import Data.Tuple
75
14
76−-- | A 'Patch' type represents a kind of change made to a datastructure.
15+import Data.Semigroup (Semigroup (..), (<>))
77−class Patch p where
78− type PatchTarget p :: *
79− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
80− -- 'Nothing'.
81− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
82−
83−-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
84−-- make the 'f' parameter have a nominal type role. We need f to be
85−-- representational so that we can use safe 'coerce'.
86−newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) }
87−
88−deriving instance Functor f => Functor (ComposeMaybe f)
89−
90−instance Patch (Identity a) where
91− type PatchTarget (Identity a) = a
92− apply (Identity a) _ = Just a
93−
94−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
95−-- deleted.
96−newtype PatchDMap k v = PatchDMap (DMap k (ComposeMaybe v))
97−
98−deriving instance GCompare k => Semigroup (PatchDMap k v)
99−
100−deriving instance GCompare k => Monoid (PatchDMap k v)
101−
102−instance GCompare k => Patch (PatchDMap k v) where
103− type PatchTarget (PatchDMap k v) = DMap k v
104− 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?
105− where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
106− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
107− nothingToJust = \case
108− Nothing -> Just $ Constant ()
109− Just _ -> Nothing
110−
111−mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
112−mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
113−
114−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
115−traversePatchDMap f = traversePatchDMapWithKey $ const f
116−
117−traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
118−traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
119−
120−weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
121−weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
122−
123−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
124−patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
125−
126−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
127−const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
128− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
129− g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
130−
131−-- | A set of changes to a 'Map'. Any element may be inserted/updated or
132−-- deleted.
133−newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
134−
135−instance Ord k => Patch (PatchMap k v) where
136− type PatchTarget (PatchMap k v) = Map k v
137− 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?
138− where insertions = Map.mapMaybeWithKey (const id) p
139− deletions = Map.mapMaybeWithKey (const nothingToJust) p
140− nothingToJust = \case
141− Nothing -> Just ()
142− Just _ -> Nothing
143−
144−instance Ord k => Semigroup (PatchMap k v) where
145− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
146− -- PatchMap is idempotent, so stimes n is id for every n
147−#if MIN_VERSION_semigroups(0,17,0)
148− stimes = stimesIdempotentMonoid
149−#else
150− times1p n x = case compare n 0 of
151− LT -> error "stimesIdempotentMonoid: negative multiplier"
152− EQ -> mempty
153− GT -> x
154−#endif
155−
156−instance Ord k => Monoid (PatchMap k v) where
157− mempty = PatchMap mempty
158− mappend = (<>)
159−
160−instance Functor (PatchMap k) where
161− fmap f = PatchMap . fmap (fmap f) . unPatchMap
162−
163−-- | Returns all the new elements that will be added to the 'Map'
164−patchMapNewElements :: PatchMap k v -> [v]
165−patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
166
16
167---- Patches based on commutative groups
17---- Patches based on commutative groups
168
18
@@ -181,207 +31,3 @@ newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
181instance Additive p => Patch (AdditivePatch p) where
31instance Additive p => Patch (AdditivePatch p) where
182 type PatchTarget (AdditivePatch p) = p
32 type PatchTarget (AdditivePatch p) = p
183 apply (AdditivePatch p) q = Just $ p <> q
33 apply (AdditivePatch p) q = Just $ p <> q
184−
185−data MapEdit k v
186− = MapEdit_Insert Bool v -- ^ Insert the given value here
187− | MapEdit_Delete Bool -- ^ Delete the existing value, if any, from here
188− | MapEdit_Move Bool k -- ^ Move the value here from the given key
189− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
190−
191−mapEditMoved :: MapEdit k v -> Bool
192−mapEditMoved = \case
193− MapEdit_Insert moved _ -> moved
194− MapEdit_Delete moved -> moved
195− MapEdit_Move moved _ -> moved
196−
197−mapEditSetMoved :: Bool -> MapEdit k v -> MapEdit k v
198−mapEditSetMoved moved = \case
199− MapEdit_Insert _ v -> MapEdit_Insert moved v
200− MapEdit_Delete _ -> MapEdit_Delete moved
201− MapEdit_Move _ k -> MapEdit_Move moved k
202−
203−-- | Patch a DMap with additions, deletions, and moves.
204−-- Invariants:
205−-- * The same source key may not be moved to two destination keys.
206−-- * Any key that is the source of a Move must also be edited
207−-- (otherwise, the Move would duplicate it)
208−newtype PatchMapWithMove k v = PatchMapWithMove (Map k (MapEdit k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
209−
210−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (MapEdit k v)
211−unPatchMapWithMove (PatchMapWithMove p) = p
212−
213−-- | Warning: when using this function, you must ensure that the invariants of
214−-- 'PatchMapWithMove' are preserved; they will not be checked.
215−unsafePatchMapWithMove :: Map k (MapEdit k v) -> PatchMapWithMove k v
216−unsafePatchMapWithMove = PatchMapWithMove
217−
218−instance Ord k => Patch (PatchMapWithMove k v) where
219− type PatchTarget (PatchMapWithMove k v) = Map k v
220− apply (PatchMapWithMove 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?
221− where insertions = flip Map.mapMaybeWithKey p $ const $ \case
222− MapEdit_Insert _ v -> Just v
223− MapEdit_Move _ k -> Map.lookup k old
224− MapEdit_Delete _ -> Nothing
225− deletions = flip Map.mapMaybeWithKey p $ const $ \case
226− MapEdit_Delete _ -> Just ()
227− _ -> Nothing
228−
229−-- | Returns all the new elements that will be added to the 'Map'
230−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
231−patchMapWithMoveNewElements (PatchMapWithMove p) = catMaybes $ flip fmap (Map.elems p) $ \case
232− MapEdit_Insert _ v -> Just v
233− MapEdit_Move _ _ -> Nothing
234− MapEdit_Delete _ -> Nothing
235−
236−validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
237−validPatchMapWithMove (PatchMapWithMove p) =
238− let moveSources = catMaybes $ flip fmap (Map.elems p) $ \case
239− MapEdit_Move _ k -> Just k
240− _ -> Nothing
241− in and
242− [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
243− all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
244− , -- All Move source keys must also be patched - since the value's being moved elsewhere, the original must either be Deleted or replaced (with Insert or Move)
245− all (isJust . flip Map.lookup p) moveSources
246− ]
247−
248−knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
249−knownValidPatchMapWithMove =
250− [ PatchMapWithMove mempty
251− , PatchMapWithMove $ Map.fromList
252− [ (1, MapEdit_Move True 2)
253− , (2, MapEdit_Move True 1)
254− ]
255− ]
256−
257−knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
258−knownInvalidPatchMapWithMove =
259− [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
260− PatchMapWithMove $ Map.singleton 1 $ MapEdit_Move False 2
261− , -- Invalid because we have multiple moves from 3
262− PatchMapWithMove $ Map.fromList
263− [ (1, MapEdit_Move False 3)
264− , (2, MapEdit_Move False 3)
265− , (3, MapEdit_Delete True)
266− ]
267− ]
268−
269−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
270−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g l $ sortBy (cmp `on` snd) l
271− where l = Map.toList m
272− g (to, _) (from, _) = if to == from
273− then Nothing
274− else Just (to, MapEdit_Move True from)
275−
276−patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
277−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
278− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
279− newList = Map.toList newByIndexUnsorted
280− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
281− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
282− let f k v = do
283− remainingValues <- get
284− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
285− then Map.delete v remainingValues
286− else Map.insert v remainingKeys remainingValues
287− case Map.lookup v remainingValues of
288− Nothing -> return $ MapEdit_Insert True v -- There's no existing value we can take
289− Just fromKs ->
290− if k `Set.member` fromKs
291− then do
292− putRemainingKeys $ Set.delete k fromKs
293− return $ MapEdit_Move True k -- There's an existing value, and it's here, so no patch necessary
294− else do
295− Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
296− putRemainingKeys remainingKeys
297− return $ MapEdit_Move True fromK
298− Map.traverseWithKey f newByIndex
299− unusedOldKeys = fold unusedValuesByValue
300− pointlessMove k = \case
301− MapEdit_Move _ k' | k == k' -> True
302− _ -> False
303− keyWasMoved k = k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
304− patch = unsafePatchMapWithMove $ Map.filterWithKey (\k v -> not $ pointlessMove k v) $ Map.mergeWithKey (\k a _ -> Just $ mapEditSetMoved (keyWasMoved k) a) (Map.mapWithKey $ \k -> mapEditSetMoved $ keyWasMoved k) (Map.mapWithKey $ \k _ -> MapEdit_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
305−
306−data DMapEdit (k :: a -> *) (v :: a -> *) :: a -> * where
307− -- | Insert the given value here
308− DMapEdit_Insert :: Bool -> v a -> DMapEdit k v a
309− -- | Delete the existing value, if any, from here
310− DMapEdit_Delete :: Bool -> DMapEdit k v a
311− -- | Move the value here from the given key
312− DMapEdit_Move :: Bool -> k a -> DMapEdit k v a
313−
314−dmapEditMoved :: DMapEdit k v a -> Bool
315−dmapEditMoved = \case
316− DMapEdit_Insert moved _ -> moved
317− DMapEdit_Delete moved -> moved
318− DMapEdit_Move moved _ -> moved
319−
320−-- | Like 'PatchMapWithMove', but for 'DMap'.
321−newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (DMapEdit k v))
322−
323−unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (DMapEdit k v)
324−unPatchDMapWithMove (PatchDMapWithMove p) = p
325−
326−-- | Warning: when using this function, you must ensure that the invariants of
327−-- 'PatchDMapWithMove' are preserved; they will not be checked.
328−unsafePatchDMapWithMove :: DMap k (DMapEdit k v) -> PatchDMapWithMove k v
329−unsafePatchDMapWithMove = PatchDMapWithMove
330−
331−mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
332−mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $ DMap.map g p
333− where g :: forall a. DMapEdit k v a -> DMapEdit k v' a
334− g = \case
335− DMapEdit_Insert moved v -> DMapEdit_Insert moved $ f v
336− DMapEdit_Delete moved -> DMapEdit_Delete moved
337− DMapEdit_Move moved k -> DMapEdit_Move moved k
338−
339−traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
340−traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
341−
342−traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
343−traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey g p
344− where g :: forall a. k a -> DMapEdit k v a -> m (DMapEdit k v' a)
345− g k = \case
346− DMapEdit_Insert moved v -> DMapEdit_Insert moved <$> f k v
347− DMapEdit_Delete moved -> pure $ DMapEdit_Delete moved
348− DMapEdit_Move moved fromKey -> pure $ DMapEdit_Move moved fromKey
349−
350−weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
351−weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
352− where g :: forall a. DMapEdit k v a -> MapEdit (Some k) v'
353− g = \case
354− DMapEdit_Insert moved v -> MapEdit_Insert moved $ f v
355− DMapEdit_Delete moved -> MapEdit_Delete moved
356− DMapEdit_Move moved k -> MapEdit_Move moved $ Some.This k
357−
358−patchDMapWithMoveToPatchMapWithMoveWith :: forall k f v v'. (f v -> v') -> PatchDMapWithMove (Const2 k v) f -> PatchMapWithMove k v'
359−patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
360− where g :: DMapEdit (Const2 k v) f v -> MapEdit k v'
361− g = \case
362− DMapEdit_Insert moved v -> MapEdit_Insert moved $ f v
363− DMapEdit_Delete moved -> MapEdit_Delete moved
364− DMapEdit_Move moved (Const2 k) -> MapEdit_Move moved k
365−
366−const2PatchDMapWithMoveWith :: forall k v f a. (v -> f a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) f
367−const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
368− where g :: (k, MapEdit k v) -> DSum (Const2 k a) (DMapEdit (Const2 k a) f)
369− g (k, e) = Const2 k :=> case e of
370− MapEdit_Insert moved v -> DMapEdit_Insert moved $ f v
371− MapEdit_Delete moved -> DMapEdit_Delete moved
372− MapEdit_Move moved fromKey -> DMapEdit_Move moved $ Const2 fromKey
373−
374−instance GCompare k => Patch (PatchDMapWithMove k v) where
375− type PatchTarget (PatchDMapWithMove k v) = DMap k v
376− 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?
377− where insertions = DMap.mapMaybeWithKey insertFunc p
378− insertFunc :: forall a. k a -> DMapEdit k v a -> Maybe (v a)
379− insertFunc _ = \case
380− DMapEdit_Insert _ v -> Just v
381− DMapEdit_Move _ k -> DMap.lookup k old
382− DMapEdit_Delete _ -> Nothing
383− deletions = DMap.mapMaybeWithKey deleteFunc p
384− deleteFunc :: forall a. k a -> DMapEdit k v a -> Maybe (Constant () a)
385− deleteFunc _ = \case
386− DMapEdit_Delete _ -> Just $ Constant ()
387− _ -> Nothing
src/Reflex/Patch/Class.hsnew file+20
@@ -0,0 +1,20 @@
1+{-# LANGUAGE TypeFamilies #-}
2+module Reflex.Patch.Class where
3+
4+import Control.Monad.Identity
5+import Data.Maybe
6+
7+-- | A 'Patch' type represents a kind of change made to a datastructure.
8+class Patch p where
9+ type PatchTarget p :: *
10+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
11+ -- 'Nothing'.
12+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
13+
14+applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
15+applyAlways p t = fromMaybe t $ apply p t
16+
17+instance Patch (Identity a) where
18+ type PatchTarget (Identity a) = a
19+ apply (Identity a) _ = Just a
20+
src/Reflex/Patch/DMap.hsnew file+61
@@ -0,0 +1,61 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE PolyKinds #-}
4+{-# LANGUAGE Rank2Types #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE StandaloneDeriving #-}
7+{-# LANGUAGE TypeFamilies #-}
8+module Reflex.Patch.DMap where
9+
10+import Reflex.Patch.Class
11+import Reflex.Patch.Map
12+
13+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
14+import qualified Data.Dependent.Map as DMap
15+import Data.Functor.Constant
16+import Data.Functor.Misc
17+import qualified Data.Map as Map
18+import Data.Semigroup (Semigroup (..))
19+import Data.Some (Some)
20+
21+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
22+-- deleted.
23+newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
24+
25+deriving instance GCompare k => Semigroup (PatchDMap k v)
26+
27+deriving instance GCompare k => Monoid (PatchDMap k v)
28+
29+instance GCompare k => Patch (PatchDMap k v) where
30+ type PatchTarget (PatchDMap k v) = DMap k v
31+ 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?
32+ where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
33+ deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
34+ nothingToJust = \case
35+ Nothing -> Just $ Constant ()
36+ Just _ -> Nothing
37+
38+mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
39+mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
40+
41+traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
42+traversePatchDMap f = traversePatchDMapWithKey $ const f
43+
44+traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
45+traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
46+
47+weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
48+weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
49+
50+patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
51+patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
52+
53+const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
54+const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
55+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
56+ g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
57+
58+-- | Get the values that will be deleted if the given patch is applied to the
59+-- given 'DMap'. Includes values that will be replaced.
60+getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
61+getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hsnew file+281
@@ -0,0 +1,281 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+module Reflex.Patch.DMapWithMove where
14+
15+import Reflex.Patch.Class
16+import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
17+import qualified Reflex.Patch.MapWithMove as MapWithMove
18+
19+import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
20+import qualified Data.Dependent.Map as DMap
21+import Data.Dependent.Sum (EqTag (..))
22+import Data.Functor.Constant
23+import Data.Functor.Misc
24+import Data.Functor.Product
25+import Data.GADT.Compare (GEq (..))
26+import qualified Data.Map as Map
27+import Data.Maybe
28+import Data.Semigroup (Semigroup (..), (<>))
29+import Data.Some (Some)
30+import qualified Data.Some as Some
31+import Data.These
32+
33+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
34+import Data.Dependent.Sum (ShowTag (..))
35+import Data.GADT.Show (GShow (..))
36+import Data.Typeable (Proxy (..))
37+#endif
38+
39+-- | Like 'PatchMapWithMove', but for 'DMap'.
40+data PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
41+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
42+ deriving (Show)
43+#endif
44+
45+data NodeInfo k v a = NodeInfo
46+ { _nodeInfo_from :: !(From k v a)
47+ , _nodeInfo_to :: !(To k a)
48+ }
49+ deriving (Show)
50+
51+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
52+instance {-# INCOHERENT #-} (GShow k, ShowTag k v) => ShowTag k (NodeInfo k v) where
53+ showTagToShow k _ r = gshowToShow k $ showTagToShow k (Proxy :: Proxy v) r
54+
55+instance {-# INCOHERENT #-} (GEq k, EqTag k v) => EqTag k (From k v) where
56+ eqTagToEq k _ r = eqTagToEq k (Proxy :: Proxy v) (geqToEq k r)
57+#endif
58+
59+data From (k :: a -> *) (v :: a -> *) :: a -> * where
60+ From_Insert :: v a -> From k v a
61+ From_Delete :: From k v a
62+ From_Move :: !(k a) -> From k v a
63+ deriving (Show, Read, Eq, Ord)
64+
65+type To = ComposeMaybe
66+
67+validPatchDMapWithMove :: forall k v. (GCompare k, EqTag k (ComposeMaybe k)) => PatchDMapWithMove k v -> Bool
68+validPatchDMapWithMove (PatchDMapWithMove m) = src == srcFromDst
69+ where
70+ src = DMap.map _nodeInfo_to m
71+ dst = DMap.map _nodeInfo_from m
72+ srcFromDst :: DMap k (ComposeMaybe k)
73+ srcFromDst = DMap.fromList $ flip fmap (DMap.toList dst) $ \(to :=> edit) -> case edit of
74+ From_Move from -> from :=> ComposeMaybe (Just to)
75+ _ -> to :=> ComposeMaybe Nothing
76+
77+instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
78+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
79+
80+data Pair1 f g a = Pair1 (f a) (g a)
81+
82+instance GCompare k => Semigroup (PatchDMapWithMove k v) where
83+ (<>) = mappend
84+
85+data Fixup k v a
86+ = Fixup_Delete
87+ | Fixup_Update (These (From k v a) (To k a))
88+
89+instance GCompare k => Monoid (PatchDMapWithMove k v) where
90+ mempty = PatchDMapWithMove mempty
91+ PatchDMapWithMove ma `mappend` PatchDMapWithMove mb = PatchDMapWithMove m
92+ where
93+ connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
94+ h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
95+ h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
96+ (Just toAfter, From_Move fromBefore)
97+ | isJust $ fromBefore `geq` toAfter
98+ -> [toAfter :=> Fixup_Delete]
99+ | otherwise
100+ -> [ toAfter :=> Fixup_Update (This editBefore)
101+ , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
102+ ]
103+ (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
104+ (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
105+ (Nothing, _) -> []
106+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
107+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
108+ | This x <- a, That y <- b
109+ = Fixup_Update $ These x y
110+ | That y <- a, This x <- b
111+ = Fixup_Update $ These x y
112+ mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups"
113+ fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
114+ combineNodeInfos _ nia nib = NodeInfo
115+ { _nodeInfo_from = _nodeInfo_from nia
116+ , _nodeInfo_to = _nodeInfo_to nib
117+ }
118+ applyFixup _ ni = \case
119+ Fixup_Delete -> Nothing
120+ Fixup_Update u -> Just $ NodeInfo
121+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
122+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
123+ }
124+ m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
125+
126+getHere :: These a b -> Maybe a
127+getHere = \case
128+ This a -> Just a
129+ These a _ -> Just a
130+ That _ -> Nothing
131+
132+getThere :: These a b -> Maybe b
133+getThere = \case
134+ This _ -> Nothing
135+ These _ b -> Just b
136+ That b -> Just b
137+
138+{-
139+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
140+PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
141+ where
142+ 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
143+ removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
144+ removeRedundantDst _ a = Just a
145+ f :: forall a. k a -> From k v a -> Maybe (From k v a)
146+ f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
147+ From_Move fromKey -> getDstAction fromKey dstBefore
148+ nonMove -> nonMove
149+ dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
150+ getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
151+ removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
152+ removeRedundantSrc _ a = Just a
153+ g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
154+ g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
155+ ComposeMaybe Nothing -> ComposeMaybe Nothing
156+ ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
157+ src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
158+-}
159+
160+moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
161+moveDMapKey src dst = case src `geq` dst of
162+ Nothing -> PatchDMapWithMove $ DMap.fromList
163+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
164+ , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
165+ ]
166+ Just _ -> mempty
167+
168+deleteDMapKey :: k a -> PatchDMapWithMove k v
169+deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
170+
171+{-
172+k1, k2 :: Const2 Int () ()
173+k1 = Const2 1
174+k2 = Const2 2
175+p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity
176+p1 = moveDMapKey k1 k2
177+p2 = moveDMapKey k2 k1
178+p12 = p1 <> p2
179+p21 = p2 <> p1
180+p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2
181+p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1
182+
183+testPatchDMapWithMove = do
184+ print p1
185+ print p2
186+ print $ p12 == deleteDMapKey k1
187+ print $ p21 == deleteDMapKey k2
188+ print $ p12Slow == deleteDMapKey k1
189+ print $ p21Slow == deleteDMapKey k2
190+
191+dst (PatchDMapWithMove x _) = x
192+src (PatchDMapWithMove _ x) = x
193+-}
194+
195+unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
196+unPatchDMapWithMove (PatchDMapWithMove p) = p
197+
198+-- | Warning: when using this function, you must ensure that the invariants of
199+-- 'PatchDMapWithMove' are preserved; they will not be checked.
200+unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
201+unsafePatchDMapWithMove = PatchDMapWithMove
202+
203+mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
204+mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
205+ DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
206+ where g :: forall a. From k v a -> From k v' a
207+ g = \case
208+ From_Insert v -> From_Insert $ f v
209+ From_Delete -> From_Delete
210+ From_Move k -> From_Move k
211+
212+traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
213+traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
214+
215+traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
216+traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
217+ where g :: forall a. k a -> From k v a -> m (From k v' a)
218+ g k = \case
219+ From_Insert v -> From_Insert <$> f k v
220+ From_Delete -> pure From_Delete
221+ From_Move fromKey -> pure $ From_Move fromKey
222+
223+nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
224+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
225+
226+nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
227+nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
228+
229+weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
230+weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
231+ where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
232+ g ni = MapWithMove.NodeInfo
233+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
234+ From_Insert v -> MapWithMove.From_Insert $ f v
235+ From_Delete -> MapWithMove.From_Delete
236+ From_Move k -> MapWithMove.From_Move $ Some.This k
237+ , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
238+ }
239+
240+patchDMapWithMoveToPatchMapWithMoveWith :: forall k f v v'. (f v -> v') -> PatchDMapWithMove (Const2 k v) f -> PatchMapWithMove k v'
241+patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
242+ where g :: NodeInfo (Const2 k v) f v -> MapWithMove.NodeInfo k v'
243+ g ni = MapWithMove.NodeInfo
244+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
245+ From_Insert v -> MapWithMove.From_Insert $ f v
246+ From_Delete -> MapWithMove.From_Delete
247+ From_Move (Const2 k) -> MapWithMove.From_Move k
248+ , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
249+ }
250+
251+const2PatchDMapWithMoveWith :: forall k v f a. (v -> f a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) f
252+const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
253+ where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) f)
254+ g (k, ni) = Const2 k :=> NodeInfo
255+ { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
256+ MapWithMove.From_Insert v -> From_Insert $ f v
257+ MapWithMove.From_Delete -> From_Delete
258+ MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
259+ , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
260+ }
261+
262+instance GCompare k => Patch (PatchDMapWithMove k v) where
263+ type PatchTarget (PatchDMapWithMove k v) = DMap k v
264+ 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?
265+ where insertions = DMap.mapMaybeWithKey insertFunc p
266+ insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
267+ insertFunc _ ni = case _nodeInfo_from ni of
268+ From_Insert v -> Just v
269+ From_Move k -> DMap.lookup k old
270+ From_Delete -> Nothing
271+ deletions = DMap.mapMaybeWithKey deleteFunc p
272+ deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
273+ deleteFunc _ ni = case _nodeInfo_from ni of
274+ From_Delete -> Just $ Constant ()
275+ _ -> Nothing
276+
277+-- | Get the values that will be deleted or moved if the given patch is applied
278+-- to the given 'DMap'.
279+getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
280+getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
281+ where f _ v ni = Pair v $ _nodeInfo_to ni
src/Reflex/Patch/Map.hsnew file+48
@@ -0,0 +1,48 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE TypeFamilies #-}
4+module Reflex.Patch.Map where
5+
6+import Reflex.Patch.Class
7+
8+import Data.Semigroup
9+import Data.Map (Map)
10+import qualified Data.Map as Map
11+import Data.Maybe
12+
13+-- | A set of changes to a 'Map'. Any element may be inserted/updated or
14+-- deleted.
15+newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
16+
17+instance Ord k => Patch (PatchMap k v) where
18+ type PatchTarget (PatchMap k v) = Map k v
19+ 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?
20+ where insertions = Map.mapMaybeWithKey (const id) p
21+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
22+ nothingToJust = \case
23+ Nothing -> Just ()
24+ Just _ -> Nothing
25+
26+instance Ord k => Semigroup (PatchMap k v) where
27+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
28+ -- PatchMap is idempotent, so stimes n is id for every n
29+#if MIN_VERSION_semigroups(0,17,0)
30+ stimes = stimesIdempotentMonoid
31+#else
32+ times1p n x = case compare n 0 of
33+ LT -> error "stimesIdempotentMonoid: negative multiplier"
34+ EQ -> mempty
35+ GT -> x
36+#endif
37+
38+instance Ord k => Monoid (PatchMap k v) where
39+ mempty = PatchMap mempty
40+ mappend = (<>)
41+
42+instance Functor (PatchMap k) where
43+ fmap f = PatchMap . fmap (fmap f) . unPatchMap
44+
45+-- | Returns all the new elements that will be added to the 'Map'
46+patchMapNewElements :: PatchMap k v -> [v]
47+patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
48+
src/Reflex/Patch/MapWithMove.hsnew file+156
@@ -0,0 +1,156 @@
1+{-# LANGUAGE DeriveFoldable #-}
2+{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE DeriveTraversable #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeFamilies #-}
8+module Reflex.Patch.MapWithMove where
9+
10+import Reflex.Patch.Class
11+
12+import Control.Arrow
13+import Control.Monad.State
14+import Data.Foldable
15+import Data.Function
16+import Data.List
17+import Data.Map (Map)
18+import qualified Data.Map as Map
19+import Data.Maybe
20+import qualified Data.Set as Set
21+import Data.Tuple
22+
23+-- | Patch a DMap with additions, deletions, and moves.
24+-- Invariants:
25+-- * The same source key may not be moved to two destination keys.
26+-- * Any key that is the source of a Move must also be edited
27+-- (otherwise, the Move would duplicate it)
28+data PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
29+
30+data NodeInfo k v = NodeInfo
31+ { _nodeInfo_from :: !(From k v)
32+ , _nodeInfo_to :: !(To k)
33+ }
34+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
35+
36+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
37+unPatchMapWithMove (PatchMapWithMove p) = p
38+
39+-- | Warning: when using this function, you must ensure that the invariants of
40+-- 'PatchMapWithMove' are preserved; they will not be checked.
41+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
42+unsafePatchMapWithMove = PatchMapWithMove
43+
44+data From k v
45+ = From_Insert v -- ^ Insert the given value here
46+ | From_Delete -- ^ Delete the existing value, if any, from here
47+ | From_Move !k -- ^ Move the value here from the given key
48+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
49+
50+type To = Maybe
51+
52+instance Ord k => Patch (PatchMapWithMove k v) where
53+ type PatchTarget (PatchMapWithMove k v) = Map k v
54+ apply (PatchMapWithMove 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?
55+ where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
56+ From_Insert v -> Just v
57+ From_Move k -> Map.lookup k old
58+ From_Delete -> Nothing
59+ deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
60+ From_Delete -> Just ()
61+ _ -> Nothing
62+
63+-- | Returns all the new elements that will be added to the 'Map'
64+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
65+patchMapWithMoveNewElements (PatchMapWithMove p) = catMaybes $ f <$> Map.elems p
66+ where f ni = case _nodeInfo_from ni of
67+ From_Insert v -> Just v
68+ From_Move _ -> Nothing
69+ From_Delete -> Nothing
70+
71+validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
72+validPatchMapWithMove (PatchMapWithMove p) =
73+ let f ni = case _nodeInfo_from ni of
74+ From_Move k -> Just k
75+ _ -> Nothing
76+ moveSources = catMaybes $ f <$> Map.elems p
77+ in and
78+ [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
79+ all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
80+ , -- All Move source keys must also be patched - since the value's being moved elsewhere, the original must either be Deleted or replaced (with Insert or Move)
81+ all (isJust . flip Map.lookup p) moveSources
82+ ]
83+
84+knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
85+knownValidPatchMapWithMove =
86+ [ PatchMapWithMove mempty
87+ , PatchMapWithMove $ Map.fromList
88+ [ (1, NodeInfo (From_Move 2) $ Just 1)
89+ , (2, NodeInfo (From_Move 1) $ Just 2)
90+ ]
91+ ]
92+
93+knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
94+knownInvalidPatchMapWithMove =
95+ [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
96+ PatchMapWithMove $ Map.singleton 1 $ NodeInfo (From_Move 2) Nothing
97+ , -- Invalid because we have multiple moves from 3
98+ PatchMapWithMove $ Map.fromList
99+ [ (1, NodeInfo (From_Move 3) Nothing)
100+ , (2, NodeInfo (From_Move 3) Nothing)
101+ , (3, NodeInfo From_Delete (Just 1))
102+ ]
103+ ]
104+
105+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
106+patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
107+ where unsorted = Map.toList m
108+ sorted = sortBy (cmp `on` snd) unsorted
109+ f (to, _) (from, _) = if to == from then Nothing else
110+ Just (from, to)
111+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
112+ g (to, _) (from, _) = if to == from then Nothing else
113+ let Just movingTo = Map.lookup from reverseMapping
114+ in Just (to, NodeInfo (From_Move from) $ Just movingTo)
115+
116+patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
117+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
118+ where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
119+ newList = Map.toList newByIndexUnsorted
120+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
121+ insertsAndMoves :: Map k (NodeInfo k v)
122+ (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
123+ let f k v = do
124+ remainingValues <- get
125+ let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
126+ then Map.delete v remainingValues
127+ else Map.insert v remainingKeys remainingValues
128+ case Map.lookup v remainingValues of
129+ Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
130+ Just fromKs ->
131+ if k `Set.member` fromKs
132+ then do
133+ putRemainingKeys $ Set.delete k fromKs
134+ return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
135+ else do
136+ Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
137+ putRemainingKeys remainingKeys
138+ return $ NodeInfo (From_Move fromK) $ Just undefined
139+ Map.traverseWithKey f newByIndex
140+ unusedOldKeys = fold unusedValuesByValue
141+ pointlessMove k = \case
142+ From_Move k' | k == k' -> True
143+ _ -> False
144+ keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
145+ then Just undefined
146+ else Nothing
147+ patch = unsafePatchMapWithMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
148+
149+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
150+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
151+
152+nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
153+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
154+
155+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
156+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
src/Reflex/Spider/Internal.hs+35−33
@@ -87,6 +87,7 @@ import qualified Reflex.Class as R
87import qualified Reflex.Host.Class
87import qualified Reflex.Host.Class
88#endif
88#endif
89import Reflex.Patch
89import Reflex.Patch
90+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
90
91
91-- Note: must come last to silence warnings due to AMP on GHC < 7.10
92-- Note: must come last to silence warnings due to AMP on GHC < 7.10
92import Prelude hiding (any, concat, mapM, mapM_, sequence)
93import Prelude hiding (any, concat, mapM, mapM_, sequence)
@@ -727,10 +728,21 @@ data SpiderTimelineEnv x = SpiderTimelineEnv
727 }
728 }
728type role SpiderTimelineEnv nominal
729type role SpiderTimelineEnv nominal
729
730
731+instance Eq (SpiderTimelineEnv x) where
732+ _ == _ = True -- Since only one exists of each type
733+
730instance GEq SpiderTimelineEnv where
734instance GEq SpiderTimelineEnv where
735+#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
736+ geqToEq _ = id
737+ geqUnify (a :: SpiderTimelineEnv a) (b :: SpiderTimelineEnv b) different same = if _spiderTimeline_lock a == _spiderTimeline_lock b
738+ then case unsafeCoerce Refl :: a :~: b of
739+ Refl -> same -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
740+ else different
741+#else
731 a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
742 a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
732 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
743 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
733 else Nothing
744 else Nothing
745+#endif
734
746
735data EventEnv x
747data EventEnv x
736 = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
748 = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
@@ -1611,38 +1623,28 @@ mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x
1611mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1623mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1612 where
1624 where
1613 updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1625 updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1614− updateMe subscriber heightBagRef oldParents pWrapped = do
1626+ updateMe subscriber heightBagRef oldParents p = do
1615− let p = unPatchDMapWithMove pWrapped
1627+ -- Prepare new parents for insertion
1616− let f (subscriptionsToKill, ps) (k :=> edit) = do
1628+ let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1617− let deleteHere = return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1629+ subscribeParent k e = do
1618− insertHere e = do
1619− keyRef <- liftIO $ newIORef k
1630+ keyRef <- liftIO $ newIORef k
1620− let s = subscriber $ liftIO $ readIORef keyRef
1631+ let s = subscriber $ liftIO $ readIORef keyRef
1621− subscription@(EventSubscription _ subd) <- subscribe e s
1632+ subscription@(EventSubscription _ subd) <- subscribe e s
1622− liftIO $ do
1633+ liftIO $ do
1623− newParentHeight <- getEventSubscribedHeight subd
1634+ newParentHeight <- getEventSubscribedHeight subd
1624− let newParent = MergeSubscribedParentWithMove subscription keyRef
1625− modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1635+ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1626− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1636+ return $ MergeSubscribedParentWithMove subscription keyRef
1627− (mOldSubd, newPs) <- case edit of
1637+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1628− DMapEdit_Delete moved -> do
1638+ -- Collect old parents for deletion and update the keys of moved parents
1629− (toDelete, newPs) <- deleteHere
1639+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1630− return (if moved then Nothing else toDelete, newPs)
1640+ moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1631− DMapEdit_Insert moved e -> do
1632− (toDelete, newPs) <- insertHere e
1633− return (if moved then Nothing else toDelete, newPs)
1634− DMapEdit_Move moved sourceKey -> do
1635− (toDelete, newPs) <- case DMap.lookup sourceKey ps of
1636− Nothing -> deleteHere
1641+ Nothing -> do
1637− Just s@(MergeSubscribedParentWithMove _ keyRef) -> do
1642+ return $ Just $ _mergeSubscribedParentWithMove_subscription parent
1638− liftIO $ writeIORef keyRef k
1643+ Just toKey -> do
1639− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k s ps
1644+ liftIO $ writeIORef (_mergeSubscribedParentWithMove_key parent) $! toKey
1640− return (if moved then Nothing else toDelete, newPs)
1645+ return Nothing
1641− forM_ mOldSubd $ \oldSubd -> do
1646+ toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1642− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription oldSubd
1647+ return (toDelete, applyAlways p' oldParents)
1643− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1644− return (maybeToList (_mergeSubscribedParentWithMove_subscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1645− foldM f ([], oldParents) $ DMap.toList p
1646 getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1648 getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1647 getInitialSubscribers initialParents subscriber = do
1649 getInitialSubscribers initialParents subscriber = do
1648 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1650 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
@@ -1705,7 +1707,7 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
1705 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1707 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1706 writeIORef heightRef $! height
1708 writeIORef heightRef $! height
1707 subscriberRecalculateHeight sub height
1709 subscriberRecalculateHeight sub height
1708− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge"
1710+ GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (numParents parents) <> ") for Merge"
1709 subscriber :: forall a. EventM x (k a) -> Subscriber x a
1711 subscriber :: forall a. EventM x (k a) -> Subscriber x a
1710 subscriber getKey = Subscriber
1712 subscriber getKey = Subscriber
1711 { subscriberPropagate = \a -> do
1713 { subscriberPropagate = \a -> do
test/Reflex/Test/CrossImpl.hs+18−10
@@ -19,6 +19,7 @@ import Control.Monad.Ref
19import Reflex.Class
19import Reflex.Class
20import Reflex.Dynamic
20import Reflex.Dynamic
21import Reflex.Host.Class
21import Reflex.Host.Class
22+import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
22import qualified Reflex.Pure as P
23import qualified Reflex.Pure as P
23import qualified Reflex.Spider.Internal as S
24import qualified Reflex.Spider.Internal as S
24
25
@@ -26,8 +27,10 @@ import Control.Arrow (second, (&&&))
26import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
27import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
27import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29−import Data.Dependent.Map (DSum (..))
30+import Data.Dependent.Map (DMap, DSum (..))
31+import qualified Data.Dependent.Map as DMap
30import Data.Foldable
32import Data.Foldable
33+import Data.Functor.Misc
31import Data.Map.Strict (Map)
34import Data.Map.Strict (Map)
32import qualified Data.Map.Strict as Map
35import qualified Data.Map.Strict as Map
33import qualified Data.Set as Set
36import qualified Data.Set as Set
@@ -106,8 +109,9 @@ testAgreement builder inputs = do
106 tracePerf "---------" $ return ()
109 tracePerf "---------" $ return ()
107 let resultsAgree = identityResult == spiderResult
110 let resultsAgree = identityResult == spiderResult
108 if resultsAgree
111 if resultsAgree
109− then do putStrLn "Success:"
112+ then do --putStrLn "Success:"
110− print identityResult
113+ --print identityResult
114+ return ()
111 else do putStrLn "Failure:"
115 else do putStrLn "Failure:"
112 putStrLn $ "Pure result: " <> show identityResult
116 putStrLn $ "Pure result: " <> show identityResult
113 putStrLn $ "Spider result: " <> show spiderResult
117 putStrLn $ "Spider result: " <> show spiderResult
@@ -126,8 +130,8 @@ testCases =
126 e' <- updated <$> count e
130 e' <- updated <$> count e
127 b' <- hold (0 :: Int) e'
131 b' <- hold (0 :: Int) e'
128 return (b', e')
132 return (b', e')
129− , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
133+ , (,) "headE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
130− e' <- onceE $ leftmost [e, e]
134+ e' <- headE $ leftmost [e, e]
131 return (b, e')
135 return (b, e')
132 , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
136 , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
133 let e' = fmap (const e) e
137 let e' = fmap (const e) e
@@ -172,7 +176,7 @@ testCases =
172 return (b, e'')
176 return (b, e'')
173 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
177 , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
174 let e' = flip push e $ \_ -> do
178 let e' = flip push e $ \_ -> do
175− Just <$> onceE e
179+ Just <$> headE e
176 e'' <- switchPromptly never e'
180 e'' <- switchPromptly never e'
177 return (b, e'')
181 return (b, e'')
178 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
182 , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
@@ -193,7 +197,7 @@ testCases =
193 e'' = coincidence e'
197 e'' = coincidence e'
194 return (b, e'')
198 return (b, e'')
195 , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
199 , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
196− let e' = flip pushAlways e $ \_ -> onceE e
200+ let e' = flip pushAlways e $ \_ -> headE e
197 e'' = coincidence e'
201 e'' = coincidence e'
198 return (b, e'')
202 return (b, e'')
199 , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
203 , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
@@ -213,17 +217,21 @@ testCases =
213 eCoincidences = coincidence $ fmap (const e') e
217 eCoincidences = coincidence $ fmap (const e') e
214 return (b, eCoincidences)
218 return (b, eCoincidences)
215 , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
219 , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
216− eo <- onceE e
220+ eo <- headE e
217 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
221 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
218 let b' = pull $ sample =<< sample bb
222 let b' = pull $ sample =<< sample bb
219 return (b', e)
223 return (b', e)
220 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
224 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
221 bb <- hold "b" e
225 bb <- hold "b" e
222− bd <- hold never . fmap (const e) =<< onceE e
226+ bd <- hold never . fmap (const e) =<< headE e
223− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
227+ eOuter <- pushAlways sample . fmap (const bb) <$> headE e
224 let eInner = switch bd
228 let eInner = switch bd
225 e' = leftmost [eOuter, eInner]
229 e' = leftmost [eOuter, eInner]
226 return (b, e')
230 return (b, e')
231+ , (,) "mergeIncrementalWithMove" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, PatchDMapWithMove.moveDMapKey LeftTag RightTag), (2, mempty)]) $ \(b, e :: Event t (PatchDMapWithMove (EitherTag () ()) (Const2 () ()))) -> do
232+ x <- holdIncremental (DMap.singleton LeftTag $ void e) $ PatchDMapWithMove.mapPatchDMapWithMove (\(Const2 _) -> void e) <$> e
233+ let e' = mergeIncrementalWithMove x :: Event t (DMap (EitherTag () ()) Identity)
234+ return (b, e')
227 ]
235 ]
228
236
229splitRecombineEvent :: Reflex t => Event t a -> Event t String
237splitRecombineEvent :: Reflex t => Event t a -> Event t String