on develop help sign in
WIP: Add Int-specialized operations
commit32fc50f250f8312829db68bdf70315403e555e25 authorRyan Trinkle <ryan.trinkle@gmail.com> dateTue 1 Aug 2017, 20:25 (9 years ago) parent68d62e4
11 files changed+603−124
reflex.cabal+4 src/Data/FastMutableIntMap.hs+100 src/Reflex/Class.hs+17 src/Reflex/EventWriter.hs+36−1 src/Reflex/Patch/Map.hs+1 src/Reflex/PerformEvent/Base.hs+15 src/Reflex/PostBuild/Base.hs+35 src/Reflex/Requester/Base.hs+101−30 src/Reflex/Spider/Internal.hs+290−93 src/Reflex/Spider/SpiderTimeline.include.hs+2 src/Reflex/TriggerEvent/Base.hs+2
reflex.cabal+4
@@ -49,6 +49,7 @@ library
4949 MemoTrie == 0.6.*,
5050 base >= 4.7 && < 4.11,
5151 bifunctors >= 5.2 && < 5.5,
52+ comonad,
5253 containers == 0.5.*,
5354 data-default >= 0.5 && < 0.8,
5455 dependent-map >= 0.2.4 && < 0.3,
@@ -63,6 +64,7 @@ library
6364 semigroupoids >= 4.0 && < 6,
6465 semigroups >= 0.16 && < 0.19,
6566 stm == 2.4.*,
67+ structs == 0.1,
6668 syb >= 0.5 && < 0.7,
6769 these >= 0.4 && < 0.8,
6870 transformers >= 0.2,
@@ -73,6 +75,7 @@ library
7375 Data.Functor.Misc,
7476 Data.WeakBag,
7577 Data.FastWeakBag,
78+ Data.FastMutableIntMap,
7679 Reflex,
7780 Reflex.Class,
7881 Reflex.EventWriter,
@@ -187,6 +190,7 @@ test-suite hlint
187190 , hlint == 1.9.*
188191 if impl(ghcjs)
189192 buildable: False
193+ buildable: False
190194
191195test-suite EventWriterT
192196 type: exitcode-stdio-1.0
src/Data/FastMutableIntMap.hsnew file+100
@@ -0,0 +1,100 @@
1+{-# LANGUAGE DeriveFunctor #-}
2+{-# LANGUAGE DeriveFoldable #-}
3+{-# LANGUAGE DeriveTraversable #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE TypeFamilies #-}
6+module Data.FastMutableIntMap
7+ ( FastMutableIntMap
8+ , new
9+ , newEmpty
10+ , insert
11+ , isEmpty
12+ , getFrozenAndClear
13+ , size
14+ , applyPatch
15+ , PatchIntMap (..)
16+ , traverseIntMapPatchWithKey
17+ , lookup
18+ , forIntersectionWithImmutable_
19+ , for_
20+ , patchIntMapNewElements
21+ , patchIntMapNewElementsMap
22+ , getDeletions
23+ ) where
24+
25+--TODO: Pure JS version
26+--TODO: Fast copy to FastIntMap
27+--TODO: Fast patch type
28+
29+import Prelude hiding (lookup)
30+import Data.IORef
31+import Data.IntMap.Strict (IntMap)
32+import qualified Data.IntMap.Strict as IntMap
33+import Data.Maybe
34+import Reflex.Patch.Class
35+import Data.Foldable (traverse_)
36+import Control.Monad.IO.Class
37+
38+newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
39+
40+new :: IntMap a -> IO (FastMutableIntMap a)
41+new m = FastMutableIntMap <$> newIORef m
42+
43+newEmpty :: IO (FastMutableIntMap a)
44+newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
45+
46+insert :: FastMutableIntMap a -> Int -> a -> IO ()
47+insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
48+
49+lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
50+lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
51+
52+forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
53+forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
54+ a <- liftIO $ readIORef r
55+ traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
56+
57+for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
58+for_ (FastMutableIntMap r) f = do
59+ a <- liftIO $ readIORef r
60+ traverse_ f a
61+
62+isEmpty :: FastMutableIntMap a -> IO Bool
63+isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
64+
65+size :: FastMutableIntMap a -> IO Int
66+size (FastMutableIntMap r) = IntMap.size <$> readIORef r
67+
68+-- | Make an immutable snapshot of the datastructure and clear it
69+getFrozenAndClear :: FastMutableIntMap a -> IO (IntMap a)
70+getFrozenAndClear (FastMutableIntMap r) = do
71+ result <- readIORef r
72+ writeIORef r IntMap.empty
73+ return result
74+
75+applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
76+applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
77+ v <- readIORef r
78+ writeIORef r $! applyAlways p v
79+ return $ IntMap.intersection v m
80+
81+newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
82+
83+instance Patch (PatchIntMap a) where
84+ type PatchTarget (PatchIntMap a) = IntMap a
85+ apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
86+ let removes = IntMap.filter isNothing p
87+ adds = IntMap.mapMaybe id p
88+ in IntMap.union adds $ v `IntMap.difference` removes
89+
90+traverseIntMapPatchWithKey :: Applicative t => (Int -> a -> t b) -> PatchIntMap a -> t (PatchIntMap b)
91+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
92+
93+patchIntMapNewElements :: PatchIntMap a -> [a]
94+patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
95+
96+patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
97+patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
98+
99+getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
100+getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Class.hs+17
@@ -29,6 +29,7 @@ module Reflex.Class
2929 ( module Reflex.Patch
3030 -- * Primitives
3131 , Reflex (..)
32+ , mergeInt
3233 , coerceBehavior
3334 , coerceEvent
3435 , coerceDynamic
@@ -36,6 +37,7 @@ module Reflex.Class
3637 , MonadHold (..)
3738 -- ** 'fan'-related types
3839 , EventSelector (..)
40+ , EventSelectorInt (..)
3941 -- ** 'Incremental'-related types
4042 -- * Convenience functions
4143 , constDyn
@@ -172,6 +174,8 @@ import qualified Data.Functor.Bind as Bind
172174import Data.Functor.Constant
173175import Data.Functor.Misc
174176import Data.Functor.Plus
177+import Data.IntMap.Strict (IntMap)
178+import qualified Data.IntMap.Strict as IntMap
175179import Data.List.NonEmpty (NonEmpty (..))
176180import Data.Map (Map)
177181import Data.Maybe
@@ -183,6 +187,7 @@ import Data.Traversable
183187import Data.Type.Coercion
184188import Reflex.FunctorMaybe
185189import Reflex.Patch
190+import Data.FastMutableIntMap (PatchIntMap)
186191
187192#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
188193import Reflex.Spider.Internal (EventM, Global, HasSpiderTimeline, SpiderHost (..), SpiderHostFrame (..),
@@ -298,8 +303,14 @@ class ( MonadHold t (PushM t)
298303 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
299304 -- occurrence type
300305 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
306+ mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
307+ fanInt :: Event t (IntMap a) -> EventSelectorInt t a
301308#endif
302309
310+--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
311+mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
312+mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
313+
303314#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
304315instance t ~ SpiderTimeline Global => Reflex t where
305316 newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
@@ -446,6 +457,8 @@ newtype EventSelector t k = EventSelector
446457 select :: forall a. k a -> Event t a
447458 }
448459
460+newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
461+
449462--------------------------------------------------------------------------------
450463-- Instances
451464--------------------------------------------------------------------------------
@@ -1031,6 +1044,7 @@ infixl 4 <@
10311044-- to determine what the best meaning for this class is in such cases.
10321045class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
10331046 runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
1047+ traverseIntMapWithKeyWithAdjust :: (IntMap.Key -> v -> m v') -> IntMap v -> Event t (PatchIntMap v) -> m (IntMap v', Event t (PatchIntMap v'))
10341048 traverseDMapWithKeyWithAdjust :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> m (DMap k v', Event t (PatchDMap k v'))
10351049 traverseDMapWithKeyWithAdjustWithMove :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> m (DMap k v', Event t (PatchDMapWithMove k v'))
10361050
@@ -1038,6 +1052,9 @@ instance MonadAdjust t m => MonadAdjust t (ReaderT r m) where
10381052 runWithReplace a0 a' = do
10391053 r <- ask
10401054 lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
1055+ traverseIntMapWithKeyWithAdjust f dm0 dm' = do
1056+ r <- ask
1057+ lift $ traverseIntMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
10411058 traverseDMapWithKeyWithAdjust f dm0 dm' = do
10421059 r <- ask
10431060 lift $ traverseDMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
src/Reflex/EventWriter.hs+36−1
@@ -37,10 +37,13 @@ import Control.Monad.Ref
3737import Control.Monad.State.Strict
3838import Data.Dependent.Map (DMap, DSum (..))
3939import qualified Data.Dependent.Map as DMap
40−import Data.GADT.Compare (GEq (..), GCompare (..), GOrdering (..))
40+import Data.FastMutableIntMap
4141import Data.Foldable
4242import Data.Functor.Compose
4343import Data.Functor.Misc
44+import Data.GADT.Compare (GEq (..), GCompare (..), GOrdering (..))
45+import Data.IntMap.Strict (IntMap)
46+import qualified Data.IntMap.Strict as IntMap
4447import Data.List.NonEmpty (NonEmpty (..))
4548import Data.Map (Map)
4649import Data.Semigroup
@@ -131,6 +134,7 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
131134
132135instance (Reflex t, MonadAdjust t m, MonadHold t m, Semigroup w, Semigroup w) => MonadAdjust t (EventWriterT t w m) where
133136 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
137+ traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
134138 traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
135139 traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
136140
@@ -157,6 +161,37 @@ runWithReplaceEventWriterTWith f a0 a' = do
157161 tellEvent $ switch $ current request
158162 return (fst result0, fmapCheap fst result')
159163
164+-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
165+sequenceIntMapWithAdjustEventWriterTWith :: forall t m p w v v'. (Reflex t, MonadHold t m, Semigroup w, Semigroup w, Functor p, Patch (p (Event t w)), PatchTarget (p (Event t w)) ~ IntMap (Event t w))
166+ => ( (IntMap.Key -> v -> m (Event t w, v'))
167+ -> IntMap v
168+ -> Event t (p v)
169+ -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
170+ )
171+ -> (p (Event t w) -> [Event t w])
172+ -> (Incremental t (p (Event t w)) -> Event t (IntMap w))
173+ -> (IntMap.Key -> v -> EventWriterT t w m v')
174+ -> IntMap v
175+ -> Event t (p v)
176+ -> EventWriterT t w m (IntMap v', Event t (p v'))
177+sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
178+ let f' :: IntMap.Key -> v -> m (Event t w, v')
179+ f' k v = swap <$> runEventWriterT (f k v)
180+ (children0, children') <- base f' dm0 dm'
181+ let result0 = fmap snd children0
182+ result' = fmapCheap (fmap snd) children'
183+ requests0 :: IntMap (Event t w)
184+ requests0 = fmap fst children0
185+ requests' :: Event t (p (Event t w))
186+ requests' = fmapCheap (fmap fst) children'
187+ childRequestMap :: Incremental t (p (Event t w)) <- holdIncremental requests0 requests'
188+ -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
189+ tellEvent $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
190+ tellEvent $ fforMaybeCheap (mergePatchIncremental childRequestMap) $ \m -> case toList m of
191+ [] -> Nothing
192+ h : t -> Just $ sconcat $ h :| t
193+ return (result0, result')
194+
160195-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
161196sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Semigroup w, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
162197 => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
src/Reflex/Patch/Map.hs+1
@@ -16,6 +16,7 @@ newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
1616
1717instance Ord k => Patch (PatchMap k v) where
1818 type PatchTarget (PatchMap k v) = Map k v
19+ {-# INLINABLE apply #-}
1920 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?
2021 where insertions = Map.mapMaybeWithKey (const id) p
2122 deletions = Map.mapMaybeWithKey (const nothingToJust) p
src/Reflex/PerformEvent/Base.hs+15
@@ -40,6 +40,9 @@ import Data.Coerce
4040import Data.Dependent.Map (DMap)
4141import qualified Data.Dependent.Map as DMap
4242import Data.Dependent.Sum
43+import Data.FastMutableIntMap
44+import Data.IntMap.Strict (IntMap)
45+import qualified Data.IntMap.Strict as IntMap
4346
4447-- | A function that fires events for the given 'EventTrigger's and then runs
4548-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
@@ -76,6 +79,7 @@ instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT
7679 result0 <- lift a0
7780 result' <- requestingIdentity a'
7881 return (result0, result')
82+ traverseIntMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseIntMapWithKeyWithAdjustRequesterTWith (defaultAdjustIntBase traverseIntMapPatchWithKey) patchIntMapNewElementsMap mergeIntIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
7983 traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
8084 traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
8185
@@ -90,6 +94,17 @@ defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
9094 result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
9195 return (result0, result')
9296
97+defaultAdjustIntBase :: forall t v v2 p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
98+ => ((IntMap.Key -> v -> HostFrame t v2) -> p v -> HostFrame t (p v2))
99+ -> (IntMap.Key -> v -> HostFrame t v2)
100+ -> IntMap v
101+ -> Event t (p v)
102+ -> RequesterT t (HostFrame t) Identity (HostFrame t) (IntMap v2, Event t (p v2))
103+defaultAdjustIntBase traversePatchWithKey f' dm0 dm' = do
104+ result0 <- lift $ IntMap.traverseWithKey f' dm0
105+ result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
106+ return (result0, result')
107+
93108instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
94109 {-# INLINABLE newEventWithTrigger #-}
95110 newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
src/Reflex/PostBuild/Base.hs+35
@@ -17,6 +17,9 @@
1717module Reflex.PostBuild.Base
1818 ( PostBuildT (..)
1919 , runPostBuildT
20+ -- * Internal
21+ , mapIntMapWithAdjustImpl
22+ , mapDMapWithAdjustImpl
2023 ) where
2124
2225import Reflex.Class
@@ -34,6 +37,8 @@ import Control.Monad.Trans.Control
3437import Data.Dependent.Map (DMap)
3538import qualified Data.Dependent.Map as DMap
3639import Data.Functor.Compose
40+import Data.IntMap.Strict (IntMap)
41+import qualified Data.IntMap.Strict as IntMap
3742
3843-- | Provides a basic implementation of 'PostBuild'.
3944newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
@@ -112,9 +117,39 @@ instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m
112117 rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
113118 let voidResult' = fmapCheap (\_ -> ()) result'
114119 return result
120+ {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
121+ traverseIntMapWithKeyWithAdjust = mapIntMapWithAdjustImpl traverseIntMapWithKeyWithAdjust
122+ {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
115123 traverseDMapWithKeyWithAdjust = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap
116124 traverseDMapWithKeyWithAdjustWithMove = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove
117125
126+{-# INLINABLE mapIntMapWithAdjustImpl #-}
127+mapIntMapWithAdjustImpl :: forall t m v v' p. (Reflex t, MonadFix m, MonadHold t m, Functor p)
128+ => ( (IntMap.Key -> ((Bool, Event t ()), v) -> m v')
129+ -> IntMap ((Bool, Event t ()), v)
130+ -> Event t (p ((Bool, Event t ()), v))
131+ -> m (IntMap v', Event t (p v'))
132+ )
133+ -> (IntMap.Key -> v -> PostBuildT t m v')
134+ -> IntMap v
135+ -> Event t (p v)
136+ -> PostBuildT t m (IntMap v', Event t (p v'))
137+mapIntMapWithAdjustImpl base f dm0 dm' = do
138+ postBuild <- getPostBuild
139+ let loweredDm0 = fmap ((,) (False, postBuild)) dm0
140+ f' :: IntMap.Key -> ((Bool, Event t ()), v) -> m v'
141+ f' k ((shouldHeadE, e), v) = do
142+ eOnce <- if shouldHeadE
143+ then headE e --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
144+ else return e
145+ runPostBuildT (f k v) eOnce
146+ lift $ do
147+ rec (result0, result') <- base f' loweredDm0 loweredDm'
148+ let voidResult' = fmapCheap (\_ -> ()) result'
149+ let loweredDm' = ffor dm' $ fmap ((,) (True, voidResult'))
150+ return (result0, result')
151+
152+{-# INLINABLE mapDMapWithAdjustImpl #-}
118153mapDMapWithAdjustImpl :: forall t m k v v' p. (Reflex t, MonadFix m, MonadHold t m)
119154 => ( (forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a))
120155 -> DMap k (Compose ((,) (Bool, Event t ())) v)
src/Reflex/Requester/Base.hs+101−30
@@ -22,6 +22,7 @@ module Reflex.Requester.Base
2222 ( RequesterT (..)
2323 , runRequesterT
2424 , runWithReplaceRequesterTWith
25+ , traverseIntMapWithKeyWithAdjustRequesterTWith
2526 , traverseDMapWithKeyWithAdjustRequesterTWith
2627 , RequesterData
2728 , traverseRequesterData
@@ -42,52 +43,84 @@ import Control.Monad.Ref
4243import Control.Monad.State.Strict
4344import Data.Bits
4445import Data.Coerce
45−import Data.Dependent.Map (DMap, DSum (..))
46+import Data.Dependent.Map (DMap)
4647import qualified Data.Dependent.Map as DMap
4748import qualified Data.Some as Some
49+import Data.FastMutableIntMap
4850import Data.Functor.Compose
4951import Data.Functor.Misc
50−import Data.Int
52+import Data.IntMap.Strict (IntMap)
53+import qualified Data.IntMap.Strict as IntMap
5154import Data.Map (Map)
52−import Data.Monoid
55+import Data.Monoid ((<>))
5356import Data.Unique.Tag
5457import Data.Some (Some)
5558import Data.Tuple
5659import Data.Type.Equality
60+import Data.Proxy
5761
62+import GHC.Exts (Any)
5863import Unsafe.Coerce
5964
60−newtype RequesterData f = RequesterData { unRequesterData :: DMap MyTag (Entry f) }
65+newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
66+
67+newtype RequesterData f = RequesterData (TagMap (Entry f))
68+
69+traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g)
70+traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m
71+ where
72+ g :: Int -> Any -> t Any
73+ g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v)
6174
6275-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
6376traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response)
64−traverseRequesterData f (RequesterData m) = RequesterData <$> DMap.traverseWithKey go m --TODO: reverse
77+traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order
6578 where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
6679 go k (Entry request) = Entry <$> case myKeyType k of
6780 MyTagType_Single -> f request
6881 MyTagType_Multi -> traverseRequesterData f request
6982 MyTagType_Multi2 -> traverse (traverseRequesterData f) request
83+ MyTagType_Multi3 -> traverse (traverseRequesterData f) request
7084
7185data MyTagType :: * -> * where
7286 MyTagType_Single :: MyTagType (Single a)
7387 MyTagType_Multi :: MyTagType Multi
7488 MyTagType_Multi2 :: MyTagType (Multi2 k)
89+ MyTagType_Multi3 :: MyTagType Multi3
7590
7691myKeyType :: MyTag x -> MyTagType x
7792myKeyType (MyTag k) = case k .&. 0x3 of
7893 0x0 -> unsafeCoerce MyTagType_Single
7994 0x1 -> unsafeCoerce MyTagType_Multi
8095 0x2 -> unsafeCoerce MyTagType_Multi2
96+ 0x3 -> unsafeCoerce MyTagType_Multi3
8197 t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
8298
8399data Single a
84100data Multi
85101data Multi2 (k :: * -> *)
102+data Multi3
103+
104+class MyTagTypeOffset x where
105+ myTagTypeOffset :: proxy x -> Int
106+
107+instance MyTagTypeOffset (Single a) where
108+ myTagTypeOffset _ = 0x0
109+
110+instance MyTagTypeOffset Multi where
111+ myTagTypeOffset _ = 0x1
112+
113+instance MyTagTypeOffset (Multi2 k) where
114+ myTagTypeOffset _ = 0x2
115+
116+instance MyTagTypeOffset Multi3 where
117+ myTagTypeOffset _ = 0x3
86118
87119type family EntryContents request a where
88120 EntryContents request (Single a) = request a
89121 EntryContents request Multi = RequesterData request
90122 EntryContents request (Multi2 k) = Map (Some k) (RequesterData request)
123+ EntryContents request Multi3 = IntMap (RequesterData request)
91124
92125newtype Entry request x = Entry { unEntry :: EntryContents request x }
93126
@@ -101,9 +134,9 @@ multiEntry = Entry
101134
102135-- | We use a hack here to pretend we have x ~ request a; we don't want to use a GADT, because GADTs (even with zero-size existential contexts) can't be newtypes
103136-- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another
104−newtype MyTag x = MyTag Int32 deriving (Show, Eq, Ord, Enum)
137+newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum)
105138
106−newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int32 deriving (Show, Eq, Ord, Enum)
139+newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum)
107140
108141{-# INLINE castMyTagWrap #-}
109142castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
@@ -135,13 +168,13 @@ instance GCompare (MyTagWrap f) where
135168 EQ -> unsafeCoerce GEQ
136169 GT -> GGT
137170
138−data RequesterState t request = RequesterState
139− { _requesterState_nextMyTag :: {-# UNPACK #-} !Int32 -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
140− , _requesterState_requests :: ![DSum (MyTagWrap request) (Event t)]
171+data RequesterState t (request :: * -> *) = RequesterState
172+ { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
173+ , _requesterState_requests :: ![(Int, Event t Any)]
141174 }
142175
143176-- | A basic implementation of 'Requester'.
144−newtype RequesterT t request response m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelector t (MyTagWrap response)) m) a }
177+newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a }
145178 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
146179-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
147180#if MIN_VERSION_base(4,9,1)
@@ -154,14 +187,6 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
154187deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
155188deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
156189
157−{-# INLINE wrapMyTags #-}
158−wrapMyTags :: DMap MyTag (Entry f) -> DMap (MyTagWrap f) Identity
159−wrapMyTags = unsafeCoerce
160−
161−{-# INLINE unwrapMyTags #-}
162−unwrapMyTags :: DMap (MyTagWrap f) Identity -> DMap MyTag (Entry f)
163−unwrapMyTags = unsafeCoerce
164−
165190-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
166191-- requests are made, and responses should be provided in the input 'Event'.
167192-- The 'Tag' keys will be used to return the responses to the same place the
@@ -172,9 +197,9 @@ runRequesterT :: (Reflex t, Monad m)
172197 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
173198 -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
174199runRequesterT (RequesterT a) responses = do
175− (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fan $
176− fmapCheap (wrapMyTags . unRequesterData) responses
177− return (result, fmapCheap (RequesterData . unwrapMyTags) $ merge $ DMap.fromDistinctAscList $ _requesterState_requests s)
200+ (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $
201+ coerceEvent responses
202+ return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
178203
179204instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
180205 type Request (RequesterT t request response m) = request
@@ -183,22 +208,22 @@ instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) wh
183208 requesting_ = void . tagRequest . fmapCheap singleEntry
184209
185210{-# INLINE tagRequest #-}
186−tagRequest :: Monad m => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
211+tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
187212tagRequest req = do
188213 old <- RequesterT get
189− let n = _requesterState_nextMyTag old
214+ let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x)
190215 t = MyTagWrap n
191216 RequesterT $ put $ RequesterState
192− { _requesterState_nextMyTag = n - 0x4
193− , _requesterState_requests = (t :=> req) : _requesterState_requests old
217+ { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4
218+ , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old
194219 }
195220 return t
196221
197222{-# INLINE responseFromTag #-}
198223responseFromTag :: (Monad m, Reflex t) => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
199−responseFromTag t = do
200− responses :: EventSelector t (MyTagWrap response) <- RequesterT ask
201− return $ select responses t
224+responseFromTag (MyTagWrap t) = do
225+ responses :: EventSelectorInt t Any <- RequesterT ask
226+ return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
202227
203228instance MonadTrans (RequesterT t request response) where
204229 lift = RequesterT . lift . lift
@@ -225,12 +250,15 @@ instance MonadReader r m => MonadReader r (RequesterT t request response m) wher
225250
226251instance (Reflex t, MonadAdjust t m, MonadHold t m, MonadFix m) => MonadAdjust t (RequesterT t request response m) where
227252 runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
253+ traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
254+ {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
228255 traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
229256 traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
230257
231−requesting' :: (Monad m, Reflex t) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
258+requesting' :: (MyTagTypeOffset x, Monad m, Reflex t) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
232259requesting' = responseFromTag . castMyTagWrap <=< tagRequest
233260
261+{-# INLINABLE runWithReplaceRequesterTWith #-}
234262runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
235263 , MonadFix m
236264 )
@@ -244,6 +272,49 @@ runWithReplaceRequesterTWith f a0 a' = do
244272 requests <- holdDyn requests0 $ fmapCheap snd v'
245273 return (result0, fmapCheap fst v')
246274
275+{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-}
276+traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p.
277+ ( Reflex t
278+ , MonadHold t m
279+ , PatchTarget (p (Event t (RequesterData request))) ~ IntMap (Event t (RequesterData request))
280+ , Patch (p (Event t (RequesterData request)))
281+ , Functor p
282+ , MonadFix m
283+ )
284+ => ( (IntMap.Key -> v -> m (Event t (RequesterData request), v'))
285+ -> IntMap v
286+ -> Event t (p v)
287+ -> RequesterT t request response m (IntMap (Event t (RequesterData request), v'), Event t (p (Event t (RequesterData request), v')))
288+ )
289+ -> (p (Event t (RequesterData request)) -> IntMap (Event t (RequesterData request)))
290+ -> (Incremental t (p (Event t (RequesterData request))) -> Event t (IntMap (RequesterData request)))
291+ -> (IntMap.Key -> v -> RequesterT t request response m v')
292+ -> IntMap v
293+ -> Event t (p v)
294+ -> RequesterT t request response m (IntMap v', Event t (p v'))
295+traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
296+ rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
297+ let responses :: EventSelectorInt t (RequesterData response)
298+ responses = fanInt $ fmapCheap unpack response
299+ unpack :: Entry response Multi3 -> IntMap (RequesterData response)
300+ unpack = unEntry
301+ pack :: IntMap (RequesterData request) -> Entry request Multi3
302+ pack = Entry
303+ f' :: IntMap.Key -> v -> m (Event t (RequesterData request), v')
304+ f' k v = fmap swap $ runRequesterT (f k v) $ selectInt responses k
305+ (children0, children') <- base f' dm0 dm'
306+ let result0 = fmap snd children0
307+ result' = fforCheap children' $ fmap snd
308+ requests0 :: IntMap (Event t (RequesterData request))
309+ requests0 = fmap fst children0
310+ requests' :: Event t (p (Event t (RequesterData request)))
311+ requests' = fforCheap children' $ fmap fst
312+ promptRequests :: Event t (IntMap (RequesterData request))
313+ promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
314+ requests <- holdIncremental requests0 requests'
315+ return (result0, result')
316+
317+{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-}
247318traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
248319 ( GCompare k
249320 , Reflex t
src/Reflex/Spider/Internal.hs+290−93
@@ -57,6 +57,8 @@ import Data.Proxy
5757import Data.These
5858import Data.Traversable
5959import Data.Word
60+import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..))
61+import qualified Data.FastMutableIntMap as FastMutableIntMap
6062import GHC.Exts
6163import GHC.IORef (IORef (..))
6264import GHC.Stack
@@ -246,10 +248,10 @@ pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a ->
246248pushCheap !f e = Event $ \sub -> do
247249 (subscription, occ) <- subscribeAndRead e $ sub
248250 { subscriberPropagate = \a -> do
249− mb <- runComputeM $ f a
251+ mb <- f a
250252 mapM_ (subscriberPropagate sub) mb
251253 }
252− occ' <- join <$> mapM (runComputeM . f) occ
254+ occ' <- join <$> mapM f occ
253255 return (subscription, occ')
254256
255257data CacheSubscribed x a
@@ -762,6 +764,7 @@ data EventEnv x
762764 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
763765 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
764766 , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
767+ , eventEnvIntClears :: !(IORef [SomeIntClear])
765768 , eventEnvRootClears :: !(IORef [SomeRootClear])
766769 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
767770 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
@@ -846,6 +849,14 @@ instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
846849scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
847850scheduleClear r = defer $ SomeClear r
848851
852+instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
853+ {-# INLINE getDeferralQueue #-}
854+ getDeferralQueue = asksEventEnv eventEnvIntClears
855+
856+{-# INLINE scheduleIntClear #-}
857+scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
858+scheduleIntClear r = defer $ SomeIntClear r
859+
849860instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
850861 {-# INLINE getDeferralQueue #-}
851862 getDeferralQueue = asksEventEnv eventEnvRootClears
@@ -981,21 +992,6 @@ newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
981992-- EventM can do everything BehaviorM can, plus create holds
982993newtype EventM x a = EventM { unEventM :: IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException) -- The environment should be Nothing if we are not in a frame, and Just if we are - in which case it is a list of assignments to be done after the frame is over
983994
984−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x], IORef [SomeDynInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
985−
986−instance Defer (SomeHoldInit x) (ComputeM x) where
987− getDeferralQueue = ComputeM $ asks fst
988−
989−instance Defer (SomeDynInit x) (ComputeM x) where
990− getDeferralQueue = ComputeM $ asks snd
991−
992−{-# INLINABLE runComputeM #-}
993−runComputeM :: (Defer (SomeHoldInit x) m, Defer (SomeDynInit x) m) => ComputeM x a -> m a
994−runComputeM (ComputeM a) = do
995− holds <- getDeferralQueue
996− dyns <- getDeferralQueue
997− liftIO $ runReaderT a (holds, dyns)
998−
999995newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
1000996
1001997data MergeSubscribedParentWithMove x k a = MergeSubscribedParentWithMove
@@ -1239,9 +1235,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
12391235 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
12401236 EQ -> a
12411237
1242−data SomeClear = forall a. SomeClear (IORef (Maybe a))
1238+data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1239+
1240+data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
12431241
1244−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1242+data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
12451243
12461244data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
12471245
@@ -1377,6 +1375,7 @@ type CanSubscribe x m =
13771375 , HasCurrentHeight x m
13781376 , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
13791377 , Defer SomeClear m -- Wiping out event firings
1378+ , Defer SomeIntClear m
13801379 , Defer (SomeAssignment x) m -- Updating state
13811380 , Defer (SomeResetCoincidence x) m
13821381 , m ~ EventM x
@@ -1437,6 +1436,68 @@ cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribe
14371436subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
14381437subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
14391438
1439+newtype EventSelectorInt x a = EventSelectorInt { selectInt :: Int -> Event x a }
1440+
1441+data FanInt x a = FanInt
1442+ { _fanInt_subscribers :: {-# UNPACK #-} !(FastMutableIntMap (FastWeakBag (Subscriber x a))) --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1443+ , _fanInt_subscriptionRef :: {-# UNPACK #-} !(IORef (EventSubscription x)) -- This should have a valid subscription iff subscribers is non-empty
1444+ , _fanInt_occRef :: {-# UNPACK #-} !(IORef (IntMap a))
1445+ }
1446+
1447+newFanInt :: IO (FanInt x a)
1448+newFanInt = do
1449+ subscribers <- FastMutableIntMap.newEmpty --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1450+ subscriptionRef <- newIORef $ error "fanInt: no subscription"
1451+ occRef <- newIORef $ error "fanInt: no occurrence"
1452+ return $ FanInt
1453+ { _fanInt_subscribers = subscribers
1454+ , _fanInt_subscriptionRef = subscriptionRef
1455+ , _fanInt_occRef = occRef
1456+ }
1457+
1458+{-# NOINLINE unsafeNewFanInt #-}
1459+unsafeNewFanInt :: b -> FanInt x a
1460+unsafeNewFanInt _ = unsafePerformIO newFanInt
1461+
1462+{-# NOINLINE fanInt #-}
1463+fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1464+fanInt p =
1465+ let self = unsafeNewFanInt p
1466+ in EventSelectorInt $ \k -> Event $ \sub -> do
1467+ isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1468+ when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1469+ (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1470+ { subscriberPropagate = \m -> do
1471+ liftIO $ writeIORef (_fanInt_occRef self) m
1472+ scheduleIntClear $ _fanInt_occRef self
1473+ FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do
1474+ FastWeakBag.traverse b $ \s -> do
1475+ subscriberPropagate s v
1476+ , subscriberInvalidateHeight = \old -> do
1477+ FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1478+ FastWeakBag.traverse b $ \s -> do
1479+ subscriberInvalidateHeight s old
1480+ , subscriberRecalculateHeight = \new -> do
1481+ FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1482+ FastWeakBag.traverse b $ \s -> do
1483+ subscriberRecalculateHeight s new
1484+ }
1485+ liftIO $ do
1486+ writeIORef (_fanInt_subscriptionRef self) subscription
1487+ writeIORef (_fanInt_occRef self) $ fromMaybe IntMap.empty parentOcc
1488+ scheduleIntClear $ _fanInt_occRef self
1489+ liftIO $ do
1490+ b <- FastMutableIntMap.lookup (_fanInt_subscribers self) k >>= \case
1491+ Nothing -> do
1492+ b <- FastWeakBag.empty
1493+ FastMutableIntMap.insert (_fanInt_subscribers self) k b
1494+ return b
1495+ Just b -> return b
1496+ t <- liftIO $ FastWeakBag.insert sub b
1497+ currentOcc <- readIORef (_fanInt_occRef self)
1498+ (EventSubscription _ (EventSubscribed !heightRef _)) <- readIORef (_fanInt_subscriptionRef self)
1499+ return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny $! _fanInt_subscriptionRef self, IntMap.lookup k currentOcc)
1500+
14401501{-# INLINABLE getFanSubscribed #-}
14411502getFanSubscribed :: (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x, GCompare k) => k a -> Fan x k -> Subscriber x a -> m (WeakBagTicket, FanSubscribed x k, Maybe a)
14421503getFanSubscribed k f sub = do
@@ -1616,7 +1677,7 @@ mergeWithMove d = cacheEvent (mergeCheapWithMove d)
16161677
16171678{-# INLINE [1] mergeCheap #-}
16181679mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1619−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1680+mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
16201681 where
16211682 updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
16221683 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
@@ -1651,7 +1712,7 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
16511712
16521713{-# INLINE [1] mergeCheapWithMove #-}
16531714mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1654−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1715+mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
16551716 where
16561717 updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
16571718 updateMe subscriber heightBagRef oldParents p = do
@@ -1709,78 +1770,108 @@ type MergeDestroyFunc k s
17091770 = DMap k s
17101771 -> IO ()
17111772
1773+data Merge x k s = Merge
1774+ { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1775+ , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1776+ , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1777+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1778+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1779+ }
1780+
1781+invalidateMergeHeight :: Merge x k s -> IO ()
1782+invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1783+
1784+invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1785+invalidateMergeHeight' heightRef sub = do
1786+ oldHeight <- readIORef heightRef
1787+ when (oldHeight /= invalidHeight) $ do -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1788+ writeIORef heightRef $! invalidHeight
1789+ subscriberInvalidateHeight sub oldHeight
1790+
1791+
1792+revalidateMergeHeight :: Merge x k s -> IO ()
1793+revalidateMergeHeight m = do
1794+ currentHeight <- readIORef $ _merge_heightRef m
1795+ when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1796+ heights <- readIORef $ _merge_heightBagRef m
1797+ parents <- readIORef $ _merge_parentsRef m
1798+ -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1799+ case heightBagSize heights `compare` DMap.size parents of
1800+ LT -> return ()
1801+ EQ -> do
1802+ let height = succHeight $ heightBagMax heights
1803+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1804+ writeIORef (_merge_heightRef m) $! height
1805+ subscriberRecalculateHeight (_merge_sub m) height
1806+ GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1807+
1808+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1809+scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1810+ vals <- liftIO $ readIORef $ _merge_accumRef m
1811+ liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
1812+ --TODO: Assert that m is not empty
1813+ subscriberPropagate (_merge_sub m) vals
1814+
1815+mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1816+mergeSubscriber m getKey = Subscriber
1817+ { subscriberPropagate = \a -> do
1818+ oldM <- liftIO $ readIORef $ _merge_accumRef m
1819+ k <- getKey
1820+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1821+ tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1822+ liftIO $ writeIORef (_merge_accumRef m) $! newM
1823+ when (DMap.null oldM) $ do -- Only schedule the firing once
1824+ height <- liftIO $ readIORef $ _merge_heightRef m
1825+ --TODO: assertions about height
1826+ currentHeight <- getCurrentHeight
1827+ when (height <= currentHeight) $ do
1828+ if height /= invalidHeight
1829+ then do
1830+ myStack <- liftIO $ whoCreatedIORef undefined --TODO
1831+ error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1832+ else liftIO $ do
1833+#ifdef DEBUG_CYCLES
1834+ nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1835+ stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1836+ let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1837+#else
1838+ let cycleInfo = ""
1839+#endif
1840+ error $ "Causality loop found" <> cycleInfo
1841+ scheduleMergeSelf m height
1842+ , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1843+ modifyIORef' (_merge_heightBagRef m) $ heightBagRemove old
1844+ invalidateMergeHeight m
1845+ , subscriberRecalculateHeight = \new -> do
1846+ modifyIORef' (_merge_heightBagRef m) $ heightBagAdd new
1847+ revalidateMergeHeight m
1848+ }
1849+
1850+--TODO: Be able to run as much of this as possible promptly
1851+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1852+updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1853+ where updateMe = do
1854+ oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1855+ (subscriptionsToKill, newParents) <- updateFunc (mergeSubscriber m) (_merge_heightBagRef m) oldParents p
1856+ liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1857+ return subscriptionsToKill
1858+
17121859{-# INLINE mergeCheap' #-}
1713−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> (DMap k s -> Int) -> Dynamic x p -> Event x (DMap k Identity)
1714−mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub -> do
1860+mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k Identity)
1861+mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
17151862 initialParents <- readBehaviorUntracked $ dynamicCurrent d
17161863 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
17171864 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
17181865 heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
17191866 parentsRef :: IORef (DMap k s) <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1720− let scheduleSelf height = scheduleMerge' height heightRef $ do
1721− m <- liftIO $ readIORef accumRef
1722− liftIO $ writeIORef accumRef $! DMap.empty
1723− --TODO: Assert that m is not empty
1724− subscriberPropagate sub m
1725− invalidateMyHeight = do
1726− oldHeight <- readIORef heightRef
1727− when (oldHeight /= invalidHeight) $ do -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1728− writeIORef heightRef $! invalidHeight
1729− subscriberInvalidateHeight sub oldHeight
1730− revalidateMyHeight = do
1731− currentHeight <- readIORef heightRef
1732− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1733− heights <- readIORef heightBagRef
1734− parents <- readIORef parentsRef
1735− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1736− case heightBagSize heights `compare` numParents parents of
1737− LT -> return ()
1738− EQ -> do
1739− let height = succHeight $ heightBagMax heights
1740− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1741− writeIORef heightRef $! height
1742− subscriberRecalculateHeight sub height
1743− GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (numParents parents) <> ") for Merge"
1744− subscriber :: forall a. EventM x (k a) -> Subscriber x a
1745− subscriber getKey = Subscriber
1746− { subscriberPropagate = \a -> do
1747− oldM <- liftIO $ readIORef accumRef
1748− k <- getKey
1749− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1750− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1751− liftIO $ writeIORef accumRef $! newM
1752− when (DMap.null oldM) $ do -- Only schedule the firing once
1753− height <- liftIO $ readIORef heightRef
1754− --TODO: assertions about height
1755− currentHeight <- getCurrentHeight
1756− when (height <= currentHeight) $ do
1757− if height /= invalidHeight
1758− then do
1759− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1760− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1761− else liftIO $ do
1762−#ifdef DEBUG_CYCLES
1763− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1764− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1765− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1766−#else
1767− let cycleInfo = ""
1768−#endif
1769− error $ "Causality loop found" <> cycleInfo
1770− scheduleSelf height
1771− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1772− modifyIORef' heightBagRef $ heightBagRemove old
1773− invalidateMyHeight
1774− , subscriberRecalculateHeight = \new -> do
1775− modifyIORef' heightBagRef $ heightBagAdd new
1776− revalidateMyHeight
1867+ let m = Merge
1868+ { _merge_parentsRef = parentsRef
1869+ , _merge_heightBagRef = heightBagRef
1870+ , _merge_heightRef = heightRef
1871+ , _merge_sub = sub
1872+ , _merge_accumRef = accumRef
17771873 }
1778− let updateMe x = do
1779− oldParents <- liftIO $ readIORef parentsRef
1780− (subscriptionsToKill, newParents) <- updateFunc subscriber heightBagRef oldParents x
1781− liftIO $ writeIORef parentsRef $! newParents
1782− return subscriptionsToKill
1783− (dm, heights, initialParentState) <- getInitialSubscribers initialParents subscriber
1874+ (dm, heights, initialParentState) <- getInitialSubscribers initialParents $ mergeSubscriber m
17841875 let myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
17851876 myHeight = if invalidHeight `elem` heights
17861877 then invalidHeight
@@ -1790,9 +1881,7 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
17901881 then (if DMap.null dm then Nothing else Just dm, DMap.empty)
17911882 else (Nothing, dm)
17921883 unless (DMap.null accum) $ do
1793− scheduleSelf myHeight
1794− occRef <- liftIO $ newIORef occ
1795− when (isJust occ) $ scheduleClear occRef
1884+ scheduleMergeSelf m myHeight
17961885 liftIO $ writeIORef accumRef $! accum
17971886 liftIO $ writeIORef heightRef $! myHeight
17981887 liftIO $ writeIORef heightBagRef $! myHeightBag
@@ -1802,12 +1891,12 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
18021891 let s = Subscriber
18031892 { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
18041893 tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1805− defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1894+ defer $ updateMerge m updateFunc a
18061895 , subscriberInvalidateHeight = \_ -> return ()
18071896 , subscriberRecalculateHeight = \_ -> return ()
18081897 }
18091898 (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1810− forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1899+ forM_ change $ \c -> defer $ updateMerge m updateFunc c
18111900 -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
18121901 -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
18131902 -- its subscription.
@@ -1817,6 +1906,106 @@ mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub
18171906 , occ
18181907 )
18191908
1909+mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1910+mergeInt = cacheEvent . mergeIntCheap
1911+
1912+{-# INLINABLE mergeIntCheap #-}
1913+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1914+mergeIntCheap d = Event $ \sub -> do
1915+ initialParents <- readBehaviorUntracked $ dynamicCurrent d
1916+ accum <- liftIO $ FastMutableIntMap.newEmpty
1917+ heightRef <- liftIO $ newIORef zeroHeight
1918+ heightBagRef <- liftIO $ newIORef heightBagEmpty
1919+ parents <- liftIO $ FastMutableIntMap.newEmpty
1920+ let scheduleSelf = do
1921+ height <- liftIO $ readIORef $ heightRef
1922+ scheduleMerge' height heightRef $ do
1923+ vals <- liftIO $ FastMutableIntMap.getFrozenAndClear accum
1924+ subscriberPropagate sub vals
1925+ invalidateMyHeight = do
1926+ invalidateMergeHeight' heightRef sub
1927+ recalculateMyHeight = do
1928+ currentHeight <- readIORef heightRef
1929+ when (currentHeight == invalidHeight) $ do --TODO: This will almost always be true; can we get rid of this check and just proceed to the next one always?
1930+ heights <- readIORef heightBagRef
1931+ numParents <- FastMutableIntMap.size parents
1932+ case heightBagSize heights `compare` numParents of
1933+ LT -> return ()
1934+ EQ -> do
1935+ let height = succHeight $ heightBagMax heights
1936+ when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1937+ writeIORef heightRef $! height
1938+ subscriberRecalculateHeight sub height
1939+ GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show numParents <> ") for Merge"
1940+ mySubscriber k = Subscriber
1941+ { subscriberPropagate = \a -> do
1942+ wasEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1943+ liftIO $ FastMutableIntMap.insert accum k a
1944+ when wasEmpty scheduleSelf
1945+ , subscriberInvalidateHeight = \old -> do
1946+ modifyIORef' heightBagRef $ heightBagRemove old
1947+ invalidateMyHeight
1948+ , subscriberRecalculateHeight = \new -> do
1949+ modifyIORef' heightBagRef $ heightBagAdd new
1950+ recalculateMyHeight
1951+ }
1952+ forM_ (IntMap.toList initialParents) $ \(k, p) -> do
1953+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead p $ mySubscriber k
1954+ liftIO $ do
1955+ forM_ parentOcc $ FastMutableIntMap.insert accum k
1956+ FastMutableIntMap.insert parents k subscription
1957+ height <- getEventSubscribedHeight parentSubd
1958+ if height == invalidHeight
1959+ then writeIORef heightRef invalidHeight
1960+ else do
1961+ modifyIORef' heightBagRef $ heightBagAdd height
1962+ modifyIORef' heightRef $ \oldHeight ->
1963+ if oldHeight == invalidHeight
1964+ then invalidHeight
1965+ else max (succHeight height) oldHeight
1966+ myHeight <- liftIO $ readIORef heightRef
1967+ currentHeight <- getCurrentHeight
1968+ isEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1969+ occ <- if currentHeight >= myHeight -- If we should have fired by now
1970+ then if isEmpty
1971+ then return Nothing
1972+ else liftIO $ Just <$> FastMutableIntMap.getFrozenAndClear accum
1973+ else do when (not isEmpty) scheduleSelf -- We have things accumulated, but we shouldn't have fired them yet
1974+ return Nothing
1975+ changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1976+ defer $ SomeMergeInit $ do
1977+ let updateMe a = SomeMergeUpdate u invalidateMyHeight recalculateMyHeight
1978+ where
1979+ u = do
1980+ let f k newParent = do
1981+ subscription@(EventSubscription _ subd) <- subscribe newParent $ mySubscriber k
1982+ newParentHeight <- liftIO $ getEventSubscribedHeight subd
1983+ liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1984+ return subscription
1985+ newSubscriptions <- FastMutableIntMap.traverseIntMapPatchWithKey f a
1986+ oldParents <- liftIO $ FastMutableIntMap.applyPatch parents newSubscriptions
1987+ liftIO $ for_ oldParents $ \oldParent -> do
1988+ oldParentHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed oldParent
1989+ modifyIORef' heightBagRef $ heightBagRemove oldParentHeight
1990+ return $ IntMap.elems oldParents
1991+ let s = Subscriber
1992+ { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1993+ tracePropagate (Proxy :: Proxy x) $ "SubscriberMergeInt/Change"
1994+ defer $ updateMe a
1995+ , subscriberInvalidateHeight = \_ -> return ()
1996+ , subscriberRecalculateHeight = \_ -> return ()
1997+ }
1998+ (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1999+ forM_ change $ \c -> defer $ updateMe c
2000+ -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
2001+ -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
2002+ -- its subscription.
2003+ liftIO $ writeIORef changeSubdRef (s, changeSubscription)
2004+ let unsubscribeAll = traverse_ unsubscribe =<< FastMutableIntMap.getFrozenAndClear parents
2005+ return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parents, changeSubdRef)
2006+ , occ
2007+ )
2008+
18202009newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
18212010
18222011fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
@@ -1856,13 +2045,14 @@ newEventEnv = do
18562045 mergeInitRef <- newIORef []
18572046 heightRef <- newIORef zeroHeight
18582047 toClearRef <- newIORef []
2048+ toClearIntRef <- newIORef []
18592049 toClearRootRef <- newIORef []
18602050 coincidenceInfosRef <- newIORef []
18612051 delayedRef <- newIORef IntMap.empty
1862− return $ EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
2052+ return $ EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef
18632053
18642054clearEventEnv :: EventEnv x -> IO ()
1865−clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
2055+clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
18662056 writeIORef toAssignRef []
18672057 writeIORef holdInitRef []
18682058 writeIORef dynInitRef []
@@ -1870,6 +2060,7 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
18702060 writeIORef mergeInitRef []
18712061 writeIORef heightRef zeroHeight
18722062 writeIORef toClearRef []
2063+ writeIORef toClearIntRef []
18732064 writeIORef toClearRootRef []
18742065 writeIORef coincidenceInfosRef []
18752066 writeIORef delayedRef IntMap.empty
@@ -1885,6 +2076,8 @@ runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
18852076 result <- runEventM go
18862077 toClear <- readIORef $ eventEnvClears env
18872078 forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2079+ toClearInt <- readIORef $ eventEnvIntClears env
2080+ forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
18882081 toClearRoot <- readIORef $ eventEnvRootClears env
18892082 forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
18902083 toAssign <- readIORef $ eventEnvAssignments env
@@ -2079,6 +2272,8 @@ withSpiderTimeline k = do
20792272
20802273newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20812274
2275+type ComputeM = EventM
2276+
20822277newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
20832278
20842279#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
@@ -2131,6 +2326,8 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
21312326 eventCoercion Coercion = Coercion
21322327 behaviorCoercion Coercion = Coercion
21332328 dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2329+ mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2330+ fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
21342331#endif
21352332
21362333data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
src/Reflex/Spider/SpiderTimeline.include.hs+2
@@ -30,6 +30,8 @@ instance (
3030instance (
3131#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
3232 x ~ Global
33+#else
34+ HasSpiderTimeline x
3335#endif
3436 ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
3537 {-# INLINABLE sample #-}
src/Reflex/TriggerEvent/Base.hs+2
@@ -118,6 +118,8 @@ instance MonadHold t m => MonadHold t (TriggerEventT t m) where
118118instance MonadAdjust t m => MonadAdjust t (TriggerEventT t m) where
119119 {-# INLINABLE runWithReplace #-}
120120 runWithReplace (TriggerEventT a0) a' = TriggerEventT $ runWithReplace a0 (coerceEvent a')
121+ {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
122+ traverseIntMapWithKeyWithAdjust f dm0 dm' = TriggerEventT $ traverseIntMapWithKeyWithAdjust (coerce . f) dm0 dm'
121123 {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
122124 traverseDMapWithKeyWithAdjust f dm0 dm' = TriggerEventT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
123125 {-# INLINABLE traverseDMapWithKeyWithAdjustWithMove #-}