@@ -1,16 +1,20 @@
1-- | This module provides 'RequesterT', the standard implementation of
1-- | This module provides 'RequesterT', the standard implementation of
2-- 'Requester'.
2-- 'Requester'.
3−{-# LANGUAGE BangPatterns #-}
3+{-# LANGUAGE AllowAmbiguousTypes #-}
4{-# LANGUAGE CPP #-}
4{-# LANGUAGE CPP #-}
5+{-# LANGUAGE EmptyDataDecls #-}
5{-# LANGUAGE ExistentialQuantification #-}
6{-# LANGUAGE ExistentialQuantification #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE Rank2Types #-}
11{-# LANGUAGE Rank2Types #-}
12+{-# LANGUAGE RecursiveDo #-}
10{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE ScopedTypeVariables #-}
14+{-# LANGUAGE StandaloneDeriving #-}
11{-# LANGUAGE TypeFamilies #-}
15{-# LANGUAGE TypeFamilies #-}
16+{-# LANGUAGE TypeOperators #-}
12{-# LANGUAGE UndecidableInstances #-}
17{-# LANGUAGE UndecidableInstances #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14#ifdef USE_REFLEX_OPTIMIZER
18#ifdef USE_REFLEX_OPTIMIZER
15{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
@@ -18,11 +22,13 @@ module Reflex.Requester.Base
18 ( RequesterT (..)
22 ( RequesterT (..)
19 , runRequesterT
23 , runRequesterT
20 , runWithReplaceRequesterTWith
24 , runWithReplaceRequesterTWith
21− , sequenceDMapWithAdjustRequesterTWith
25+ , traverseDMapWithKeyWithAdjustRequesterTWith
27+ , traverseRequesterData
24import Reflex.Class
30import Reflex.Class
25−import Reflex.EventWriter
31+import Reflex.Dynamic
26import Reflex.Host.Class
32import Reflex.Host.Class
27import Reflex.PerformEvent.Class
33import Reflex.PerformEvent.Class
28import Reflex.PostBuild.Class
34import Reflex.PostBuild.Class
@@ -31,20 +37,111 @@ import Reflex.TriggerEvent.Class
32import Control.Monad.Exception
38import Control.Monad.Exception
33import Control.Monad.Identity
39import Control.Monad.Identity
34−import Control.Monad.Primitive
35import Control.Monad.Reader
40import Control.Monad.Reader
36import Control.Monad.Ref
41import Control.Monad.Ref
37import Control.Monad.State.Strict
42import Control.Monad.State.Strict
38import Data.Coerce
44import Data.Coerce
39import Data.Dependent.Map (DMap, DSum (..))
45import Data.Dependent.Map (DMap, DSum (..))
40import qualified Data.Dependent.Map as DMap
46import qualified Data.Dependent.Map as DMap
47+import qualified Data.Some as Some
48+import Data.Functor.Compose
41import Data.Functor.Misc
49import Data.Functor.Misc
42import Data.Map (Map)
51import Data.Map (Map)
43import Data.Unique.Tag
53import Data.Unique.Tag
44import Data.Some (Some)
54import Data.Some (Some)
56+import Data.Type.Equality
60+newtype RequesterData f = RequesterData { unRequesterData :: DMap MyTag (Entry f) }
62+-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
63+traverseRequesterData :: 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
65+ where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
66+ go k (Entry request) = Entry <$> case myKeyType k of
67+ MyTagType_Single -> f request
68+ MyTagType_Multi -> traverseRequesterData f request
69+ MyTagType_Multi2 -> traverse (traverseRequesterData f) request
71+data MyTagType :: * -> * where
72+ MyTagType_Single :: MyTagType (Single a)
73+ MyTagType_Multi :: MyTagType Multi
74+ MyTagType_Multi2 :: MyTagType (Multi2 k)
76+myKeyType :: MyTag x -> MyTagType x
77+myKeyType (MyTag k) = case k .&. 0x3 of
78+ 0x0 -> unsafeCoerce MyTagType_Single
79+ 0x1 -> unsafeCoerce MyTagType_Multi
80+ 0x2 -> unsafeCoerce MyTagType_Multi2
81+ t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
85+data Multi2 (k :: * -> *)
87+type family EntryContents request a where
88+ EntryContents request (Single a) = request a
89+ EntryContents request Multi = RequesterData request
90+ EntryContents request (Multi2 k) = Map (Some k) (RequesterData request)
92+newtype Entry request x = Entry { unEntry :: EntryContents request x }
94+{-# INLINE singleEntry #-}
95+singleEntry :: f a -> Entry f (Single a)
98+{-# INLINE multiEntry #-}
99+multiEntry :: RequesterData f -> Entry f Multi
102+-- | 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
103+-- 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)
106+newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int32 deriving (Show, Eq, Ord, Enum)
108+{-# INLINE castMyTagWrap #-}
109+castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
110+castMyTagWrap = coerce
112+instance GEq MyTag where
113+ (MyTag a) `geq` (MyTag b) =
115+ then Just $ unsafeCoerce Refl
118+instance GCompare MyTag where
119+ (MyTag a) `gcompare` (MyTag b) =
120+ case a `compare` b of
122+ EQ -> unsafeCoerce GEQ
125+instance GEq (MyTagWrap f) where
126+ (MyTagWrap a) `geq` (MyTagWrap b) =
128+ then Just $ unsafeCoerce Refl
131+instance GCompare (MyTagWrap f) where
132+ (MyTagWrap a) `gcompare` (MyTagWrap b) =
133+ case a `compare` b of
135+ EQ -> unsafeCoerce GEQ
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)]
46-- | A basic implementation of 'Requester'.
143-- | A basic implementation of 'Requester'.
47−newtype RequesterT t request response m a = RequesterT { unRequesterT :: EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
144+newtype RequesterT t request response m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelector t (MyTagWrap response)) m) a }
48 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
145 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
49-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
146-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
50#if MIN_VERSION_base(4,9,1)
147#if MIN_VERSION_base(4,9,1)
@@ -57,6 +154,14 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
57deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
154deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
58deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
155deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
157+{-# INLINE wrapMyTags #-}
158+wrapMyTags :: DMap MyTag (Entry f) -> DMap (MyTagWrap f) Identity
159+wrapMyTags = unsafeCoerce
161+{-# INLINE unwrapMyTags #-}
162+unwrapMyTags :: DMap (MyTagWrap f) Identity -> DMap MyTag (Entry f)
163+unwrapMyTags = unsafeCoerce
60-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
165-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
61-- requests are made, and responses should be provided in the input 'Event'.
166-- requests are made, and responses should be provided in the input 'Event'.
62-- The 'Tag' keys will be used to return the responses to the same place the
167-- The 'Tag' keys will be used to return the responses to the same place the
@@ -64,22 +169,36 @@ deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request respo
65runRequesterT :: (Reflex t, Monad m)
170runRequesterT :: (Reflex t, Monad m)
66 => RequesterT t request response m a
171 => RequesterT t request response m a
67− -> Event t (DMap (Tag (PrimState m)) response)
172+ -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
68− -> m (a, Event t (DMap (Tag (PrimState m)) request))
173+ -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
69runRequesterT (RequesterT a) responses = do
174runRequesterT (RequesterT a) responses = do
70− (result, requests) <- runReaderT (runEventWriterT a) $ fan $
175+ (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fan $
71− mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
176+ fmapCheap (wrapMyTags . unRequesterData) responses
72− return (result, requests)
177+ return (result, fmapCheap (RequesterData . unwrapMyTags) $ merge $ DMap.fromDistinctAscList $ _requesterState_requests s)
74−instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
179+instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
75 type Request (RequesterT t request response m) = request
180 type Request (RequesterT t request response m) = request
76 type Response (RequesterT t request response m) = response
181 type Response (RequesterT t request response m) = response
77− withRequesting a = do
182+ requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a)))
78− !t <- lift newTag --TODO: Fix this upstream
183+ requesting_ = void . tagRequest . fmapCheap singleEntry
79− s <- RequesterT ask
184+
80− (req, result) <- a $ select s $ WrapArg t
185+{-# INLINE tagRequest #-}
81− RequesterT $ tellEvent $ fmapCheap (DMap.singleton t) req
186+tagRequest :: Monad m => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
82− return result
187+tagRequest req = do
188+ old <- RequesterT get
189+ let n = _requesterState_nextMyTag old
191+ RequesterT $ put $ RequesterState
192+ { _requesterState_nextMyTag = n - 0x4
193+ , _requesterState_requests = (t :=> req) : _requesterState_requests old
197+{-# INLINE responseFromTag #-}
198+responseFromTag :: (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
84instance MonadTrans (RequesterT t request response) where
203instance MonadTrans (RequesterT t request response) where
85 lift = RequesterT . lift . lift
204 lift = RequesterT . lift . lift
@@ -101,34 +220,37 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT
102instance MonadReader r m => MonadReader r (RequesterT t request response m) where
221instance MonadReader r m => MonadReader r (RequesterT t request response m) where
103 ask = lift ask
222 ask = lift ask
104− local f (RequesterT (EventWriterT a)) = RequesterT $ EventWriterT $ mapStateT (mapReaderT $ local f) a
223+ local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
105 reader = lift . reader
224 reader = lift . reader
107−instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequesterT t request response m) where
226+instance (Reflex t, MonadAdjust t m, MonadHold t m, MonadFix m) => MonadAdjust t (RequesterT t request response m) where
108− runWithReplace (RequesterT a) em = RequesterT $ runWithReplace a (coerceEvent em)
227+ runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
109− traverseDMapWithKeyWithAdjust f dm edm = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
228+ traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
110− traverseDMapWithKeyWithAdjustWithMove f dm edm = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
229+ traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
231+requesting' :: (Monad m, Reflex t) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
232+requesting' = responseFromTag . castMyTagWrap <=< tagRequest
112−runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
234+runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
113 => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
237 => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
114 -> RequesterT t request response m a
238 -> RequesterT t request response m a
115 -> Event t (RequesterT t request response m b)
239 -> Event t (RequesterT t request response m b)
116 -> RequesterT t request response m (a, Event t b)
240 -> RequesterT t request response m (a, Event t b)
117−runWithReplaceRequesterTWith f a0 a' =
241+runWithReplaceRequesterTWith f a0 a' = do
118− let f' :: forall a' b'. ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m a'
242+ rec response <- fmap (fmapCheap unEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here
119− -> Event t (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m b')
243+ ((result0, requests0), v') <- f (runRequesterT a0 response) $ fmapCheap (`runRequesterT` response) a'
120− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (a', Event t b')
244+ requests <- holdDyn requests0 $ fmapCheap snd v'
121− f' x y = do
245+ return (result0, fmapCheap fst v')
122− r <- EventWriterT $ ask
246+
123− unRequesterT (f (runReaderT x r) (fmapCheap (`runReaderT` r) y))
124− in RequesterT $ runWithReplaceEventWriterTWith f' (coerce a0) (coerceEvent a')
126−sequenceDMapWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
247+traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
127 ( GCompare k
248 ( GCompare k
128 , Reflex t
249 , Reflex t
129 , MonadHold t m
250 , MonadHold t m
130− , PatchTarget (p' (Some k) (Event t (DMap (Tag (PrimState m)) request))) ~ Map (Some k) (Event t (DMap (Tag (PrimState m)) request))
251+ , PatchTarget (p' (Some k) (Event t (RequesterData request))) ~ Map (Some k) (Event t (RequesterData request))
131− , Patch (p' (Some k) (Event t (DMap (Tag (PrimState m)) request)))
252+ , Patch (p' (Some k) (Event t (RequesterData request)))
133 => (forall k' v1 v2. GCompare k'
255 => (forall k' v1 v2. GCompare k'
134 => (forall a. k' a -> v1 a -> m (v2 a))
256 => (forall a. k' a -> v1 a -> m (v2 a))
@@ -138,19 +260,30 @@ sequenceDMapWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
139 -> (forall v1. (forall a. v1 a -> v' a) -> p k v1 -> p k v')
261 -> (forall v1. (forall a. v1 a -> v' a) -> p k v1 -> p k v')
140 -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
262 -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
141− -> (forall v2. p' (Some k) v2 -> [v2])
263+ -> (forall v2. p' (Some k) v2 -> Map (Some k) v2)
142 -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
264 -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
143 -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
265 -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
144 -> DMap k v
266 -> DMap k v
145 -> Event t (p k v)
267 -> Event t (p k v)
146 -> RequesterT t request response m (DMap k v', Event t (p k v'))
268 -> RequesterT t request response m (DMap k v', Event t (p k v'))
147−sequenceDMapWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' =
269+traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
148− let base' :: forall v1 v2.
270+ rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
149− (forall a. k a -> v1 a -> ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m (v2 a))
271+ let responses :: EventSelector t (Const2 (Some k) (RequesterData response))
150− -> DMap k v1
272+ responses = fanMap $ fmapCheap unpack response
151− -> Event t (p k v1)
273+ unpack :: Entry response (Multi2 k) -> Map (Some k) (RequesterData response)
152− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (DMap k v2, Event t (p k v2))
274+ unpack = unEntry
153− base' f' x y = do
275+ pack :: Map (Some k) (RequesterData request) -> Entry request (Multi2 k)
154− r <- EventWriterT ask
276+ pack = Entry
155− unRequesterT $ base (\k v -> runReaderT (f' k v) r) x y
277+ f' :: forall a. k a -> v a -> m (Compose ((,) (Event t (RequesterData request))) v' a)
156− in RequesterT $ sequenceDMapWithAdjustEventWriterTWith base' mapPatch weakenPatchWith patchNewElements mergePatchIncremental (\k v -> unRequesterT $ f k v) (coerce dm0) (coerceEvent dm')
278+ f' k v = fmap (Compose . swap) $ runRequesterT (f k v) $ select responses (Const2 (Some.This k))
279+ (children0, children') <- base f' dm0 dm'
280+ let result0 = DMap.map (snd . getCompose) children0
281+ result' = fforCheap children' $ mapPatch $ snd . getCompose
282+ requests0 :: Map (Some k) (Event t (RequesterData request))
283+ requests0 = weakenDMapWith (fst . getCompose) children0
284+ requests' :: Event t (p' (Some k) (Event t (RequesterData request)))
285+ requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
286+ promptRequests :: Event t (Map (Some k) (RequesterData request))
287+ promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
288+ requests <- holdIncremental requests0 requests'
289+ return (result0, result')