1-- | This module provides 'RequesterT', the standard implementation of
3{-# LANGUAGE AllowAmbiguousTypes #-}
5{-# LANGUAGE EmptyDataDecls #-}
6{-# LANGUAGE ExistentialQuantification #-}
7{-# LANGUAGE FlexibleInstances #-}
9{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE Rank2Types #-}
12{-# LANGUAGE RecursiveDo #-}
13{-# LANGUAGE ScopedTypeVariables #-}
14{-# LANGUAGE StandaloneDeriving #-}
15{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE UndecidableInstances #-}
18#ifdef USE_REFLEX_OPTIMIZER
19{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21module Reflex.Requester.Base
24 , runWithReplaceRequesterTWith
25 , traverseIntMapWithKeyWithAdjustRequesterTWith
26 , traverseDMapWithKeyWithAdjustRequesterTWith
29 , traverseRequesterData
31 , singletonRequesterData
35import Reflex.Adjustable.Class
37import Reflex.Host.Class
38import Reflex.PerformEvent.Class
39import Reflex.PostBuild.Class
40import Reflex.Requester.Class
41import Reflex.TriggerEvent.Class
43import Control.Applicative (liftA2)
44import Control.Monad.Exception
45import Control.Monad.Identity
46import Control.Monad.Reader
47import Control.Monad.Ref
48import Control.Monad.State.Strict
51import Data.Dependent.Map (DMap, DSum (..))
52import qualified Data.Dependent.Map as DMap
53import Data.Functor.Compose
54import Data.Functor.Misc
55import Data.IntMap.Strict (IntMap)
56import qualified Data.IntMap.Strict as IntMap
58import qualified Data.Map as Map
59import Data.Monoid ((<>))
61import qualified Data.Semigroup as S
62import Data.Some (Some)
63import qualified Data.Some as Some
64import Data.Type.Equality
70--TODO: Make this module type-safe
72newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
74newtype RequesterData f = RequesterData (TagMap (Entry f))
76data RequesterDataKey a where
77 RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
78 RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead
79 RequesterDataKey_Multi2 :: {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
80 RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
82singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
83singletonRequesterData rdk v = case rdk of
84 RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
85 RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
86 RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
87 RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
89requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
90requesterDataToList (RequesterData m) = do
91 k :=> Entry e <- tagMapToList m
93 MyTagType_Single -> return $ RequesterDataKey_Single k :=> e
95 (k', e') <- IntMap.toList e
96 k'' :=> e'' <- requesterDataToList e'
97 return $ RequesterDataKey_Multi k k' k'' :=> e''
98 MyTagType_Multi2 -> do
99 (k', e') <- Map.toList e
100 (k'', e'') <- IntMap.toList e'
101 k''' :=> e''' <- requesterDataToList e''
102 return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
103 MyTagType_Multi3 -> do
104 (k', e') <- IntMap.toList e
105 (k'', e'') <- IntMap.toList e'
106 k''' :=> e''' <- requesterDataToList e''
107 return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e'''
109singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f
110singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v
112tagMapToList :: forall f. TagMap f -> [DSum MyTag f]
113tagMapToList (TagMap m) = f <$> IntMap.toList m
114 where f :: (Int, Any) -> DSum MyTag f
115 f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v
117traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g)
118traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m
120 g :: Int -> Any -> t Any
121 g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v)
123-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
124traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response)
125traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order
126 where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
127 go k (Entry request) = Entry <$> case myKeyType k of
128 MyTagType_Single -> f request
129 MyTagType_Multi -> traverse (traverseRequesterData f) request
130 MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
131 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
133data MyTagType :: * -> * where
134 MyTagType_Single :: MyTagType (Single a)
135 MyTagType_Multi :: MyTagType Multi
136 MyTagType_Multi2 :: MyTagType (Multi2 k)
137 MyTagType_Multi3 :: MyTagType Multi3
139myKeyType :: MyTag x -> MyTagType x
140myKeyType (MyTag k) = case k .&. 0x3 of
141 0x0 -> unsafeCoerce MyTagType_Single
142 0x1 -> unsafeCoerce MyTagType_Multi
143 0x2 -> unsafeCoerce MyTagType_Multi2
144 0x3 -> unsafeCoerce MyTagType_Multi3
145 t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
149data Multi2 (k :: * -> *)
152class MyTagTypeOffset x where
153 myTagTypeOffset :: proxy x -> Int
155instance MyTagTypeOffset (Single a) where
156 myTagTypeOffset _ = 0x0
158instance MyTagTypeOffset Multi where
159 myTagTypeOffset _ = 0x1
161instance MyTagTypeOffset (Multi2 k) where
162 myTagTypeOffset _ = 0x2
164instance MyTagTypeOffset Multi3 where
165 myTagTypeOffset _ = 0x3
167type family EntryContents request a where
168 EntryContents request (Single a) = request a
169 EntryContents request Multi = IntMap (RequesterData request)
170 EntryContents request (Multi2 k) = Map (Some k) (IntMap (RequesterData request))
171 EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
173newtype Entry request x = Entry { unEntry :: EntryContents request x }
175{-# INLINE singleEntry #-}
176singleEntry :: f a -> Entry f (Single a)
179{-# INLINE multiEntry #-}
180multiEntry :: IntMap (RequesterData f) -> Entry f Multi
183{-# INLINE unMultiEntry #-}
184unMultiEntry :: Entry f Multi -> IntMap (RequesterData f)
185unMultiEntry = unEntry
187-- | 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
188-- 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
189newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum)
191newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum)
193{-# INLINE castMyTagWrap #-}
194castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
195castMyTagWrap = coerce
197instance GEq MyTag where
198 (MyTag a) `geq` (MyTag b) =
200 then Just $ unsafeCoerce Refl
203instance GCompare MyTag where
204 (MyTag a) `gcompare` (MyTag b) =
205 case a `compare` b of
207 EQ -> unsafeCoerce GEQ
210instance GEq (MyTagWrap f) where
211 (MyTagWrap a) `geq` (MyTagWrap b) =
213 then Just $ unsafeCoerce Refl
216instance GCompare (MyTagWrap f) where
217 (MyTagWrap a) `gcompare` (MyTagWrap b) =
218 case a `compare` b of
220 EQ -> unsafeCoerce GEQ
223data RequesterState t (request :: * -> *) = RequesterState
224 { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
225 , _requesterState_requests :: ![(Int, Event t Any)]
228-- | A basic implementation of 'Requester'.
229newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a }
230 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
231-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
232#if MIN_VERSION_base(4,9,1)
233 , MonadAsyncException
237deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
238deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
239deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
240deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
242-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
243instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
245 mappend = liftA2 mappend
247instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
251-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
252-- requests are made, and responses should be provided in the input 'Event'.
253-- The 'Tag' keys will be used to return the responses to the same place the
254-- requests were issued.
256runRequesterT :: (Reflex t, Monad m)
257 => RequesterT t request response m a
258 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
259 -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
260runRequesterT (RequesterT a) responses = do
261 (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $
262 coerceEvent responses
263 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
265instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
266 type Request (RequesterT t request response m) = request
267 type Response (RequesterT t request response m) = response
268 requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a)))
269 requesting_ = void . tagRequest . fmapCheap singleEntry
271{-# INLINE tagRequest #-}
272tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
274 old <- RequesterT get
275 let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x)
277 RequesterT $ put $ RequesterState
278 { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4
279 , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old
283{-# INLINE responseFromTag #-}
284responseFromTag :: Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
285responseFromTag (MyTagWrap t) = do
286 responses :: EventSelectorInt t Any <- RequesterT ask
287 return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
289instance MonadTrans (RequesterT t request response) where
290 lift = RequesterT . lift . lift
292instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
293 type Performable (RequesterT t request response m) = Performable m
294 performEvent_ = lift . performEvent_
295 performEvent = lift . performEvent
297instance MonadRef m => MonadRef (RequesterT t request response m) where
298 type Ref (RequesterT t request response m) = Ref m
299 newRef = lift . newRef
300 readRef = lift . readRef
301 writeRef r = lift . writeRef r
303instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
304 newEventWithTrigger = lift . newEventWithTrigger
305 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
307instance MonadReader r m => MonadReader r (RequesterT t request response m) where
309 local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
310 reader = lift . reader
312instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where
313 runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
314 traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
315 {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
316 traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
317 traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
319requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
320requesting' = responseFromTag . castMyTagWrap <=< tagRequest
322{-# INLINABLE runWithReplaceRequesterTWith #-}
323runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
326 => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
327 -> RequesterT t request response m a
328 -> Event t (RequesterT t request response m b)
329 -> RequesterT t request response m (a, Event t b)
330runWithReplaceRequesterTWith f a0 a' = do
331 rec na' <- numberOccurrencesFrom 1 a'
332 responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here
333 let responses' = fanInt responses
334 ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na'
335 requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v'
336 return (result0, fmapCheap (fst . snd) v')
338{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-}
339traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p.
342 , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request)))
343 , Patch (p (Event t (IntMap (RequesterData request))))
347 => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v'))
348 -> IntMap (IntMap.Key, v)
349 -> Event t (p (IntMap.Key, v))
350 -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v')))
352 -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request))))
353 -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request))))
354 -> (IntMap.Key -> v -> RequesterT t request response m v')
357 -> RequesterT t request response m (IntMap v', Event t (p v'))
358traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
359 rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
360 let responses :: EventSelectorInt t (IntMap (RequesterData response))
361 responses = fanInt $ fmapCheap unpack response
362 unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response))
364 pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3
366 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
368 (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
369 return (fmapCheap (IntMap.singleton n) myRequests, result)
370 ndm' <- numberOccurrencesFrom 1 dm'
371 (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable
372 let result0 = fmap snd children0
373 result' = fforCheap children' $ fmap snd
374 requests0 :: IntMap (Event t (IntMap (RequesterData request)))
375 requests0 = fmap fst children0
376 requests' :: Event t (p (Event t (IntMap (RequesterData request))))
377 requests' = fforCheap children' $ fmap fst
378 promptRequests :: Event t (IntMap (IntMap (RequesterData request)))
379 promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
380 requests <- holdIncremental requests0 requests'
381 return (result0, result')
383{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-}
384traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
388 , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request)))
389 , Patch (p' (Some k) (Event t (IntMap (RequesterData request))))
392 => (forall k' v1 v2. GCompare k'
393 => (forall a. k' a -> v1 a -> m (v2 a))
396 -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
398 -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2)
399 -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
400 -> (forall v2. p' (Some k) v2 -> Map (Some k) v2)
401 -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
402 -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
405 -> RequesterT t request response m (DMap k v', Event t (p k v'))
406traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
407 rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
408 let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
409 responses = fanMap $ fmapCheap unpack response
410 unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
412 pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
414 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
415 f' k (Compose (n, v)) = do
416 (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
417 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
418 ndm' <- numberOccurrencesFrom 1 dm'
419 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
420 let result0 = DMap.map (snd . getCompose) children0
421 result' = fforCheap children' $ mapPatch $ snd . getCompose
422 requests0 :: Map (Some k) (Event t (IntMap (RequesterData request)))
423 requests0 = weakenDMapWith (fst . getCompose) children0
424 requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request))))
425 requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
426 promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request)))
427 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
428 requests <- holdIncremental requests0 requests'
429 return (result0, result')
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list