@@ -1,3 +1,5 @@
1+-- | This module provides 'PerformEventT', the standard implementation of
1{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
2{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE ExistentialQuantification #-}
3{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
@@ -17,12 +19,17 @@
17#ifdef USE_REFLEX_OPTIMIZER
19#ifdef USE_REFLEX_OPTIMIZER
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20−module Reflex.PerformEvent.Base where
22+module Reflex.PerformEvent.Base
22import Reflex.Class
28import Reflex.Class
23import Reflex.Host.Class
29import Reflex.Host.Class
24import Reflex.PerformEvent.Class
30import Reflex.PerformEvent.Class
25−import Reflex.PostBuild.Class
31+import Reflex.Requester.Base
32+import Reflex.Requester.Class
27import Control.Lens
34import Control.Lens
28import Control.Monad.Exception
35import Control.Monad.Exception
@@ -30,25 +37,20 @@ import Control.Monad.Identity
30import Control.Monad.Primitive
37import Control.Monad.Primitive
31import Control.Monad.Reader
38import Control.Monad.Reader
32import Control.Monad.Ref
39import Control.Monad.Ref
33−import Control.Monad.State.Strict
34import Data.Coerce
40import Data.Coerce
35−import Data.Dependent.Map (DMap, GCompare (..), Some)
41+import Data.Dependent.Map (DMap)
36import qualified Data.Dependent.Map as DMap
42import qualified Data.Dependent.Map as DMap
37import Data.Dependent.Sum
43import Data.Dependent.Sum
38−import Data.Functor.Misc
39−import qualified Data.List.NonEmpty as NonEmpty
42−import qualified Data.Some as Some
45−import Data.Unique.Tag
47−newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
45+-- | A function that fires events for the given 'EventTrigger's and then runs
46+-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
47+-- action will be run once for the initial trigger execution as well as once for
49newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
49newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
51+-- | Provides a basic implementation of 'PerformEvent'. Note that, despite the
52+-- name, 'PerformEventT' is not an instance of 'MonadTrans'.
51−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequestT t (HostFrame t) Identity (HostFrame t) a }
53+newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequesterT t (HostFrame t) Identity (HostFrame t) a }
53deriving instance ReflexHost t => Functor (PerformEventT t m)
55deriving instance ReflexHost t => Functor (PerformEventT t m)
54deriving instance ReflexHost t => Applicative (PerformEventT t m)
56deriving instance ReflexHost t => Applicative (PerformEventT t m)
@@ -66,17 +68,17 @@ instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent
66 {-# INLINABLE performEvent_ #-}
68 {-# INLINABLE performEvent_ #-}
67 performEvent_ = PerformEventT . requesting_
69 performEvent_ = PerformEventT . requesting_
68 {-# INLINABLE performEvent #-}
70 {-# INLINABLE performEvent #-}
69− performEvent = PerformEventT . fmap (fmap runIdentity) . requesting
71+ performEvent = PerformEventT . requestingIdentity
71instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
73instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
72− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequestTWith f (coerce outerA0) (coerceEvent outerA')
74+ runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
73− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequestT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
75+ where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
74 f a0 a' = do
76 f a0 a' = do
75 result0 <- lift a0
77 result0 <- lift a0
76 result' <- requesting a'
78 result' <- requesting a'
77 return (result0, fmap runIdentity result')
79 return (result0, fmap runIdentity result')
78− sequenceDMapWithAdjust (outerDm0 :: DMap k (PerformEventT t m)) outerDm' = PerformEventT $ sequenceDMapWithAdjustRequestTWith f (coerce outerDm0) (coerceEvent outerDm')
80+ sequenceDMapWithAdjust (outerDm0 :: DMap k (PerformEventT t m)) outerDm' = PerformEventT $ sequenceDMapWithAdjustRequesterTWith f (coerce outerDm0) (coerceEvent outerDm')
79− where f :: DMap k' (HostFrame t) -> Event t (PatchDMap k' (HostFrame t)) -> RequestT t (HostFrame t) Identity (HostFrame t) (DMap k' Identity, Event t (PatchDMap k' Identity))
81+ where f :: DMap k' (HostFrame t) -> Event t (PatchDMap k' (HostFrame t)) -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' Identity, Event t (PatchDMap k' Identity))
80 f dm0 dm' = do
82 f dm0 dm' = do
81 result0 <- lift $ DMap.traverseWithKey (\_ v -> Identity <$> v) dm0
83 result0 <- lift $ DMap.traverseWithKey (\_ v -> Identity <$> v) dm0
82 result' <- requesting $ ffor dm' $ \(PatchDMap p) -> do
84 result' <- requesting $ ffor dm' $ \(PatchDMap p) -> do
@@ -89,6 +91,9 @@ instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
89 {-# INLINABLE newFanEventWithTrigger #-}
91 {-# INLINABLE newFanEventWithTrigger #-}
90 newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
92 newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
94+-- | Run a 'PerformEventT' action, returning a 'FireCommand' that allows the
95+-- caller to trigger 'Event's while ensuring that 'performEvent' actions are run
96+-- at the appropriate time.
92{-# INLINABLE hostPerformEventT #-}
97{-# INLINABLE hostPerformEventT #-}
93hostPerformEventT :: forall t m a.
98hostPerformEventT :: forall t m a.
@@ -101,7 +106,7 @@ hostPerformEventT :: forall t m a.
101 -> m (a, FireCommand t m)
106 -> m (a, FireCommand t m)
102hostPerformEventT a = do
107hostPerformEventT a = do
103 (response, responseTrigger) <- newEventWithTriggerRef
108 (response, responseTrigger) <- newEventWithTriggerRef
104− (result, eventToPerform) <- runHostFrame $ runRequestT (unPerformEventT a) response
109+ (result, eventToPerform) <- runHostFrame $ runRequesterT (unPerformEventT a) response
105 eventToPerformHandle <- subscribeEvent eventToPerform
110 eventToPerformHandle <- subscribeEvent eventToPerform
106 return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
111 return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
107 let go :: [DSum (EventTrigger t) Identity] -> m [a']
112 let go :: [DSum (EventTrigger t) Identity] -> m [a']
@@ -145,177 +150,3 @@ instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m)
145instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
150instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
146 {-# INLINABLE atomicModifyRef #-}
151 {-# INLINABLE atomicModifyRef #-}
147 atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
152 atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
149−--------------------------------------------------------------------------------
151−--------------------------------------------------------------------------------
153−newtype RequestT t request response m a
154− = RequestT { unRequestT :: StateT [Event t (DMap (Tag (PrimState m)) request)] (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
155− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
157−instance MonadTrans (RequestT t request response) where
158− lift = RequestT . lift . lift
160−runRequestT :: forall t m a request response.
164− => RequestT t request response m a
165− -> Event t (DMap (Tag (PrimState m)) response)
166− -> m (a, Event t (DMap (Tag (PrimState m)) request))
167−runRequestT (RequestT a) responses = do
168− (result, requests) <- runReaderT (runStateT a mempty) $ fan $ mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
169− return (result, mergeWith DMap.union requests)
171−instance (Reflex t, PrimMonad m) => MonadRequest t (RequestT t request response m) where
172− type Request (RequestT t request response m) = request
173− type Response (RequestT t request response m) = response
174− withRequesting a = do
177− (req, result) <- a $ select s $ WrapArg t
178− RequestT $ modify $ (:) $ DMap.singleton t <$> req
181−data DMapTransform (a :: *) k k' v v' = forall (b :: *). DMapTransform !(k a -> k' b) !(v a -> v' b)
183−mapPatchKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> PatchDMap k v -> PatchDMap k' v'
184−mapPatchKeysAndValuesMonotonic x (PatchDMap p) = PatchDMap $ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> case x of DMapTransform f g -> f k :=> ComposeMaybe (fmap g mv)) p
186−mapKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> DMap k v -> DMap k' v'
187−mapKeysAndValuesMonotonic x = mapKeyValuePairsMonotonic $ \(k :=> v) -> case x of
188− DMapTransform f g -> f k :=> g v
190−instance MonadSample t m => MonadSample t (RequestT t request response m) where
191− sample = lift . sample
193−instance MonadHold t m => MonadHold t (RequestT t request response m) where
194− hold v0 = lift . hold v0
195− holdDyn v0 = lift . holdDyn v0
196− holdIncremental v0 = lift . holdIncremental v0
198−instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequestT t request response m) where
199− runWithReplace = runWithReplaceRequestTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
200− sequenceDMapWithAdjust = sequenceDMapWithAdjustRequestTWith $ \dm0 dm' -> lift $ sequenceDMapWithAdjust dm0 dm'
202−runWithReplaceRequestTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
206− -> RequestT t request response m (a', Event t b')
208− -> RequestT t request response m a
209− -> Event t (RequestT t request response m b)
210− -> RequestT t request response m (a, Event t b)
211−runWithReplaceRequestTWith f a0 a' = do
212− response <- RequestT ask
213− let g :: RequestT t request response m c -> m (c, [Event t (DMap (Tag (PrimState m)) request)])
214− g (RequestT r) = runReaderT (runStateT r mempty) response
215− (result0, result') <- f (g a0) $ g <$> a'
216− request <- holdDyn (fmap (mconcat . NonEmpty.toList) $ mergeList $ snd result0) $ fmap (mconcat . NonEmpty.toList) . mergeList . snd <$> result'
217− -- 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.)
218− RequestT $ modify $ (:) $ coincidence $ updated request
219− RequestT $ modify $ (:) $ switch $ current request
220− return (fst result0, fst <$> result')
222−sequenceDMapWithAdjustRequestTWith :: (GCompare k, Reflex t, MonadHold t m)
223− => (forall k'. GCompare k'
225− -> Event t (PatchDMap k' m)
226− -> RequestT t request response m (DMap k' Identity, Event t (PatchDMap k' Identity))
228− -> DMap k (RequestT t request response m)
229− -> Event t (PatchDMap k (RequestT t request response m))
230− -> RequestT t request response m (DMap k Identity, Event t (PatchDMap k Identity))
231−sequenceDMapWithAdjustRequestTWith f (dm0 :: DMap k (RequestT t request response m)) dm' = do
232− response <- RequestT ask
233− let inputTransform :: forall a. DMapTransform a k (WrapArg ((,) [Event t (DMap (Tag (PrimState m)) request)]) k) (RequestT t request response m) m
234− inputTransform = DMapTransform WrapArg (\(RequestT v) -> swap <$> runReaderT (runStateT v mempty) response)
235− (children0, children') <- f (mapKeysAndValuesMonotonic inputTransform dm0) $ mapPatchKeysAndValuesMonotonic inputTransform <$> dm'
236− let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, v)) -> k :=> Identity v) children0
237− result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
238− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> k :=> ComposeMaybe (fmap (Identity . snd . runIdentity) mv)) p
239− requests0 :: DMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t)
240− requests0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (r, _)) -> Const2 (Some.This k) :=> mergeWith DMap.union r) children0
241− requests' :: Event t (PatchDMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t))
242− requests' = ffor children' $ \(PatchDMap p) -> PatchDMap $
243− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> Const2 (Some.This k) :=> ComposeMaybe (fmap (mergeWith DMap.union . fst . runIdentity) mv)) p
244− childRequestMap <- holdIncremental requests0 requests'
245− -- 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.)
246− RequestT $ modify $ (:) $ coincidence $ ffor requests' $ \(PatchDMap p) -> mergeWith DMap.union $ catMaybes $ ffor (DMap.toList p) $ \(Const2 _ :=> ComposeMaybe me) -> me
247− RequestT $ modify $ (:) $ ffor (mergeIncremental childRequestMap) $ \m ->
248− mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m
249− return (result0, result')
251−instance PerformEvent t m => PerformEvent t (RequestT t request response m) where
252− type Performable (RequestT t request response m) = Performable m
253− performEvent_ = lift . performEvent_
254− performEvent = lift . performEvent
256−instance PostBuild t m => PostBuild t (RequestT t request response m) where
257− getPostBuild = lift getPostBuild
259−instance TriggerEvent t m => TriggerEvent t (RequestT t request response m) where
260− newTriggerEvent = lift newTriggerEvent
261− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
262− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
264−instance MonadReader r m => MonadReader r (RequestT t request response m) where
266− local f (RequestT a) = RequestT $ mapStateT (mapReaderT $ local f) a
267− reader = lift . reader
270−withRequestT :: forall t m a (req :: * -> *) (req' :: * -> *).
271− (Reflex t, MonadHold t m, MonadFix m)
272− => (forall x. request x -> request' x)
273− -> (forall x. response' x -> response x)
274− -> RequestT t request response m a
275− -> RequestT t request' response m a
276−withRequestT f g m = RequestT $ hoist (hoist (withDynamicWriterT f')) (unRequestT m)
278− f' :: Event t [(y, Some request)] -> Event t [(y, Some request')]
279− f' = fmap . fmap . fmap $ \(SomeRequest req) -> SomeRequest $ f req
282−instance MonadRef m => MonadRef (RequestT t request response m) where
283− type Ref (RequestT t request response m) = Ref m
284− newRef = lift . newRef
285− readRef = lift . readRef
286− writeRef r = lift . writeRef r
288−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequestT t request response m) where
289− newEventWithTrigger = lift . newEventWithTrigger
290− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
293−--------------------------------------------------------------------------------
295−--------------------------------------------------------------------------------
297−{-# INLINABLE numberWith #-}
298−numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
299−numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
301−{-# INLINABLE mergeSelfDeletingEvents #-}
302−mergeSelfDeletingEvents :: forall t m k v. (Ord k, Reflex t, MonadFix m, MonadHold t m)
303− => DMap (Const2 k (These v ())) (Event t)
304− -> Event t (PatchDMap (Const2 k (These v ())) (Event t))
306−mergeSelfDeletingEvents initial eAddNew = do
307− rec events :: Incremental t (PatchDMap (Const2 k (These v ())) (Event t))
308− <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
309− let event = mergeWith DMap.union
310− [ mergeIncremental events
311− , coincidence $ ffor eAddNew $ \(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getComposeMaybe) m
313− eDelete :: Event t (PatchDMap (Const2 k (These v ())) (Event t))
314− eDelete = fforMaybe event $
315− fmap (PatchDMap . DMap.fromDistinctAscList) .
316− (\l -> if null l then Nothing else Just l) .
317− fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> ComposeMaybe Nothing) <$ x ^? there) .
319− return $ ffor event $
320− fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .