on develop help sign in
Merge pull request #190 from reflex-frp/eac-split-base-class

Split out Base and Class for DynamicWriter/EventWriter

commite549ebb9439bc8fbf3152e1e84a9e62b9eadd40e authorRyan Trinkle <ryan.trinkle@gmail.com> dateThu 10 May 2018, 00:07 (8 years ago) parent9f3356f4602fe7
11 files changed+557−506
reflex.cabal+4 src/Reflex.hs+4−2 src/Reflex/DynamicWriter.hs+4−221 src/Reflex/DynamicWriter/Base.hs+213 src/Reflex/DynamicWriter/Class.hs+24 src/Reflex/EventWriter.hs+4−279 src/Reflex/EventWriter/Base.hs+273 src/Reflex/EventWriter/Class.hs+26 src/Reflex/NotReady/Class.hs+2−2 src/Reflex/Query/Base.hs+2−1 test/EventWriterT.hs+1−1
reflex.cabal+4
@@ -88,9 +88,13 @@ library
8888 Reflex.Class,
8989 Reflex.Collection,
9090 Reflex.EventWriter,
91+ Reflex.EventWriter.Base,
92+ Reflex.EventWriter.Class,
9193 Reflex.Dynamic,
9294 Reflex.Dynamic.Uniq,
9395 Reflex.DynamicWriter,
96+ Reflex.DynamicWriter.Base,
97+ Reflex.DynamicWriter.Class,
9498 Reflex.FastWeak,
9599 Reflex.FunctorMaybe,
96100 Reflex.Host.Class,
src/Reflex.hs+4−2
@@ -7,13 +7,15 @@ module Reflex
77
88import Reflex.Class as X
99import Reflex.Collection as X
10−import Reflex.EventWriter as X
10+import Reflex.EventWriter.Base as X
11+import Reflex.EventWriter.Class as X
1112import Reflex.Dynamic as X
1213#ifdef USE_TEMPLATE_HASKELL
1314import Reflex.Dynamic.TH as X
1415#endif
1516import Reflex.Dynamic.Uniq as X
16−import Reflex.DynamicWriter as X
17+import Reflex.DynamicWriter.Base as X
18+import Reflex.DynamicWriter.Class as X
1719import Reflex.PerformEvent.Base as X
1820import Reflex.PerformEvent.Class as X
1921import Reflex.PostBuild.Base as X
src/Reflex/DynamicWriter.hs+4−221
@@ -1,224 +1,7 @@
1−-- | This module defines 'MonadDynamicWriter' and 'DynamicWriterT', its standard
2−-- implementation.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE LambdaCase #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
171module Reflex.DynamicWriter
18− ( MonadDynamicWriter (..)
19− , DynamicWriterT (..)
20− , runDynamicWriterT
21− , withDynamicWriterT
2+ {-# DEPRECATED "Use 'Reflex.DynamicWriter.Class' and 'Reflex.DynamicWrite.Base' instead, or just import 'Reflex'" #-}
3+ ( module X
224 ) where
235
24−import Control.Monad.Exception
25−import Control.Monad.Identity
26−import Control.Monad.IO.Class
27−import Control.Monad.Reader
28−import Control.Monad.Ref
29−import Control.Monad.State.Strict
30−import Data.Align
31−import Data.Dependent.Map (DMap)
32−import qualified Data.Dependent.Map as DMap
33−import Data.FastMutableIntMap
34−import Data.Functor.Misc
35−import Data.IntMap (IntMap)
36−import qualified Data.IntMap as IntMap
37−import Data.Map (Map)
38−import qualified Data.Map as Map
39−import Data.Semigroup
40−import Data.Some (Some)
41−import Data.These
42−import Reflex.Class
43−import Reflex.Host.Class
44−import qualified Reflex.Patch.MapWithMove as MapWithMove
45−import Reflex.PerformEvent.Class
46−import Reflex.PostBuild.Class
47−import Reflex.Requester.Class
48−import Reflex.TriggerEvent.Class
49−
50−instance MonadTrans (DynamicWriterT t w) where
51− lift = DynamicWriterT . lift
52−
53−mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
54−mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
55−
56−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
57−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
58− where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
59− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
60−
61−mergeIntMapDynIncremental :: Reflex t => Incremental t (PatchIntMap (Dynamic t v)) -> Incremental t (PatchIntMap v)
62−mergeIntMapDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
63− where changedValues = fmap (PatchIntMap . fmap Just) $ mergeIntMapIncremental $ mapIncrementalMapValues updated a
64− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchIntMap m) -> PatchIntMap <$> mapM (mapM (sample . current)) m
65−
66−mergeDynIncrementalWithMove :: forall t k v. (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
67−mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
68− where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
69− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
70− f :: These (PatchMapWithMove k v) (Map k v) -> PatchMapWithMove k v
71− f x = unsafePatchMapWithMove $
72− let (p, changed) = case x of
73− This p_ -> (unPatchMapWithMove p_, mempty)
74− That c -> (mempty, c)
75− These p_ c -> (unPatchMapWithMove p_, c)
76− (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ MapWithMove.nodeInfoMapMFrom $ \case
77− MapWithMove.From_Insert v -> return $ MapWithMove.From_Insert v
78− MapWithMove.From_Delete -> return $ MapWithMove.From_Delete
79− MapWithMove.From_Move k -> case Map.lookup k changed of
80− Nothing -> return $ MapWithMove.From_Move k
81− Just v -> do
82− modify (k:)
83− return $ MapWithMove.From_Insert v
84− noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
85− in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
86−
87−-- | A basic implementation of 'MonadDynamicWriter'.
88−newtype 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
89−
90−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
91− type Ref (DynamicWriterT t w m) = Ref m
92− newRef = lift . newRef
93− readRef = lift . readRef
94− writeRef r = lift . writeRef r
95−
96−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
97− atomicModifyRef r = lift . atomicModifyRef r
98−
99−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
100− newEventWithTrigger = lift . newEventWithTrigger
101− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
102−
103−-- | Run a 'DynamicWriterT' action. The dynamic writer output will be provided
104−-- along with the result of the action.
105−runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
106−runDynamicWriterT (DynamicWriterT a) = do
107− (result, ws) <- runStateT a []
108− return (result, mconcat $ reverse ws)
109−
110−-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
111−-- and combines them monoidally to provide a 'Dynamic' result.
112−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
113− tellDyn :: Dynamic t w -> m ()
114−
115−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
116− tellDyn w = DynamicWriterT $ modify (w :)
117−
118−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
119− ask = lift ask
120− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
121− reader = lift . reader
122−
123−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
124− type Performable (DynamicWriterT t w m) = Performable m
125− performEvent_ = lift . performEvent_
126− performEvent = lift . performEvent
127−
128−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
129− newTriggerEvent = lift newTriggerEvent
130− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
131− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
132−
133−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
134− getPostBuild = lift getPostBuild
135−
136−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
137− tellDyn = lift . tellDyn
138−
139−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
140− get = lift get
141− put = lift . put
142−
143−newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
144−
145−-- | When the execution of a 'DynamicWriterT' action is adjusted using
146−-- 'Adjustable', the 'Dynamic' output of that action will also be updated to
147−-- match.
148−instance (Adjustable t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (DynamicWriterT t w m) where
149− runWithReplace a0 a' = do
150− (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
151− tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
152− return (fst result0, fst <$> result')
153− traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust mergeIntMapDynIncremental
154− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap weakenPatchDMapWith mergeDynIncremental
155− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeDynIncrementalWithMove
156−
157−traverseDMapWithKeyWithAdjustImpl :: forall t w k v' p p' v m. (PatchTarget (p' (Some k) (Dynamic t w)) ~ Map (Some k) (Dynamic t w), PatchTarget (p' (Some k) w) ~ Map (Some k) w, Patch (p' (Some k) w), Patch (p' (Some k) (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m)
158− => ( (forall a. k a -> v a -> m (DynamicWriterTLoweredResult t w v' a))
159− -> DMap k v
160− -> Event t (p k v)
161− -> m (DMap k (DynamicWriterTLoweredResult t w v'), Event t (p k (DynamicWriterTLoweredResult t w v')))
162− )
163− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> v' a) -> p k (DynamicWriterTLoweredResult t w v') -> p k v')
164− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> Dynamic t w) -> p k (DynamicWriterTLoweredResult t w v') -> p' (Some k) (Dynamic t w))
165− -> (Incremental t (p' (Some k) (Dynamic t w)) -> Incremental t (p' (Some k) w))
166− -> (forall a. k a -> v a -> DynamicWriterT t w m (v' a))
167− -> DMap k v
168− -> Event t (p k v)
169− -> DynamicWriterT t w m (DMap k v', Event t (p k v'))
170−traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith mergeMyDynIncremental f (dm0 :: DMap k v) dm' = do
171− (result0, result') <- lift $ base (\k v -> fmap DynamicWriterTLoweredResult $ runDynamicWriterT $ f k v) dm0 dm'
172− let getValue (DynamicWriterTLoweredResult (v, _)) = v
173− getWritten (DynamicWriterTLoweredResult (_, w)) = w
174− liftedResult0 = DMap.map getValue result0
175− liftedResult' = ffor result' $ mapPatch getValue
176− liftedWritten0 :: Map (Some k) (Dynamic t w)
177− liftedWritten0 = weakenDMapWith getWritten result0
178− liftedWritten' = ffor result' $ weakenPatchWith getWritten
179− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
180− i <- holdIncremental liftedWritten0 liftedWritten'
181− tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
182− return (liftedResult0, liftedResult')
183−
184−traverseIntMapWithKeyWithAdjustImpl :: forall t w v' p p' v m. (PatchTarget (p' (Dynamic t w)) ~ IntMap (Dynamic t w), PatchTarget (p' w) ~ IntMap w, Patch (p' w), Patch (p' (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m, Functor p, p ~ p')
185− => ( (IntMap.Key -> v -> m ((v', Dynamic t w)))
186− -> IntMap v
187− -> Event t (p v)
188− -> m (IntMap (v', Dynamic t w), Event t (p (v', Dynamic t w)))
189− )
190− -> (Incremental t (p' (Dynamic t w)) -> Incremental t (p' w))
191− -> (IntMap.Key -> v -> DynamicWriterT t w m v')
192− -> IntMap v
193− -> Event t (p v)
194− -> DynamicWriterT t w m (IntMap v', Event t (p v'))
195−traverseIntMapWithKeyWithAdjustImpl base mergeMyDynIncremental f (dm0 :: IntMap v) dm' = do
196− (result0, result') <- lift $ base (\k v -> runDynamicWriterT $ f k v) dm0 dm'
197− let liftedResult0 = fmap fst result0
198− liftedResult' = fmap (fmap fst) result'
199− liftedWritten0 :: IntMap (Dynamic t w)
200− liftedWritten0 = fmap snd result0
201− liftedWritten' = fmap (fmap snd) result'
202− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
203− i <- holdIncremental liftedWritten0 liftedWritten'
204− tellDyn $ fmap (mconcat . IntMap.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
205− return (liftedResult0, liftedResult')
206−
207−-- | Map a function over the output of a 'DynamicWriterT'.
208−withDynamicWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m, MonadFix m)
209− => (w -> w')
210− -> DynamicWriterT t w m a
211− -> DynamicWriterT t w' m a
212−withDynamicWriterT f dw = do
213− (r, d) <- lift $ do
214− (r, d) <- runDynamicWriterT dw
215− let d' = fmap f d
216− return (r, d')
217− tellDyn d
218− return r
219−
220−instance Requester t m => Requester t (DynamicWriterT t w m) where
221− type Request (DynamicWriterT t w m) = Request m
222− type Response (DynamicWriterT t w m) = Response m
223− requesting = lift . requesting
224− requesting_ = lift . requesting_
6+import Reflex.DynamicWriter.Base as X
7+import Reflex.DynamicWriter.Class as X
src/Reflex/DynamicWriter/Base.hsnew file+213
@@ -0,0 +1,213 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE GADTs #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE RankNTypes #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE UndecidableInstances #-}
11+#ifdef USE_REFLEX_OPTIMIZER
12+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13+#endif
14+module Reflex.DynamicWriter.Base
15+ ( DynamicWriterT (..)
16+ , runDynamicWriterT
17+ , withDynamicWriterT
18+ ) where
19+
20+import Control.Monad.Exception
21+import Control.Monad.Identity
22+import Control.Monad.IO.Class
23+import Control.Monad.Reader
24+import Control.Monad.Ref
25+import Control.Monad.State.Strict
26+import Data.Align
27+import Data.Dependent.Map (DMap)
28+import qualified Data.Dependent.Map as DMap
29+import Data.FastMutableIntMap
30+import Data.Functor.Misc
31+import Data.IntMap (IntMap)
32+import qualified Data.IntMap as IntMap
33+import Data.Map (Map)
34+import qualified Data.Map as Map
35+import Data.Semigroup
36+import Data.Some (Some)
37+import Data.These
38+import Reflex.Class
39+import Reflex.DynamicWriter.Class
40+import Reflex.Host.Class
41+import qualified Reflex.Patch.MapWithMove as MapWithMove
42+import Reflex.PerformEvent.Class
43+import Reflex.PostBuild.Class
44+import Reflex.Requester.Class
45+import Reflex.TriggerEvent.Class
46+
47+instance MonadTrans (DynamicWriterT t w) where
48+ lift = DynamicWriterT . lift
49+
50+mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
51+mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
52+
53+mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
54+mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
55+ where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
56+ addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
57+
58+mergeIntMapDynIncremental :: Reflex t => Incremental t (PatchIntMap (Dynamic t v)) -> Incremental t (PatchIntMap v)
59+mergeIntMapDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
60+ where changedValues = fmap (PatchIntMap . fmap Just) $ mergeIntMapIncremental $ mapIncrementalMapValues updated a
61+ addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchIntMap m) -> PatchIntMap <$> mapM (mapM (sample . current)) m
62+
63+mergeDynIncrementalWithMove :: forall t k v. (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
64+mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
65+ where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
66+ addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
67+ f :: These (PatchMapWithMove k v) (Map k v) -> PatchMapWithMove k v
68+ f x = unsafePatchMapWithMove $
69+ let (p, changed) = case x of
70+ This p_ -> (unPatchMapWithMove p_, mempty)
71+ That c -> (mempty, c)
72+ These p_ c -> (unPatchMapWithMove p_, c)
73+ (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ MapWithMove.nodeInfoMapMFrom $ \case
74+ MapWithMove.From_Insert v -> return $ MapWithMove.From_Insert v
75+ MapWithMove.From_Delete -> return MapWithMove.From_Delete
76+ MapWithMove.From_Move k -> case Map.lookup k changed of
77+ Nothing -> return $ MapWithMove.From_Move k
78+ Just v -> do
79+ modify (k:)
80+ return $ MapWithMove.From_Insert v
81+ noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
82+ in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
83+
84+-- | A basic implementation of 'MonadDynamicWriter'.
85+newtype 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
86+
87+instance MonadRef m => MonadRef (DynamicWriterT t w m) where
88+ type Ref (DynamicWriterT t w m) = Ref m
89+ newRef = lift . newRef
90+ readRef = lift . readRef
91+ writeRef r = lift . writeRef r
92+
93+instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
94+ atomicModifyRef r = lift . atomicModifyRef r
95+
96+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
97+ newEventWithTrigger = lift . newEventWithTrigger
98+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
99+
100+-- | Run a 'DynamicWriterT' action. The dynamic writer output will be provided
101+-- along with the result of the action.
102+runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
103+runDynamicWriterT (DynamicWriterT a) = do
104+ (result, ws) <- runStateT a []
105+ return (result, mconcat $ reverse ws)
106+
107+instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
108+ tellDyn w = DynamicWriterT $ modify (w :)
109+
110+instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
111+ ask = lift ask
112+ local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
113+ reader = lift . reader
114+
115+instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
116+ type Performable (DynamicWriterT t w m) = Performable m
117+ performEvent_ = lift . performEvent_
118+ performEvent = lift . performEvent
119+
120+instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
121+ newTriggerEvent = lift newTriggerEvent
122+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
123+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
124+
125+instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
126+ getPostBuild = lift getPostBuild
127+
128+instance MonadState s m => MonadState s (DynamicWriterT t w m) where
129+ get = lift get
130+ put = lift . put
131+
132+newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
133+
134+-- | When the execution of a 'DynamicWriterT' action is adjusted using
135+-- 'Adjustable', the 'Dynamic' output of that action will also be updated to
136+-- match.
137+instance (Adjustable t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (DynamicWriterT t w m) where
138+ runWithReplace a0 a' = do
139+ (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
140+ tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
141+ return (fst result0, fst <$> result')
142+ traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust mergeIntMapDynIncremental
143+ traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap weakenPatchDMapWith mergeDynIncremental
144+ traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeDynIncrementalWithMove
145+
146+traverseDMapWithKeyWithAdjustImpl :: forall t w k v' p p' v m. (PatchTarget (p' (Some k) (Dynamic t w)) ~ Map (Some k) (Dynamic t w), PatchTarget (p' (Some k) w) ~ Map (Some k) w, Patch (p' (Some k) w), Patch (p' (Some k) (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m)
147+ => ( (forall a. k a -> v a -> m (DynamicWriterTLoweredResult t w v' a))
148+ -> DMap k v
149+ -> Event t (p k v)
150+ -> m (DMap k (DynamicWriterTLoweredResult t w v'), Event t (p k (DynamicWriterTLoweredResult t w v')))
151+ )
152+ -> ((forall a. DynamicWriterTLoweredResult t w v' a -> v' a) -> p k (DynamicWriterTLoweredResult t w v') -> p k v')
153+ -> ((forall a. DynamicWriterTLoweredResult t w v' a -> Dynamic t w) -> p k (DynamicWriterTLoweredResult t w v') -> p' (Some k) (Dynamic t w))
154+ -> (Incremental t (p' (Some k) (Dynamic t w)) -> Incremental t (p' (Some k) w))
155+ -> (forall a. k a -> v a -> DynamicWriterT t w m (v' a))
156+ -> DMap k v
157+ -> Event t (p k v)
158+ -> DynamicWriterT t w m (DMap k v', Event t (p k v'))
159+traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith mergeMyDynIncremental f (dm0 :: DMap k v) dm' = do
160+ (result0, result') <- lift $ base (\k v -> fmap DynamicWriterTLoweredResult $ runDynamicWriterT $ f k v) dm0 dm'
161+ let getValue (DynamicWriterTLoweredResult (v, _)) = v
162+ getWritten (DynamicWriterTLoweredResult (_, w)) = w
163+ liftedResult0 = DMap.map getValue result0
164+ liftedResult' = ffor result' $ mapPatch getValue
165+ liftedWritten0 :: Map (Some k) (Dynamic t w)
166+ liftedWritten0 = weakenDMapWith getWritten result0
167+ liftedWritten' = ffor result' $ weakenPatchWith getWritten
168+ --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
169+ i <- holdIncremental liftedWritten0 liftedWritten'
170+ tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
171+ return (liftedResult0, liftedResult')
172+
173+traverseIntMapWithKeyWithAdjustImpl :: forall t w v' p p' v m. (PatchTarget (p' (Dynamic t w)) ~ IntMap (Dynamic t w), PatchTarget (p' w) ~ IntMap w, Patch (p' w), Patch (p' (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m, Functor p, p ~ p')
174+ => ( (IntMap.Key -> v -> m ((v', Dynamic t w)))
175+ -> IntMap v
176+ -> Event t (p v)
177+ -> m (IntMap (v', Dynamic t w), Event t (p (v', Dynamic t w)))
178+ )
179+ -> (Incremental t (p' (Dynamic t w)) -> Incremental t (p' w))
180+ -> (IntMap.Key -> v -> DynamicWriterT t w m v')
181+ -> IntMap v
182+ -> Event t (p v)
183+ -> DynamicWriterT t w m (IntMap v', Event t (p v'))
184+traverseIntMapWithKeyWithAdjustImpl base mergeMyDynIncremental f (dm0 :: IntMap v) dm' = do
185+ (result0, result') <- lift $ base (\k v -> runDynamicWriterT $ f k v) dm0 dm'
186+ let liftedResult0 = fmap fst result0
187+ liftedResult' = fmap (fmap fst) result'
188+ liftedWritten0 :: IntMap (Dynamic t w)
189+ liftedWritten0 = fmap snd result0
190+ liftedWritten' = fmap (fmap snd) result'
191+ --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
192+ i <- holdIncremental liftedWritten0 liftedWritten'
193+ tellDyn $ fmap (mconcat . IntMap.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
194+ return (liftedResult0, liftedResult')
195+
196+-- | Map a function over the output of a 'DynamicWriterT'.
197+withDynamicWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m, MonadFix m)
198+ => (w -> w')
199+ -> DynamicWriterT t w m a
200+ -> DynamicWriterT t w' m a
201+withDynamicWriterT f dw = do
202+ (r, d) <- lift $ do
203+ (r, d) <- runDynamicWriterT dw
204+ let d' = fmap f d
205+ return (r, d')
206+ tellDyn d
207+ return r
208+
209+instance Requester t m => Requester t (DynamicWriterT t w m) where
210+ type Request (DynamicWriterT t w m) = Request m
211+ type Response (DynamicWriterT t w m) = Response m
212+ requesting = lift . requesting
213+ requesting_ = lift . requesting_
src/Reflex/DynamicWriter/Class.hsnew file+24
@@ -0,0 +1,24 @@
1+-- | This module defines the 'MonadDynamicWriter' class.
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE UndecidableInstances #-}
7+#ifdef USE_REFLEX_OPTIMIZER
8+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9+#endif
10+module Reflex.DynamicWriter.Class
11+ ( MonadDynamicWriter (..)
12+ ) where
13+
14+import Control.Monad.Reader (ReaderT, lift)
15+import Reflex.Class (Dynamic)
16+
17+-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
18+-- and combines them monoidally to provide a 'Dynamic' result.
19+class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
20+ tellDyn :: Dynamic t w -> m ()
21+
22+instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
23+ tellDyn = lift . tellDyn
24+
src/Reflex/EventWriter.hs+4−279
@@ -1,282 +1,7 @@
1−-- | This module provides 'EventWriterT', the standard implementation of
2−-- 'EventWriter'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
171module Reflex.EventWriter
18− ( EventWriterT (..)
19− , runEventWriterT
20− , EventWriter (..)
21− , runWithReplaceEventWriterTWith
22− , sequenceDMapWithAdjustEventWriterTWith
23− , withEventWriterT
2+ {-# DEPRECATED "Use 'Reflex.EventWriter.Class' and 'Reflex.EventWriter.Base' instead, or just import 'Reflex'" #-}
3+ ( module X
244 ) where
255
26−import Reflex.Class
27−import Reflex.Host.Class
28−import Reflex.PerformEvent.Class
29−import Reflex.PostBuild.Class
30−import Reflex.Query.Class
31−import Reflex.Requester.Class
32−import Reflex.TriggerEvent.Class
33−
34−import Control.Monad.Exception
35−import Control.Monad.Identity
36−import Control.Monad.Reader
37−import Control.Monad.Ref
38−import Control.Monad.State.Strict
39−import Data.Dependent.Map (DMap, DSum (..))
40−import qualified Data.Dependent.Map as DMap
41−import Data.Foldable
42−import Data.Functor.Compose
43−import 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
47−import Data.List.NonEmpty (NonEmpty (..))
48−import Data.Map (Map)
49−import Data.Semigroup
50−import Data.Some (Some)
51−import Data.Tuple
52−import Data.Type.Equality
53−
54−import Unsafe.Coerce
55−
56−-- | 'EventWriter' efficiently collects 'Event' values using 'tellEvent'
57−-- and combines them monoidally to provide an 'Event' result.
58−class (Monad m, Semigroup w) => EventWriter t w m | m -> t w where
59− tellEvent :: Event t w -> m ()
60−
61−{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
62−newtype TellId w x
63− = TellId Int -- ^ WARNING: Do not construct this directly; use 'TellId' instead
64− deriving (Show, Eq, Ord, Enum)
65−
66−tellId :: Int -> TellId w w
67−tellId = TellId
68−{-# INLINE tellId #-}
69−
70−tellIdRefl :: TellId w x -> w :~: x
71−tellIdRefl _ = unsafeCoerce Refl
72−
73−withTellIdRefl :: TellId w x -> (w ~ x => r) -> r
74−withTellIdRefl tid r = case tellIdRefl tid of
75− Refl -> r
76−
77−instance GEq (TellId w) where
78− a `geq` b =
79− withTellIdRefl a $
80− withTellIdRefl b $
81− if a == b
82− then Just Refl
83− else Nothing
84−
85−instance GCompare (TellId w) where
86− a `gcompare` b =
87− withTellIdRefl a $
88− withTellIdRefl b $
89− case a `compare` b of
90− LT -> GLT
91− EQ -> GEQ
92− GT -> GGT
93−
94−data EventWriterState t w = EventWriterState
95− { _eventWriterState_nextId :: {-# UNPACK #-} !Int -- Always negative (and decreasing over time)
96− , _eventWriterState_told :: ![DSum (TellId w) (Event t)] -- In increasing order
97− }
98−
99−-- | A basic implementation of 'EventWriter'.
100−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
101− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
102−
103−-- | Run a 'EventWriterT' action.
104−runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
105−runEventWriterT (EventWriterT a) = do
106− (result, requests) <- runStateT a $ EventWriterState (-1) []
107− let combineResults :: DMap (TellId w) Identity -> w
108− combineResults = sconcat
109− . (\(h : t) -> h :| t) -- Unconditional; 'merge' guarantees that it will only fire with non-empty DMaps
110− . DMap.foldlWithKey (\vs tid (Identity v) -> withTellIdRefl tid $ v : vs) [] -- This is where we finally reverse the DMap to get things in the correct order
111− return (result, fmap combineResults $ merge $ DMap.fromDistinctAscList $ _eventWriterState_told requests) --TODO: We can probably make this fromDistinctAscList more efficient by knowing the length in advance, but this will require exposing internals of DMap; also converting it to use a strict list might help
112−
113−instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m) where
114− tellEvent w = EventWriterT $ modify $ \old ->
115− let myId = _eventWriterState_nextId old
116− in EventWriterState
117− { _eventWriterState_nextId = pred myId
118− , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
119− }
120−
121−instance EventWriter t w m => EventWriter t w (ReaderT r m) where
122− tellEvent = lift . tellEvent
123−
124−instance MonadTrans (EventWriterT t w) where
125− lift = EventWriterT . lift
126−
127−instance MonadSample t m => MonadSample t (EventWriterT t w m) where
128− sample = lift . sample
129−
130−instance MonadHold t m => MonadHold t (EventWriterT t w m) where
131− {-# INLINABLE hold #-}
132− hold v0 = lift . hold v0
133− {-# INLINABLE holdDyn #-}
134− holdDyn v0 = lift . holdDyn v0
135− {-# INLINABLE holdIncremental #-}
136− holdIncremental v0 = lift . holdIncremental v0
137− {-# INLINABLE buildDynamic #-}
138− buildDynamic a0 = lift . buildDynamic a0
139− {-# INLINABLE headE #-}
140− headE = lift . headE
141−
142−instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
143− runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
144− traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
145− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
146− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
147−
148−instance Requester t m => Requester t (EventWriterT t w m) where
149− type Request (EventWriterT t w m) = Request m
150− type Response (EventWriterT t w m) = Response m
151− requesting = lift . requesting
152− requesting_ = lift . requesting_
153−
154−-- | Given a function like 'runWithReplace' for the underlying monad, implement
155−-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
156−-- monad doesn't have a 'Adjustable' instance or to override the default
157−-- 'Adjustable' behavior.
158−runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Semigroup w)
159− => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
160− -> EventWriterT t w m a
161− -> Event t (EventWriterT t w m b)
162− -> EventWriterT t w m (a, Event t b)
163−runWithReplaceEventWriterTWith f a0 a' = do
164− (result0, result') <- f (runEventWriterT a0) $ fmapCheap runEventWriterT a'
165− request <- holdDyn (snd result0) $ fmapCheap snd result'
166− -- 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.)
167− tellEvent $ coincidence $ updated request
168− tellEvent $ switch $ current request
169− return (fst result0, fmapCheap fst result')
170−
171−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
172−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))
173− => ( (IntMap.Key -> v -> m (Event t w, v'))
174− -> IntMap v
175− -> Event t (p v)
176− -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
177− )
178− -> (p (Event t w) -> [Event t w])
179− -> (Incremental t (p (Event t w)) -> Event t (IntMap w))
180− -> (IntMap.Key -> v -> EventWriterT t w m v')
181− -> IntMap v
182− -> Event t (p v)
183− -> EventWriterT t w m (IntMap v', Event t (p v'))
184−sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
185− let f' :: IntMap.Key -> v -> m (Event t w, v')
186− f' k v = swap <$> runEventWriterT (f k v)
187− (children0, children') <- base f' dm0 dm'
188− let result0 = fmap snd children0
189− result' = fmapCheap (fmap snd) children'
190− requests0 :: IntMap (Event t w)
191− requests0 = fmap fst children0
192− requests' :: Event t (p (Event t w))
193− requests' = fmapCheap (fmap fst) children'
194− childRequestMap :: Incremental t (p (Event t w)) <- holdIncremental requests0 requests'
195− -- 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.)
196− tellEvent $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
197− tellEvent $ fforMaybeCheap (mergePatchIncremental childRequestMap) $ \m -> case toList m of
198− [] -> Nothing
199− h : t -> Just $ sconcat $ h :| t
200− return (result0, result')
201−
202−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
203−sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
204− => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
205− -> DMap k v
206− -> Event t (p k v)
207− -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
208− )
209− -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
210− -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
211− -> (p' (Some k) (Event t w) -> [Event t w])
212− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
213− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
214− -> DMap k v
215− -> Event t (p k v)
216− -> EventWriterT t w m (DMap k v', Event t (p k v'))
217−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
218− let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
219− f' k v = Compose . swap <$> runEventWriterT (f k v)
220− (children0, children') <- base f' dm0 dm'
221− let result0 = DMap.map (snd . getCompose) children0
222− result' = fforCheap children' $ mapPatch $ snd . getCompose
223− requests0 :: Map (Some k) (Event t w)
224− requests0 = weakenDMapWith (fst . getCompose) children0
225− requests' :: Event t (p' (Some k) (Event t w))
226− requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
227− childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
228− -- 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.)
229− tellEvent $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
230− tellEvent $ fforMaybeCheap (mergePatchIncremental childRequestMap) $ \m -> case toList m of
231− [] -> Nothing
232− h : t -> Just $ sconcat $ h :| t
233− return (result0, result')
234−
235−instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
236− type Performable (EventWriterT t w m) = Performable m
237− performEvent_ = lift . performEvent_
238− performEvent = lift . performEvent
239−
240−instance PostBuild t m => PostBuild t (EventWriterT t w m) where
241− getPostBuild = lift getPostBuild
242−
243−instance TriggerEvent t m => TriggerEvent t (EventWriterT t w m) where
244− newTriggerEvent = lift newTriggerEvent
245− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
246− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
247−
248−instance MonadReader r m => MonadReader r (EventWriterT t w m) where
249− ask = lift ask
250− local f (EventWriterT a) = EventWriterT $ mapStateT (local f) a
251− reader = lift . reader
252−
253−instance MonadRef m => MonadRef (EventWriterT t w m) where
254− type Ref (EventWriterT t w m) = Ref m
255− newRef = lift . newRef
256− readRef = lift . readRef
257− writeRef r = lift . writeRef r
258−
259−instance MonadAtomicRef m => MonadAtomicRef (EventWriterT t w m) where
260− atomicModifyRef r = lift . atomicModifyRef r
261−
262−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (EventWriterT t w m) where
263− newEventWithTrigger = lift . newEventWithTrigger
264− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
265−
266−instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) where
267− tellQueryIncremental = lift . tellQueryIncremental
268− askQueryResult = lift askQueryResult
269− queryIncremental = lift . queryIncremental
270−
271−-- | Map a function over the output of a 'EventWriterT'.
272−withEventWriterT :: (Semigroup w, Semigroup w', Reflex t, MonadHold t m, MonadFix m)
273− => (w -> w')
274− -> EventWriterT t w m a
275− -> EventWriterT t w' m a
276−withEventWriterT f ew = do
277− (r, e) <- lift $ do
278− (r, e) <- runEventWriterT ew
279− let e' = fmap f e
280− return (r, e')
281− tellEvent e
282− return r
6+import Reflex.EventWriter.Base as X
7+import Reflex.EventWriter.Class as X
src/Reflex/EventWriter/Base.hsnew file+273
@@ -0,0 +1,273 @@
1+-- | This module provides 'EventWriterT', the standard implementation of
2+-- 'EventWriter'.
3+{-# LANGUAGE CPP #-}
4+{-# LANGUAGE ExistentialQuantification #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE Rank2Types #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE TypeOperators #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+#ifdef USE_REFLEX_OPTIMIZER
14+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15+#endif
16+module Reflex.EventWriter.Base
17+ ( EventWriterT (..)
18+ , runEventWriterT
19+ , runWithReplaceEventWriterTWith
20+ , sequenceDMapWithAdjustEventWriterTWith
21+ , withEventWriterT
22+ ) where
23+
24+import Reflex.Class
25+import Reflex.EventWriter.Class (EventWriter, tellEvent)
26+import Reflex.Host.Class
27+import Reflex.PerformEvent.Class
28+import Reflex.PostBuild.Class
29+import Reflex.Query.Class
30+import Reflex.Requester.Class
31+import Reflex.TriggerEvent.Class
32+
33+import Control.Monad.Exception
34+import Control.Monad.Identity
35+import Control.Monad.Reader
36+import Control.Monad.Ref
37+import Control.Monad.State.Strict
38+import Data.Dependent.Map (DMap, DSum (..))
39+import qualified Data.Dependent.Map as DMap
40+import Data.Foldable
41+import Data.Functor.Compose
42+import Data.Functor.Misc
43+import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
44+import Data.IntMap.Strict (IntMap)
45+import qualified Data.IntMap.Strict as IntMap
46+import Data.List.NonEmpty (NonEmpty (..))
47+import Data.Map (Map)
48+import Data.Semigroup
49+import Data.Some (Some)
50+import Data.Tuple
51+import Data.Type.Equality
52+
53+import Unsafe.Coerce
54+
55+{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
56+newtype TellId w x
57+ = TellId Int -- ^ WARNING: Do not construct this directly; use 'TellId' instead
58+ deriving (Show, Eq, Ord, Enum)
59+
60+tellId :: Int -> TellId w w
61+tellId = TellId
62+{-# INLINE tellId #-}
63+
64+tellIdRefl :: TellId w x -> w :~: x
65+tellIdRefl _ = unsafeCoerce Refl
66+
67+withTellIdRefl :: TellId w x -> (w ~ x => r) -> r
68+withTellIdRefl tid r = case tellIdRefl tid of
69+ Refl -> r
70+
71+instance GEq (TellId w) where
72+ a `geq` b =
73+ withTellIdRefl a $
74+ withTellIdRefl b $
75+ if a == b
76+ then Just Refl
77+ else Nothing
78+
79+instance GCompare (TellId w) where
80+ a `gcompare` b =
81+ withTellIdRefl a $
82+ withTellIdRefl b $
83+ case a `compare` b of
84+ LT -> GLT
85+ EQ -> GEQ
86+ GT -> GGT
87+
88+data EventWriterState t w = EventWriterState
89+ { _eventWriterState_nextId :: {-# UNPACK #-} !Int -- Always negative (and decreasing over time)
90+ , _eventWriterState_told :: ![DSum (TellId w) (Event t)] -- In increasing order
91+ }
92+
93+-- | A basic implementation of 'EventWriter'.
94+newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
95+ deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
96+
97+-- | Run a 'EventWriterT' action.
98+runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
99+runEventWriterT (EventWriterT a) = do
100+ (result, requests) <- runStateT a $ EventWriterState (-1) []
101+ let combineResults :: DMap (TellId w) Identity -> w
102+ combineResults = sconcat
103+ . (\(h : t) -> h :| t) -- Unconditional; 'merge' guarantees that it will only fire with non-empty DMaps
104+ . DMap.foldlWithKey (\vs tid (Identity v) -> withTellIdRefl tid $ v : vs) [] -- This is where we finally reverse the DMap to get things in the correct order
105+ return (result, fmap combineResults $ merge $ DMap.fromDistinctAscList $ _eventWriterState_told requests) --TODO: We can probably make this fromDistinctAscList more efficient by knowing the length in advance, but this will require exposing internals of DMap; also converting it to use a strict list might help
106+
107+instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m) where
108+ tellEvent w = EventWriterT $ modify $ \old ->
109+ let myId = _eventWriterState_nextId old
110+ in EventWriterState
111+ { _eventWriterState_nextId = pred myId
112+ , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
113+ }
114+
115+instance MonadTrans (EventWriterT t w) where
116+ lift = EventWriterT . lift
117+
118+instance MonadSample t m => MonadSample t (EventWriterT t w m) where
119+ sample = lift . sample
120+
121+instance MonadHold t m => MonadHold t (EventWriterT t w m) where
122+ {-# INLINABLE hold #-}
123+ hold v0 = lift . hold v0
124+ {-# INLINABLE holdDyn #-}
125+ holdDyn v0 = lift . holdDyn v0
126+ {-# INLINABLE holdIncremental #-}
127+ holdIncremental v0 = lift . holdIncremental v0
128+ {-# INLINABLE buildDynamic #-}
129+ buildDynamic a0 = lift . buildDynamic a0
130+ {-# INLINABLE headE #-}
131+ headE = lift . headE
132+
133+instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
134+ runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
135+ traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
136+ traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
137+ traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
138+
139+instance Requester t m => Requester t (EventWriterT t w m) where
140+ type Request (EventWriterT t w m) = Request m
141+ type Response (EventWriterT t w m) = Response m
142+ requesting = lift . requesting
143+ requesting_ = lift . requesting_
144+
145+-- | Given a function like 'runWithReplace' for the underlying monad, implement
146+-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
147+-- monad doesn't have a 'Adjustable' instance or to override the default
148+-- 'Adjustable' behavior.
149+runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Semigroup w)
150+ => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
151+ -> EventWriterT t w m a
152+ -> Event t (EventWriterT t w m b)
153+ -> EventWriterT t w m (a, Event t b)
154+runWithReplaceEventWriterTWith f a0 a' = do
155+ (result0, result') <- f (runEventWriterT a0) $ fmapCheap runEventWriterT a'
156+ request <- holdDyn (snd result0) $ fmapCheap snd result'
157+ -- 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.)
158+ tellEvent $ coincidence $ updated request
159+ tellEvent $ switch $ current request
160+ return (fst result0, fmapCheap fst result')
161+
162+-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
163+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))
164+ => ( (IntMap.Key -> v -> m (Event t w, v'))
165+ -> IntMap v
166+ -> Event t (p v)
167+ -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
168+ )
169+ -> (p (Event t w) -> [Event t w])
170+ -> (Incremental t (p (Event t w)) -> Event t (IntMap w))
171+ -> (IntMap.Key -> v -> EventWriterT t w m v')
172+ -> IntMap v
173+ -> Event t (p v)
174+ -> EventWriterT t w m (IntMap v', Event t (p v'))
175+sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
176+ let f' :: IntMap.Key -> v -> m (Event t w, v')
177+ f' k v = swap <$> runEventWriterT (f k v)
178+ (children0, children') <- base f' dm0 dm'
179+ let result0 = fmap snd children0
180+ result' = fmapCheap (fmap snd) children'
181+ requests0 :: IntMap (Event t w)
182+ requests0 = fmap fst children0
183+ requests' :: Event t (p (Event t w))
184+ requests' = fmapCheap (fmap fst) children'
185+ childRequestMap :: Incremental t (p (Event t w)) <- holdIncremental requests0 requests'
186+ -- 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.)
187+ tellEvent $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
188+ tellEvent $ fforMaybeCheap (mergePatchIncremental childRequestMap) $ \m -> case toList m of
189+ [] -> Nothing
190+ h : t -> Just $ sconcat $ h :| t
191+ return (result0, result')
192+
193+-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
194+sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
195+ => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
196+ -> DMap k v
197+ -> Event t (p k v)
198+ -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
199+ )
200+ -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
201+ -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
202+ -> (p' (Some k) (Event t w) -> [Event t w])
203+ -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
204+ -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
205+ -> DMap k v
206+ -> Event t (p k v)
207+ -> EventWriterT t w m (DMap k v', Event t (p k v'))
208+sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
209+ let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
210+ f' k v = Compose . swap <$> runEventWriterT (f k v)
211+ (children0, children') <- base f' dm0 dm'
212+ let result0 = DMap.map (snd . getCompose) children0
213+ result' = fforCheap children' $ mapPatch $ snd . getCompose
214+ requests0 :: Map (Some k) (Event t w)
215+ requests0 = weakenDMapWith (fst . getCompose) children0
216+ requests' :: Event t (p' (Some k) (Event t w))
217+ requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
218+ childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
219+ -- 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.)
220+ tellEvent $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
221+ tellEvent $ fforMaybeCheap (mergePatchIncremental childRequestMap) $ \m -> case toList m of
222+ [] -> Nothing
223+ h : t -> Just $ sconcat $ h :| t
224+ return (result0, result')
225+
226+instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
227+ type Performable (EventWriterT t w m) = Performable m
228+ performEvent_ = lift . performEvent_
229+ performEvent = lift . performEvent
230+
231+instance PostBuild t m => PostBuild t (EventWriterT t w m) where
232+ getPostBuild = lift getPostBuild
233+
234+instance TriggerEvent t m => TriggerEvent t (EventWriterT t w m) where
235+ newTriggerEvent = lift newTriggerEvent
236+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
237+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
238+
239+instance MonadReader r m => MonadReader r (EventWriterT t w m) where
240+ ask = lift ask
241+ local f (EventWriterT a) = EventWriterT $ mapStateT (local f) a
242+ reader = lift . reader
243+
244+instance MonadRef m => MonadRef (EventWriterT t w m) where
245+ type Ref (EventWriterT t w m) = Ref m
246+ newRef = lift . newRef
247+ readRef = lift . readRef
248+ writeRef r = lift . writeRef r
249+
250+instance MonadAtomicRef m => MonadAtomicRef (EventWriterT t w m) where
251+ atomicModifyRef r = lift . atomicModifyRef r
252+
253+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (EventWriterT t w m) where
254+ newEventWithTrigger = lift . newEventWithTrigger
255+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
256+
257+instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) where
258+ tellQueryIncremental = lift . tellQueryIncremental
259+ askQueryResult = lift askQueryResult
260+ queryIncremental = lift . queryIncremental
261+
262+-- | Map a function over the output of a 'EventWriterT'.
263+withEventWriterT :: (Semigroup w, Semigroup w', Reflex t, MonadHold t m, MonadFix m)
264+ => (w -> w')
265+ -> EventWriterT t w m a
266+ -> EventWriterT t w' m a
267+withEventWriterT f ew = do
268+ (r, e) <- lift $ do
269+ (r, e) <- runEventWriterT ew
270+ let e' = fmap f e
271+ return (r, e')
272+ tellEvent e
273+ return r
src/Reflex/EventWriter/Class.hsnew file+26
@@ -0,0 +1,26 @@
1+-- | This module defines the 'EventWriter' class.
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE FunctionalDependencies #-}
5+{-# LANGUAGE UndecidableInstances #-}
6+#ifdef USE_REFLEX_OPTIMIZER
7+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
8+#endif
9+module Reflex.EventWriter.Class
10+ ( EventWriter (..)
11+ ) where
12+
13+import Control.Monad.Reader (ReaderT, lift)
14+import Data.Semigroup (Semigroup)
15+
16+import Reflex.Class (Event)
17+
18+
19+-- | 'EventWriter' efficiently collects 'Event' values using 'tellEvent'
20+-- and combines them via 'Semigroup' to provide an 'Event' result.
21+class (Monad m, Semigroup w) => EventWriter t w m | m -> t w where
22+ tellEvent :: Event t w -> m ()
23+
24+
25+instance EventWriter t w m => EventWriter t w (ReaderT r m) where
26+ tellEvent = lift . tellEvent
src/Reflex/NotReady/Class.hs+2−2
@@ -18,8 +18,8 @@ import Control.Monad.Reader (ReaderT)
1818import Reflex.Class
1919import Reflex.Host.Class
2020import Reflex.PostBuild.Base (PostBuildT)
21−import Reflex.EventWriter (EventWriterT)
22−import Reflex.DynamicWriter (DynamicWriterT)
21+import Reflex.EventWriter.Base (EventWriterT)
22+import Reflex.DynamicWriter.Base (DynamicWriterT)
2323import Reflex.Query.Base (QueryT)
2424import Reflex.Requester.Base (RequesterT)
2525import Reflex.PerformEvent.Base (PerformEventT(..))
src/Reflex/Query/Base.hs+2−1
@@ -37,7 +37,8 @@ import qualified Data.Some as Some
3737import Data.These
3838
3939import Reflex.Class
40−import Reflex.EventWriter
40+import Reflex.EventWriter.Class
41+import Reflex.EventWriter.Base
4142import Reflex.Host.Class
4243import Reflex.PerformEvent.Class
4344import Reflex.PostBuild.Class
test/EventWriterT.hs+1−1
@@ -6,7 +6,7 @@ import Control.Monad
66import Data.These
77
88import Reflex
9−import Reflex.EventWriter
9+import Reflex.EventWriter.Base
1010import Test.Run
1111
1212main :: IO ()