on d73838e help sign in
21 files changed+879−681
since splitsnapshots
unifiedsplit
ChangeLog.md+19 reflex.cabal+22−5 src/Data/List/Deferred.hs+60 src/Data/List/NonEmpty/Deferred.hs+13 src/Data/List/NonEmpty/Deferred/Internal.hs+30 src/Data/TagMap.hs+43 src/Data/Unique/Tag/Local.hs+11 src/Data/Unique/Tag/Local/Internal.hs+59 src/Reflex/Class.hs+61−33 src/Reflex/Dynamic.hs+1−1 src/Reflex/EventWriter/Base.hs+8−56 src/Reflex/FanTag.hs+23 src/Reflex/NotReady/Class.hs+5 src/Reflex/PerformEvent/Base.hs+82−34 src/Reflex/Profiled.hs−1 src/Reflex/Requester/Base.hs+13−527 src/Reflex/Requester/Base/Internal.hs+310 src/Reflex/Requester/Class.hs+1−1 test/Adjustable.hs+3−2 test/EventWriterT.hs+15−11 test/RequesterT.hs+100−10
ChangeLog.md+19
@@ -14,6 +14,25 @@
1414* Add cabal flags `debug-propagation` and `debug-event-cycles` to build in debugging
1515 code for performance and for cyclic dependencies between events
1616
17+* Refactor of `Reflex.Requester`:
18+ * Updated:
19+ * `RequesterData` to `RequestData`, `RequesterEnvelope`, and `ResponseData`
20+ * `singletonRequesterData` to `singletonRequestData` and `singletonResponseData`
21+ * `requesterDataToList` to `requestEnvelopesToDSums`
22+ * Removed:
23+ * `RequesterDataKey`
24+ * `multiEntry`
25+ * `unMultiEntry`
26+ * `withRequesterT`
27+ * `runWithReplaceRequesterTWith`
28+ * `requesting'`
29+ * `traverseIntMapWithKeyWithAdjustRequesterTWith`
30+ * `traverseDMapWithKeyWithAdjustRequesterTWith`
31+
32+* Added `Data.List.Deferred` and `Data.List.NonEmpty.Deferred` for optimizing `<>` operations.
33+
34+* Added `Data.TagMap`, `Reflex.FanTag`, and `Data.Unique.Tag.Local` to improve request and response tagging.
35+
1736## 0.6.3
1837
1938* `Data.WeakBag.traverse` and `Data.FastWeakBag.traverse` have been deprecated.
reflex.cabal+22−5
@@ -68,11 +68,12 @@ library
6868 containers >= 0.6 && < 0.7,
6969 data-default >= 0.5 && < 0.8,
7070 dependent-map >= 0.3 && < 0.4,
71+ dlist == 0.8.*,
7172 exception-transformers == 0.4.*,
7273 lens >= 4.7 && < 5,
7374 monad-control >= 1.0.1 && < 1.1,
7475 mtl >= 2.1 && < 2.3,
75− patch >= 0.0.1 && < 0.1,
76+ patch >= 0.0.2 && < 0.1,
7677 prim-uniq >= 0.1.0.1 && < 0.2,
7778 primitive >= 0.5 && < 0.8,
7879 profunctors >= 5.3 && < 5.6,
@@ -100,13 +101,19 @@ library
100101 Data.AppendMap,
101102 Data.FastMutableIntMap,
102103 Data.FastWeakBag,
104+ Data.List.Deferred,
105+ Data.List.NonEmpty.Deferred,
106+ Data.List.NonEmpty.Deferred.Internal,
103107 Data.Map.Misc,
108+ Data.TagMap,
109+ Data.Unique.Tag.Local,
110+ Data.Unique.Tag.Local.Internal,
104111 Data.WeakBag,
105112 Reflex,
106− Reflex.Class,
107113 Reflex.Adjustable.Class,
108114 Reflex.BehaviorWriter.Base,
109115 Reflex.BehaviorWriter.Class,
116+ Reflex.Class,
110117 Reflex.Collection,
111118 Reflex.Dynamic,
112119 Reflex.Dynamic.Uniq,
@@ -116,6 +123,7 @@ library
116123 Reflex.EventWriter,
117124 Reflex.EventWriter.Base,
118125 Reflex.EventWriter.Class,
126+ Reflex.FanTag,
119127 Reflex.FastWeak,
120128 Reflex.FunctorMaybe,
121129 Reflex.Host.Class,
@@ -130,6 +138,7 @@ library
130138 Reflex.Query.Base,
131139 Reflex.Query.Class,
132140 Reflex.Requester.Base,
141+ Reflex.Requester.Base.Internal,
133142 Reflex.Requester.Class,
134143 Reflex.Spider,
135144 Reflex.Spider.Internal,
@@ -150,6 +159,7 @@ library
150159 patch:Data.Patch.MapWithMove as Reflex.Patch.MapWithMove
151160
152161 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
162+ ghc-prof-options: -fprof-auto-calls
153163
154164 if flag(debug-trace-events)
155165 cpp-options: -DDEBUG_TRACE_EVENTS
@@ -192,6 +202,7 @@ test-suite semantics
192202 main-is: semantics.hs
193203 hs-source-dirs: test
194204 ghc-options: -O2 -Wall -rtsopts
205+ ghc-prof-options: -fprof-auto-calls
195206 build-depends:
196207 base,
197208 bifunctors,
@@ -218,6 +229,7 @@ test-suite CrossImpl
218229 main-is: Reflex/Test/CrossImpl.hs
219230 hs-source-dirs: test
220231 ghc-options: -O2 -Wall -rtsopts
232+ ghc-prof-options: -fprof-auto-calls
221233 build-depends:
222234 base,
223235 containers,
@@ -307,15 +319,18 @@ test-suite RequesterT
307319 hs-source-dirs: test
308320 build-depends: base
309321 , containers
322+ , constraints
323+ , constraints-extras
310324 , deepseq
311− , dependent-sum
312325 , dependent-map
326+ , dependent-sum
313327 , lens
314328 , mtl
329+ , primitive
330+ , ref-tf
331+ , reflex
315332 , these
316333 , transformers
317− , reflex
318− , ref-tf
319334
320335 if flag(split-these)
321336 build-depends: these-lens
@@ -418,6 +433,7 @@ benchmark spider-bench
418433 hs-source-dirs: bench test
419434 main-is: Main.hs
420435 ghc-options: -Wall -O2 -rtsopts
436+ ghc-prof-options: -fprof-auto-calls
421437 build-depends:
422438 base,
423439 containers,
@@ -444,6 +460,7 @@ benchmark saulzar-bench
444460 c-sources: bench-cbits/checkCapability.c
445461 main-is: RunAll.hs
446462 ghc-options: -Wall -O2 -rtsopts -threaded
463+ ghc-prof-options: -fprof-auto-calls
447464 build-depends:
448465 base,
449466 containers,
src/Data/List/Deferred.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# LANGUAGE LambdaCase #-}
2+module Data.List.Deferred
3+ ( Deferred
4+ , empty
5+ , singleton
6+ , toNonEmpty
7+ , fromNonEmpty
8+ , toList
9+ ) where
10+
11+import Data.List.NonEmpty.Deferred.Internal (NonEmptyDeferred (..))
12+import qualified Data.List.NonEmpty.Deferred as NonEmpty
13+
14+data Deferred a
15+ = Deferred_Empty
16+ | Deferred_Singleton a
17+ | Deferred_Append !(NonEmptyDeferred a) !(NonEmptyDeferred a)
18+
19+{-# INLINE toNonEmpty #-}
20+toNonEmpty :: Deferred a -> Maybe (NonEmptyDeferred a)
21+toNonEmpty = \case
22+ Deferred_Empty -> Nothing
23+ Deferred_Singleton a -> Just $ NonEmpty.singleton a
24+ Deferred_Append a b -> Just $ a <> b
25+
26+{-# INLINE fromNonEmpty #-}
27+fromNonEmpty :: NonEmptyDeferred a -> Deferred a
28+fromNonEmpty = \case
29+ NonEmptyDeferred_Singleton a -> Deferred_Singleton a
30+ NonEmptyDeferred_Append a b -> Deferred_Append a b
31+
32+{-# INLINE empty #-}
33+empty :: Deferred a
34+empty = Deferred_Empty
35+
36+{-# INLINE singleton #-}
37+singleton :: a -> Deferred a
38+singleton = fromNonEmpty . NonEmpty.singleton
39+
40+{-# INLINE toList #-}
41+toList :: Deferred a -> [a]
42+toList = \case
43+ Deferred_Empty -> []
44+ Deferred_Singleton a -> [a]
45+ Deferred_Append a b -> NonEmpty.toList $ a <> b
46+
47+instance Semigroup (Deferred a) where
48+ (<>) = \case
49+ Deferred_Empty -> id
50+ a@(Deferred_Singleton va) -> \case
51+ Deferred_Empty -> a
52+ Deferred_Singleton vb -> Deferred_Append (NonEmpty.singleton va) (NonEmpty.singleton vb)
53+ Deferred_Append b1 b2 -> Deferred_Append (NonEmpty.singleton va) (b1 <> b2)
54+ a@(Deferred_Append a1 a2) -> \case
55+ Deferred_Empty -> a
56+ Deferred_Singleton vb -> Deferred_Append (a1 <> a2) (NonEmpty.singleton vb)
57+ Deferred_Append b1 b2 -> Deferred_Append (a1 <> a2) (b1 <> b2)
58+
59+instance Monoid (Deferred a) where
60+ mempty = Deferred_Empty
src/Data/List/NonEmpty/Deferred.hsnew file+13
@@ -0,0 +1,13 @@
1+-- | Uses a non-associative internal structure to represent a NonEmpty list, but
2+-- prevents external observers from observing the non-associativity. This
3+-- allows O(1) '(<>)'.
4+
5+{-# LANGUAGE LambdaCase #-}
6+module Data.List.NonEmpty.Deferred
7+ ( NonEmptyDeferred
8+ , singleton
9+ , toNonEmpty
10+ , toList
11+ ) where
12+
13+import Data.List.NonEmpty.Deferred.Internal
src/Data/List/NonEmpty/Deferred/Internal.hsnew file+30
@@ -0,0 +1,30 @@
1+{-# LANGUAGE LambdaCase #-}
2+module Data.List.NonEmpty.Deferred.Internal where
3+
4+import Data.List.NonEmpty (NonEmpty (..))
5+import qualified Data.List.NonEmpty as NonEmpty
6+
7+data NonEmptyDeferred a
8+ = NonEmptyDeferred_Singleton a
9+ | NonEmptyDeferred_Append !(NonEmptyDeferred a) !(NonEmptyDeferred a)
10+
11+{-# INLINE singleton #-}
12+singleton :: a -> NonEmptyDeferred a
13+singleton = NonEmptyDeferred_Singleton
14+
15+{-# INLINE toNonEmpty #-}
16+toNonEmpty :: NonEmptyDeferred a -> NonEmpty a
17+toNonEmpty = go []
18+ where go t = \case
19+ NonEmptyDeferred_Singleton a -> a :| t
20+ NonEmptyDeferred_Append a b -> go (NonEmpty.toList $ go t b) a
21+
22+{-# INLINE toList #-}
23+toList :: NonEmptyDeferred a -> [a]
24+toList = go []
25+ where go t = \case
26+ NonEmptyDeferred_Singleton a -> a : t
27+ NonEmptyDeferred_Append a b -> go (go t b) a
28+
29+instance Semigroup (NonEmptyDeferred a) where
30+ (<>) = NonEmptyDeferred_Append
src/Data/TagMap.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE PolyKinds #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+module Data.TagMap
4+ ( TagMap
5+ , unTagMap
6+ , fromDMap
7+ , toDMap
8+ , fromList
9+ , insert
10+ , size
11+ , singletonTagMap
12+ ) where
13+
14+import Data.IntMap (IntMap)
15+import qualified Data.IntMap as IntMap
16+import Data.Dependent.Map (DMap)
17+import qualified Data.Dependent.Map as DMap
18+import Data.Dependent.Sum (DSum (..))
19+import Data.Unique.Tag.Local
20+
21+import GHC.Exts (Any)
22+import Unsafe.Coerce
23+
24+-- | Like DMap, but with 'Data.Unique.Tag.Tag' as the keys. Implemented using 'Data.IntMap.IntMap' under the hood.
25+newtype TagMap x (v :: k -> *) = TagMap { unTagMap :: IntMap Any }
26+
27+fromDMap :: forall k x (v :: k -> *). DMap (Tag x) v -> TagMap x v
28+fromDMap = TagMap . IntMap.fromDistinctAscList . fmap (\((k :: Tag x (a :: k)) :=> v) -> (tagId k, (unsafeCoerce :: v a -> Any) v)) . DMap.toAscList
29+
30+toDMap :: forall x v. TagMap x v -> DMap (Tag x) v
31+toDMap = DMap.fromDistinctAscList . fmap (\(k, v) -> (unsafeTagFromId k :=> (unsafeCoerce :: Any -> v a) v)) . IntMap.toAscList . unTagMap
32+
33+insert :: forall x a v. Tag x a -> v a -> TagMap x v -> TagMap x v
34+insert k v = TagMap . IntMap.insert (tagId k) ((unsafeCoerce :: v a -> Any) v) . unTagMap
35+
36+fromList :: [DSum (Tag x) v] -> TagMap x v
37+fromList = TagMap . IntMap.fromList . fmap (\(t :=> v) -> (tagId t, (unsafeCoerce :: v a -> Any) v))
38+
39+size :: TagMap x v -> Int
40+size = IntMap.size . unTagMap
41+
42+singletonTagMap :: forall ps k v a. Tag ps k -> v a -> TagMap k v
43+singletonTagMap tag v = TagMap $ IntMap.singleton (tagId tag) $ (unsafeCoerce :: v a -> Any) v
src/Data/Unique/Tag/Local.hsnew file+11
@@ -0,0 +1,11 @@
1+module Data.Unique.Tag.Local
2+ ( Tag
3+ , TagGen (..)
4+ , tagId
5+ , unsafeTagFromId
6+ , newTag
7+ , newTagGen
8+ , withTagGen
9+ ) where
10+
11+import Data.Unique.Tag.Local.Internal
src/Data/Unique/Tag/Local/Internal.hsnew file+59
@@ -0,0 +1,59 @@
1+-- | The type-safety of this module depends on two assumptions:
2+-- 1. If the `s` parameters on two `TagGen`s can unify, then they contain the same MutVar
3+-- 2. Two Tag values made from the same TagGen never contain the same Int
4+
5+{-# LANGUAGE EmptyDataDecls #-}
6+{-# LANGUAGE DeriveFunctor #-}
7+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8+{-# LANGUAGE TypeFamilies #-}
9+{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE MagicHash #-}
13+module Data.Unique.Tag.Local.Internal where
14+
15+import Control.Monad.Primitive
16+import Data.Primitive.MutVar
17+import Data.GADT.Compare
18+import Data.Some
19+import GHC.Exts (Int (..), Int#, MutVar#, unsafeCoerce#)
20+
21+import Unsafe.Coerce
22+
23+-- `x` is which generator it's from
24+-- `a` is the type of the thing it's tagging
25+newtype Tag x a = Tag Int
26+
27+tagId :: Tag x a -> Int
28+tagId (Tag n) = n
29+
30+-- | WARNING: If you construct a tag with the wrong type, it will result in
31+-- incorrect unsafeCoerce applications, which can segfault or cause arbitrary
32+-- other damage to your program
33+unsafeTagFromId :: Int -> Tag x a
34+unsafeTagFromId n = Tag n
35+
36+-- We use Int because it is supported by e.g. IntMap
37+newtype TagGen ps s = TagGen { unTagGen :: MutVar ps Int }
38+
39+instance Show (TagGen ps s) where
40+ show (TagGen (MutVar m)) = show $ I# ((unsafeCoerce# :: MutVar# ps Int -> Int#) m)
41+
42+instance GEq (TagGen ps) where
43+ TagGen a `geq` TagGen b =
44+ if a == b
45+ then Just $ unsafeCoerce Refl
46+ else Nothing
47+
48+newTag :: PrimMonad m => TagGen (PrimState m) s -> m (Tag s a)
49+newTag (TagGen r) = do
50+ n <- atomicModifyMutVar' r $ \x -> (succ x, x)
51+ pure $ Tag n
52+
53+newTagGen :: PrimMonad m => m (Some (TagGen (PrimState m)))
54+newTagGen = Some . TagGen <$> newMutVar minBound
55+
56+withTagGen :: PrimMonad m => (forall s. TagGen (PrimState m) s -> m a) -> m a
57+withTagGen f = do
58+ g <- newTagGen
59+ withSome g f
src/Reflex/Class.hs+61−33
@@ -169,6 +169,8 @@ module Reflex.Class
169169 , tagCheap
170170 , mergeWithCheap
171171 , mergeWithCheap'
172+ , sconcatCheap
173+ , mconcatCheap
172174 -- * Slow, but general, implementations
173175 , slowHeadE
174176 ) where
@@ -210,6 +212,7 @@ import Data.IntMap.Strict (IntMap)
210212import qualified Data.IntMap.Strict as IntMap
211213import Data.List.NonEmpty (NonEmpty (..))
212214import Data.Map (Map)
215+import qualified Data.Map as Map
213216import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
214217import Data.Some (Some(Some))
215218import Data.String
@@ -739,11 +742,6 @@ instance Reflex t => Functor (Event t) where
739742 {-# INLINE (<$) #-}
740743 x <$ e = fmapCheap (const x) e
741744
742−-- TODO Remove this instance
743−instance Reflex t => FunctorMaybe (Event t) where
744− {-# INLINE fmapMaybe #-}
745− fmapMaybe = mapMaybe
746−
747745instance Reflex t => Filterable (Event t) where
748746 {-# INLINE mapMaybe #-}
749747 mapMaybe f = push $ return . f
@@ -897,6 +895,12 @@ instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
897895 sconcat = fmap sconcat . mergeList . toList
898896 stimes n = fmap $ stimes n
899897
898+sconcatCheap :: (Semigroup a, Reflex t) => NonEmpty (Event t a) -> Event t a
899+sconcatCheap = fmapCheap sconcat . mergeList . toList
900+
901+mconcatCheap :: (Semigroup a, Reflex t) => [Event t a] -> Event t a
902+mconcatCheap = fmapCheap sconcat . mergeList
903+
900904instance (Semigroup a, Reflex t) => Monoid (Event t a) where
901905 mempty = never
902906 mappend = (<>)
@@ -911,6 +915,8 @@ mergeWith = mergeWith' id
911915
912916{-# INLINE mergeWith' #-}
913917mergeWith' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
918+mergeWith' _ _ [] = never
919+mergeWith' f _ [e] = fmap f e
914920mergeWith' f g es = fmap (Prelude.foldl1 g . fmap f)
915921 . mergeInt
916922 . IntMap.fromDistinctAscList
@@ -928,6 +934,7 @@ leftmost = mergeWith const
928934-- time.
929935mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
930936mergeList [] = never
937+mergeList [e] = fmapCheap (:|[]) e
931938mergeList es = mergeWithFoldCheap' id es
932939
933940unsafeMapIncremental
@@ -944,10 +951,6 @@ unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIn
944951mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
945952mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
946953
947−-- | Like 'mergeMap' but for 'IntMap'.
948−mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
949−mergeIntMap = fmap dmapToIntMap . merge . intMapWithFunctorToDMap
950−
951954-- | Create a merge whose parents can change over time
952955mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
953956mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
@@ -1024,33 +1027,45 @@ switchHoldPromptly ea0 eea = do
10241027switchHoldPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
10251028switchHoldPromptOnly e0 e' = do
10261029 eLag <- switch <$> hold e0 e'
1027− return $ coincidence $ leftmost [e', eLag <$ eLag]
1030+ return $ fmapMaybeCheap id $ leftmost
1031+ [ fmapCheap Just $ coincidence e'
1032+ , fmapCheap (const Nothing) e'
1033+ , fmapCheap Just eLag
1034+ ]
10281035
10291036-- | When the given outer event fires, condense the inner events into the contained patch. Non-firing inner events will be replaced with deletions.
10301037coincidencePatchMap :: (Reflex t, Ord k) => Event t (PatchMap k (Event t v)) -> Event t (PatchMap k v)
1031−coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ ffor e $ \(PatchMap m) -> mergeMap $ ffor m $ \case
1032− Nothing -> fmapCheap (const Nothing) e
1033− Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1038+coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ fforCheap e $ \(PatchMap m) -> if Map.null m then never else
1039+ let firingNewItems = mergeMap $ fforMaybe m $ \case
1040+ Nothing -> Nothing
1041+ Just ev -> Just $ fmapCheap Just ev
1042+ oldItemMask = (Nothing <$ m) <$ e
1043+ in alignWith (mergeThese Map.union) firingNewItems oldItemMask -- Must be left-biased
10341044
10351045-- | See 'coincidencePatchMap'
10361046coincidencePatchIntMap :: Reflex t => Event t (PatchIntMap (Event t v)) -> Event t (PatchIntMap v)
1037−coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ ffor e $ \(PatchIntMap m) -> mergeIntMap $ ffor m $ \case
1038− Nothing -> fmapCheap (const Nothing) e
1039− Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1047+coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ fforCheap e $ \(PatchIntMap m) -> if IntMap.null m then never else
1048+ let firingNewItems = mergeIntMap $ fforMaybe m $ \case
1049+ Nothing -> Nothing
1050+ Just ev -> Just $ fmapCheap Just ev
1051+ oldItemMask = (Nothing <$ m) <$ e
1052+ in alignWith (mergeThese IntMap.union) firingNewItems oldItemMask -- Must be left-biased
10401053
10411054-- | See 'coincidencePatchMap'
10421055coincidencePatchMapWithMove :: (Reflex t, Ord k) => Event t (PatchMapWithMove k (Event t v)) -> Event t (PatchMapWithMove k v)
1043−coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ ffor e $ \p -> mergeMap $ ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1044− PatchMapWithMove.From_Delete -> fforCheap e $ \_ ->
1045− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1046− PatchMapWithMove.From_Move k -> fforCheap e $ \_ ->
1047− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1048− PatchMapWithMove.From_Insert ev -> leftmost
1049− [ fforCheap ev $ \v ->
1050− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1051− , fforCheap e $ \_ ->
1052− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1053− ]
1056+coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ fforCheap e $ \p -> if Map.null (unPatchMapWithMove p) then never else
1057+ let firingNewItems = mergeMap $ fforMaybe (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1058+ PatchMapWithMove.From_Insert ev -> Just $ fforCheap ev $ \v ->
1059+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1060+ _ -> Nothing
1061+ oldItemMask = fforCheap e $ \_ -> ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1062+ PatchMapWithMove.From_Delete ->
1063+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1064+ PatchMapWithMove.From_Move k ->
1065+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1066+ PatchMapWithMove.From_Insert _ ->
1067+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1068+ in alignWith (mergeThese Map.union) firingNewItems oldItemMask -- Must be left-biased
10541069
10551070-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
10561071-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
@@ -1303,7 +1318,7 @@ accumMaybeMDyn
13031318 -> Event t b
13041319 -> m (Dynamic t a)
13051320accumMaybeMDyn f z e = do
1306− rec let e' = flip push e $ \o -> do
1321+ rec let e' = flip pushCheap e $ \o -> do
13071322 v <- sample $ current d'
13081323 f v o
13091324 d' <- holdDyn z e'
@@ -1359,8 +1374,8 @@ mapAccumMaybeMDyn f z e = do
13591374 return $ case result of
13601375 (Nothing, Nothing) -> Nothing
13611376 _ -> Just result
1362− d' <- holdDyn z $ mapMaybe fst e'
1363− return (d', mapMaybe snd e')
1377+ d' <- holdDyn z $ fmapMaybeCheap fst e'
1378+ return (d', fmapMaybeCheap snd e')
13641379
13651380-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
13661381-- with the provided function.
@@ -1398,7 +1413,7 @@ accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
13981413{-# INLINE accumMaybeMB #-}
13991414accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
14001415accumMaybeMB f z e = do
1401− rec let e' = flip push e $ \o -> do
1416+ rec let e' = flip pushCheap e $ \o -> do
14021417 v <- sample d'
14031418 f v o
14041419 d' <- hold z e'
@@ -1448,8 +1463,8 @@ mapAccumMaybeMB f z e = do
14481463 return $ case result of
14491464 (Nothing, Nothing) -> Nothing
14501465 _ -> Just result
1451− d' <- hold z $ mapMaybe fst e'
1452− return (d', mapMaybe snd e')
1466+ d' <- hold z $ fmapMaybeCheap fst e'
1467+ return (d', fmapMaybeCheap snd e')
14531468
14541469-- | Accumulate occurrences of an 'Event', producing an output occurrence each
14551470-- time. Discard the underlying 'Accumulator'.
@@ -1659,6 +1674,8 @@ mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
16591674-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
16601675{-# INLINE mergeWithFoldCheap' #-}
16611676mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1677+mergeWithFoldCheap' _ [] = never
1678+mergeWithFoldCheap' f [e] = fmapCheap (f . (:|[])) e
16621679mergeWithFoldCheap' f es =
16631680 fmapCheap (f . (\(h : t) -> h :| t) . IntMap.elems)
16641681 . mergeInt
@@ -1673,7 +1690,18 @@ mergeWithFoldCheap' f es =
16731690-- | See 'switchHoldPromptly'
16741691switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
16751692switchPromptly = switchHoldPromptly
1693+
16761694{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
16771695-- | See 'switchHoldPromptOnly'
16781696switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
16791697switchPromptOnly = switchHoldPromptOnly
1698+
1699+{-# DEPRECATED mergeIntMap "Use 'mergeInt' instead" #-}
1700+-- | Like 'mergeMap' but for 'IntMap'.
1701+mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
1702+mergeIntMap = mergeInt
1703+
1704+-- NOTE: A deprecation warning is expected on this instance
1705+instance Reflex t => FunctorMaybe (Event t) where
1706+ {-# INLINE fmapMaybe #-}
1707+ fmapMaybe = mapMaybe
src/Reflex/Dynamic.hs+1−1
@@ -80,7 +80,6 @@ import Data.Functor.Compose
8080import Data.Functor.Misc
8181import Reflex.Class
8282
83−import Control.Monad
8483import Control.Monad.Fix
8584import Control.Monad.Identity
8685import Data.Align
@@ -192,6 +191,7 @@ switchPromptlyDyn de =
192191 let eLag = switch $ current de
193192 eCoincidences = coincidence $ updated de
194193 in leftmost [eCoincidences, eLag]
194+--TODO: switchPromptlyDyn should get the `only` treatment like switchHoldPromptOnly
195195
196196-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
197197splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
src/Reflex/EventWriter/Base.hs+8−56
@@ -39,82 +39,34 @@ import Control.Monad.Primitive
3939import Control.Monad.Reader
4040import Control.Monad.Ref
4141import Control.Monad.State.Strict
42−import Data.Dependent.Map (DMap, DSum (..))
42+import Data.Dependent.Map (DMap)
4343import qualified Data.Dependent.Map as DMap
4444import Data.Functor.Compose
4545import Data.Functor.Misc
46−import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
46+import Data.GADT.Compare (GCompare (..))
4747import Data.IntMap.Strict (IntMap)
4848import qualified Data.IntMap.Strict as IntMap
4949import Data.List.NonEmpty (NonEmpty (..))
50+import Data.List.Deferred (Deferred)
51+import qualified Data.List.Deferred as Deferred
5052import Data.Map (Map)
5153import qualified Data.Map as Map
5254import Data.Semigroup
5355import Data.Some (Some)
5456import Data.Tuple
55−import Data.Type.Equality
56−
57−import Unsafe.Coerce
58−
59−{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
60−newtype TellId w x
61− = TellId Int -- ^ WARNING: Do not construct this directly; use 'tellId' instead
62− deriving (Show, Eq, Ord, Enum)
63−
64−tellId :: Int -> TellId w w
65−tellId = TellId
66−{-# INLINE tellId #-}
67−
68−tellIdRefl :: TellId w x -> w :~: x
69−tellIdRefl _ = unsafeCoerce Refl
70−
71−withTellIdRefl :: TellId w x -> (w ~ x => r) -> r
72−withTellIdRefl tid r = case tellIdRefl tid of
73− Refl -> r
74−
75−instance GEq (TellId w) where
76− a `geq` b =
77− withTellIdRefl a $
78− withTellIdRefl b $
79− if a == b
80− then Just Refl
81− else Nothing
82−
83−instance GCompare (TellId w) where
84− a `gcompare` b =
85− withTellIdRefl a $
86− withTellIdRefl b $
87− case a `compare` b of
88− LT -> GLT
89− EQ -> GEQ
90− GT -> GGT
91−
92−data EventWriterState t w = EventWriterState
93− { _eventWriterState_nextId :: {-# UNPACK #-} !Int -- Always negative (and decreasing over time)
94− , _eventWriterState_told :: ![DSum (TellId w) (Event t)] -- In increasing order
95− }
9657
9758-- | A basic implementation of 'EventWriter'.
98−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
59+newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (Deferred (Event t w)) m a }
9960 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
10061
10162-- | Run a 'EventWriterT' action.
10263runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
10364runEventWriterT (EventWriterT a) = do
104− (result, requests) <- runStateT a $ EventWriterState (-1) []
105− let combineResults :: DMap (TellId w) Identity -> w
106− combineResults = sconcat
107− . (\(h : t) -> h :| t) -- Unconditional; 'merge' guarantees that it will only fire with non-empty DMaps
108− . 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
109− 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
65+ (result, requests) <- runStateT a mempty
66+ return (result, mconcatCheap $ Deferred.toList requests)
11067
11168instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m) where
112− tellEvent w = EventWriterT $ modify $ \old ->
113− let myId = _eventWriterState_nextId old
114− in EventWriterState
115− { _eventWriterState_nextId = pred myId
116− , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
117− }
69+ tellEvent w = EventWriterT $ modify (<> Deferred.singleton w)
11870
11971instance MonadTrans (EventWriterT t w) where
12072 lift = EventWriterT . lift
src/Reflex/FanTag.hsnew file+23
@@ -0,0 +1,23 @@
1+{-# LANGUAGE PolyKinds #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+module Reflex.FanTag
4+ ( EventSelectorTag
5+ , unEventSelectorTag
6+ , fanTag
7+ , selectTag
8+ ) where
9+
10+import Data.Unique.Tag.Local
11+import Data.TagMap
12+import Reflex.Class
13+
14+import GHC.Exts (Any)
15+import Unsafe.Coerce
16+
17+newtype EventSelectorTag t x (v :: k -> *) = EventSelectorTag { unEventSelectorTag :: EventSelectorInt t Any }
18+
19+fanTag :: Reflex t => Event t (TagMap x v) -> EventSelectorTag t x v
20+fanTag = EventSelectorTag . fanInt . fmapCheap unTagMap
21+
22+selectTag :: forall t x v a. Reflex t => EventSelectorTag t x v -> Tag x a -> Event t (v a)
23+selectTag (EventSelectorTag s) = fmapCheap (unsafeCoerce :: Any -> v a) . selectInt s . tagId
src/Reflex/NotReady/Class.hs+5
@@ -25,6 +25,7 @@ import Reflex.PerformEvent.Base (PerformEventT (..))
2525import Reflex.PostBuild.Base (PostBuildT)
2626import Reflex.Query.Base (QueryT)
2727import Reflex.Requester.Base (RequesterT)
28+import Reflex.Requester.Base.Internal (RequesterInternalT)
2829import Reflex.TriggerEvent.Base (TriggerEventT)
2930
3031class Monad m => NotReady t m | m -> t where
@@ -72,6 +73,10 @@ instance NotReady t m => NotReady t (RequesterT t request response m) where
7273 notReadyUntil = lift . notReadyUntil
7374 notReady = lift notReady
7475
76+instance NotReady t m => NotReady t (RequesterInternalT s t request response m) where
77+ notReadyUntil = lift . notReadyUntil
78+ notReady = lift notReady
79+
7580instance NotReady t m => NotReady t (TriggerEventT t m) where
7681 notReadyUntil = lift . notReadyUntil
7782 notReady = lift notReady
src/Reflex/PerformEvent/Base.hs+82−34
@@ -28,8 +28,10 @@ import Reflex.Class
2828import Reflex.Adjustable.Class
2929import Reflex.Host.Class
3030import Reflex.PerformEvent.Class
31−import Reflex.Requester.Base
31+import Reflex.Requester.Base.Internal
3232import Reflex.Requester.Class
33+import Reflex.EventWriter.Class
34+import Reflex.EventWriter.Base
3335
3436import Control.Lens
3537import Control.Monad.Exception
@@ -37,13 +39,21 @@ import Control.Monad.Identity
3739import Control.Monad.Primitive
3840import Control.Monad.Reader
3941import Control.Monad.Ref
40−import Data.Coerce
4142import Data.Dependent.Map (DMap)
4243import qualified Data.Dependent.Map as DMap
4344import Data.Dependent.Sum
45+import Data.Functor.Compose
46+import Data.Functor.Misc
4447import Data.IntMap.Strict (IntMap)
4548import qualified Data.IntMap.Strict as IntMap
49+import Data.List.NonEmpty (NonEmpty (..))
50+import Data.List.NonEmpty.Deferred (NonEmptyDeferred)
51+import Data.Map (Map)
52+import qualified Data.Map as Map
53+import Data.Semigroup (Semigroup (sconcat))
4654import qualified Data.Semigroup as S
55+import Data.Unique.Tag.Local
56+import Data.Tuple
4757
4858-- | A function that fires events for the given 'EventTrigger's and then runs
4959-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
@@ -68,7 +78,7 @@ instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m
6878 type PrimState (PerformEventT t m) = PrimState (HostFrame t)
6979 primitive = PerformEventT . lift . primitive
7080
71−instance (ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) where
81+instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
7282 type Performable (PerformEventT t m) = HostFrame t
7383 {-# INLINABLE performEvent_ #-}
7484 performEvent_ = PerformEventT . requesting_
@@ -76,37 +86,74 @@ instance (ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) wh
7686 performEvent = PerformEventT . requestingIdentity
7787
7888instance (ReflexHost t, PrimMonad (HostFrame t)) => Adjustable t (PerformEventT t m) where
79− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
80− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
81− f a0 a' = do
82− result0 <- lift a0
83− result' <- requestingIdentity a'
84− return (result0, result')
85− traverseIntMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseIntMapWithKeyWithAdjustRequesterTWith (defaultAdjustIntBase traverseIntMapPatchWithKey) patchIntMapNewElementsMap mergeIntIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
86− traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
87− traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
88−
89−defaultAdjustBase :: forall t v v2 k' p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
90− => ((forall a. k' a -> v a -> HostFrame t (v2 a)) -> p k' v -> HostFrame t (p k' v2))
91− -> (forall a. k' a -> v a -> HostFrame t (v2 a))
92− -> DMap k' v
93− -> Event t (p k' v)
94− -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' v2, Event t (p k' v2))
95−defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
96− result0 <- lift $ DMap.traverseWithKey f' dm0
97− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
98− return (result0, result')
99−
100−defaultAdjustIntBase :: forall t v v2 p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
101− => ((IntMap.Key -> v -> HostFrame t v2) -> p v -> HostFrame t (p v2))
102− -> (IntMap.Key -> v -> HostFrame t v2)
103− -> IntMap v
104− -> Event t (p v)
105− -> RequesterT t (HostFrame t) Identity (HostFrame t) (IntMap v2, Event t (p v2))
106−defaultAdjustIntBase traversePatchWithKey f' dm0 dm' = do
107− result0 <- lift $ IntMap.traverseWithKey f' dm0
108− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
109− return (result0, result')
89+ {-# INLINE runWithReplace #-}
90+ runWithReplace a0 a' = PerformEventT $ RequesterT $ do
91+ env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
92+ let runA :: forall a. PerformEventT t m a -> HostFrame t (a, Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))
93+ runA (PerformEventT (RequesterT a)) = runEventWriterT $ runReaderT (unRequesterInternalT a) env
94+ (result0, requests0) <- lift $ runA a0
95+ newA <- requestingIdentity $ runA <$> a'
96+ requests <- switchHoldPromptOnly requests0 $ fmapCheap snd newA
97+ --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
98+ RequesterInternalT $ tellEvent requests
99+ pure (result0, fmapCheap fst newA)
100+ {-# INLINE traverseIntMapWithKeyWithAdjust #-}
101+ traverseIntMapWithKeyWithAdjust f a0 a' = PerformEventT $ RequesterT $ do
102+ env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
103+ let runA :: forall a. PerformEventT t m a -> HostFrame t (a, Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))
104+ runA (PerformEventT (RequesterT a)) = runEventWriterT $ runReaderT (unRequesterInternalT a) env
105+ children' <- requestingIdentity $ itraverse (\k -> runA . f k) <$> a'
106+ children0 <- lift $ itraverse (\k -> runA . f k) a0
107+ let results0 = fmap fst children0
108+ requests0 = fmap snd children0
109+ results' = fmap fst <$> children'
110+ requests' = fmap snd `fmapCheap` children'
111+ requests <- switchHoldPromptOnlyIncremental mergeIntIncremental coincidencePatchIntMap requests0 requests'
112+ --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
113+ RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatIntMapMaybe
114+ pure (results0, results')
115+ {-# INLINE traverseDMapWithKeyWithAdjust #-}
116+ traverseDMapWithKeyWithAdjust (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
117+ env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
118+ let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
119+ runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
120+ where (PerformEventT (RequesterT a)) = f k v
121+ children' <- requestingIdentity $ traversePatchDMapWithKey runA <$> a'
122+ children0 <- lift $ DMap.traverseWithKey runA a0
123+ let results0 = DMap.map (snd . getCompose) children0
124+ requests0 = weakenDMapWith (fst . getCompose) children0
125+ results' = mapPatchDMap (snd . getCompose) <$> children'
126+ requests' = weakenPatchDMapWith (fst . getCompose) `fmapCheap` children'
127+ requests <- switchHoldPromptOnlyIncremental mergeMapIncremental coincidencePatchMap requests0 requests'
128+ --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
129+ RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
130+ pure (results0, results')
131+ {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
132+ traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
133+ env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
134+ let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
135+ runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
136+ where (PerformEventT (RequesterT a)) = f k v
137+ children' <- requestingIdentity $ traversePatchDMapWithMoveWithKey runA <$> a'
138+ children0 <- lift $ DMap.traverseWithKey runA a0
139+ let results0 = DMap.map (snd . getCompose) children0
140+ requests0 = weakenDMapWith (fst . getCompose) children0
141+ results' = mapPatchDMapWithMove (snd . getCompose) <$> children'
142+ requests' = weakenPatchDMapWithMoveWith (fst . getCompose) `fmapCheap` children'
143+ requests <- switchHoldPromptOnlyIncremental mergeMapIncrementalWithMove coincidencePatchMapWithMove requests0 requests'
144+ --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
145+ RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
146+ pure (results0, results')
147+
148+concatIntMapMaybe :: Semigroup a => IntMap a -> Maybe a
149+concatIntMapMaybe m = case IntMap.elems m of
150+ [] -> Nothing
151+ h : t -> Just $ sconcat $ h :| t
152+
153+concatMapMaybe :: Semigroup a => Map k a -> Maybe a
154+concatMapMaybe m = case Map.elems m of
155+ [] -> Nothing
156+ h : t -> Just $ sconcat $ h :| t
110157
111158instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
112159 {-# INLINABLE newEventWithTrigger #-}
@@ -124,6 +171,7 @@ hostPerformEventT :: forall t m a.
124171 , MonadReflexHost t m
125172 , MonadRef m
126173 , Ref m ~ Ref IO
174+ , PrimMonad (HostFrame t)
127175 )
128176 => PerformEventT t m a
129177 -> m (a, FireCommand t m)
src/Reflex/Profiled.hs−1
@@ -47,7 +47,6 @@ import Reflex.Host.Class
4747import Reflex.PerformEvent.Class
4848
4949import System.IO.Unsafe
50−import Unsafe.Coerce
5150
5251data ProfiledTimeline t
5352
src/Reflex/Requester/Base.hs+13−527
@@ -1,537 +1,23 @@
11-- | This module provides 'RequesterT', the standard implementation of
22-- 'Requester'.
3−{-# LANGUAGE AllowAmbiguousTypes #-}
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE EmptyDataDecls #-}
6−{-# LANGUAGE ExistentialQuantification #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE GADTs #-}
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 #-}
20−#endif
213module Reflex.Requester.Base
224 ( RequesterT (..)
5+ , RequestData (..)
6+ , ResponseData (..)
7+ , RequestEnvelope (..)
238 , runRequesterT
24− , withRequesterT
25− , runWithReplaceRequesterTWith
26− , traverseIntMapWithKeyWithAdjustRequesterTWith
27− , traverseDMapWithKeyWithAdjustRequesterTWith
28− , RequesterData
29− , RequesterDataKey
9+-- , withRequesterT
10+-- , RequesterData
11+-- , RequesterDataKey
3012 , traverseRequesterData
3113 , forRequesterData
32− , requesterDataToList
33− , singletonRequesterData
14+ , requestEnvelopesToDSums
15+ , singletonRequestData
16+ , singletonResponseData
3417 , matchResponsesWithRequests
35− , multiEntry
36− , unMultiEntry
37− , requesting'
18+-- , multiEntry
19+-- , unMultiEntry
20+-- , requesting'
3821 ) where
3922
40−import Reflex.Class
41−import Reflex.Adjustable.Class
42−import Reflex.Dynamic
43−import Reflex.Host.Class
44−import Reflex.PerformEvent.Class
45−import Reflex.PostBuild.Class
46−import Reflex.Requester.Class
47−import Reflex.TriggerEvent.Class
48−
49−import Control.Applicative (liftA2)
50−import Control.Monad.Exception
51−import Control.Monad.Identity
52−import Control.Monad.Primitive
53−import Control.Monad.Reader
54−import Control.Monad.Ref
55−import Control.Monad.State.Strict
56−import Data.Bits
57−import Data.Coerce
58−import Data.Dependent.Map (DMap, DSum (..))
59−import qualified Data.Dependent.Map as DMap
60−import Data.Functor.Compose
61−import Data.Functor.Misc
62−import Data.IntMap.Strict (IntMap)
63−import qualified Data.IntMap.Strict as IntMap
64−import Data.Map (Map)
65−import qualified Data.Map as Map
66−import Data.Monoid ((<>))
67−import Data.Proxy
68−import qualified Data.Semigroup as S
69−import Data.Some (Some(Some))
70−import Data.Type.Equality
71−import Data.Unique.Tag
72−
73−import GHC.Exts (Any)
74−import Unsafe.Coerce
75−
76−--TODO: Make this module type-safe
77−
78−newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
79−
80−newtype RequesterData f = RequesterData (TagMap (Entry f))
81−
82−data RequesterDataKey a where
83− RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
84− 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
85− RequesterDataKey_Multi2 :: {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
86− RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
87−
88−singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
89−singletonRequesterData rdk v = case rdk of
90− RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
91− RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
92− RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
93− RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
94−
95−requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
96−requesterDataToList (RequesterData m) = do
97− k :=> Entry e <- tagMapToList m
98− case myKeyType k of
99− MyTagType_Single -> return $ RequesterDataKey_Single k :=> e
100− MyTagType_Multi -> do
101− (k', e') <- IntMap.toList e
102− k'' :=> e'' <- requesterDataToList e'
103− return $ RequesterDataKey_Multi k k' k'' :=> e''
104− MyTagType_Multi2 -> do
105− (k', e') <- Map.toList e
106− (k'', e'') <- IntMap.toList e'
107− k''' :=> e''' <- requesterDataToList e''
108− return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
109− MyTagType_Multi3 -> do
110− (k', e') <- IntMap.toList e
111− (k'', e'') <- IntMap.toList e'
112− k''' :=> e''' <- requesterDataToList e''
113− return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e'''
114−
115−singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f
116−singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v
117−
118−tagMapToList :: forall f. TagMap f -> [DSum MyTag f]
119−tagMapToList (TagMap m) = f <$> IntMap.toList m
120− where f :: (Int, Any) -> DSum MyTag f
121− f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v
122−
123−traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g)
124−traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m
125− where
126− g :: Int -> Any -> t Any
127− g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v)
128−
129−-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
130−traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response)
131−traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order
132− where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
133− go k (Entry request) = Entry <$> case myKeyType k of
134− MyTagType_Single -> f request
135− MyTagType_Multi -> traverse (traverseRequesterData f) request
136− MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
137− MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
138−
139−-- | 'traverseRequesterData' with its arguments flipped
140−forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response)
141−forRequesterData r f = traverseRequesterData f r
142−
143−data MyTagType :: * -> * where
144− MyTagType_Single :: MyTagType (Single a)
145− MyTagType_Multi :: MyTagType Multi
146− MyTagType_Multi2 :: MyTagType (Multi2 k)
147− MyTagType_Multi3 :: MyTagType Multi3
148−
149−myKeyType :: MyTag x -> MyTagType x
150−myKeyType (MyTag k) = case k .&. 0x3 of
151− 0x0 -> unsafeCoerce MyTagType_Single
152− 0x1 -> unsafeCoerce MyTagType_Multi
153− 0x2 -> unsafeCoerce MyTagType_Multi2
154− 0x3 -> unsafeCoerce MyTagType_Multi3
155− t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
156−
157−data Single a
158−data Multi
159−data Multi2 (k :: * -> *)
160−data Multi3
161−
162−class MyTagTypeOffset x where
163− myTagTypeOffset :: proxy x -> Int
164−
165−instance MyTagTypeOffset (Single a) where
166− myTagTypeOffset _ = 0x0
167−
168−instance MyTagTypeOffset Multi where
169− myTagTypeOffset _ = 0x1
170−
171−instance MyTagTypeOffset (Multi2 k) where
172− myTagTypeOffset _ = 0x2
173−
174−instance MyTagTypeOffset Multi3 where
175− myTagTypeOffset _ = 0x3
176−
177−type family EntryContents request a where
178− EntryContents request (Single a) = request a
179− EntryContents request Multi = IntMap (RequesterData request)
180− EntryContents request (Multi2 k) = Map (Some k) (IntMap (RequesterData request))
181− EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
182−
183−newtype Entry request x = Entry { unEntry :: EntryContents request x }
184−
185−{-# INLINE singleEntry #-}
186−singleEntry :: f a -> Entry f (Single a)
187−singleEntry = Entry
188−
189−{-# INLINE multiEntry #-}
190−multiEntry :: IntMap (RequesterData f) -> Entry f Multi
191−multiEntry = Entry
192−
193−{-# INLINE unMultiEntry #-}
194−unMultiEntry :: Entry f Multi -> IntMap (RequesterData f)
195−unMultiEntry = unEntry
196−
197−-- | 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
198−-- 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
199−newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum)
200−
201−newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum)
202−
203−{-# INLINE castMyTagWrap #-}
204−castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
205−castMyTagWrap = coerce
206−
207−instance GEq MyTag where
208− (MyTag a) `geq` (MyTag b) =
209− if a == b
210− then Just $ unsafeCoerce Refl
211− else Nothing
212−
213−instance GCompare MyTag where
214− (MyTag a) `gcompare` (MyTag b) =
215− case a `compare` b of
216− LT -> GLT
217− EQ -> unsafeCoerce GEQ
218− GT -> GGT
219−
220−instance GEq (MyTagWrap f) where
221− (MyTagWrap a) `geq` (MyTagWrap b) =
222− if a == b
223− then Just $ unsafeCoerce Refl
224− else Nothing
225−
226−instance GCompare (MyTagWrap f) where
227− (MyTagWrap a) `gcompare` (MyTagWrap b) =
228− case a `compare` b of
229− LT -> GLT
230− EQ -> unsafeCoerce GEQ
231− GT -> GGT
232−
233−data RequesterState t (request :: * -> *) = RequesterState
234− { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
235− , _requesterState_requests :: ![(Int, Event t Any)]
236− }
237−
238−-- | A basic implementation of 'Requester'.
239−newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a }
240− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
241−-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
242−#if MIN_VERSION_base(4,9,1)
243− , MonadAsyncException
244−#endif
245− )
246−
247−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
248−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
249−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
250−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
251−
252−instance PrimMonad m => PrimMonad (RequesterT t request response m) where
253− type PrimState (RequesterT t request response m) = PrimState m
254− primitive = lift . primitive
255−
256−-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
257−instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
258− mempty = pure mempty
259− mappend = liftA2 mappend
260−
261−instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
262− (<>) = liftA2 (S.<>)
263−
264−
265−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
266−-- requests are made, and responses should be provided in the input 'Event'.
267−-- The 'Tag' keys will be used to return the responses to the same place the
268−-- requests were issued.
269−runRequesterT :: (Reflex t, Monad m)
270− => RequesterT t request response m a
271− -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
272− -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
273−runRequesterT (RequesterT a) responses = do
274− (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $
275− coerceEvent responses
276− return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
277−
278−-- | Map a function over the request and response of a 'RequesterT'
279−withRequesterT
280− :: (Reflex t, MonadFix m)
281− => (forall x. req x -> req' x) -- ^ The function to map over the request
282− -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response
283− -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed
284− -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT'
285−withRequesterT freq frsp child = do
286− rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp'
287− (a, req) <- lift $ runRequesterT child rsp
288− rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $
289− fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req
290− return a
291−
292−instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
293− type Request (RequesterT t request response m) = request
294− type Response (RequesterT t request response m) = response
295− requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a)))
296− requesting_ = void . tagRequest . fmapCheap singleEntry
297−
298−{-# INLINE tagRequest #-}
299−tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
300−tagRequest req = do
301− old <- RequesterT get
302− let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x)
303− t = MyTagWrap n
304− RequesterT $ put $ RequesterState
305− { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4
306− , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old
307− }
308− return t
309−
310−{-# INLINE responseFromTag #-}
311−responseFromTag :: Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
312−responseFromTag (MyTagWrap t) = do
313− responses :: EventSelectorInt t Any <- RequesterT ask
314− return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
315−
316−instance MonadTrans (RequesterT t request response) where
317− lift = RequesterT . lift . lift
318−
319−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
320− type Performable (RequesterT t request response m) = Performable m
321− performEvent_ = lift . performEvent_
322− performEvent = lift . performEvent
323−
324−instance MonadRef m => MonadRef (RequesterT t request response m) where
325− type Ref (RequesterT t request response m) = Ref m
326− newRef = lift . newRef
327− readRef = lift . readRef
328− writeRef r = lift . writeRef r
329−
330−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
331− newEventWithTrigger = lift . newEventWithTrigger
332− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
333−
334−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
335− ask = lift ask
336− local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
337− reader = lift . reader
338−
339−instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where
340− runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
341− traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
342− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
343− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
344− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
345−
346−requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
347−requesting' = responseFromTag . castMyTagWrap <=< tagRequest
348−
349−{-# INLINABLE runWithReplaceRequesterTWith #-}
350−runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
351− , MonadFix m
352− )
353− => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
354− -> RequesterT t request response m a
355− -> Event t (RequesterT t request response m b)
356− -> RequesterT t request response m (a, Event t b)
357−runWithReplaceRequesterTWith f a0 a' = do
358− rec na' <- numberOccurrencesFrom 1 a'
359− responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here
360− let responses' = fanInt responses
361− ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na'
362− requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v'
363− return (result0, fmapCheap (fst . snd) v')
364−
365−{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-}
366−traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p.
367− ( Reflex t
368− , MonadHold t m
369− , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request)))
370− , Patch (p (Event t (IntMap (RequesterData request))))
371− , Functor p
372− , MonadFix m
373− )
374− => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v'))
375− -> IntMap (IntMap.Key, v)
376− -> Event t (p (IntMap.Key, v))
377− -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v')))
378− )
379− -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request))))
380− -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request))))
381− -> (IntMap.Key -> v -> RequesterT t request response m v')
382− -> IntMap v
383− -> Event t (p v)
384− -> RequesterT t request response m (IntMap v', Event t (p v'))
385−traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
386− rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
387− let responses :: EventSelectorInt t (IntMap (RequesterData response))
388− responses = fanInt $ fmapCheap unpack response
389− unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response))
390− unpack = unEntry
391− pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3
392− pack = Entry
393− f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
394− f' k (n, v) = do
395− (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
396− return (fmapCheap (IntMap.singleton n) myRequests, result)
397− ndm' <- numberOccurrencesFrom 1 dm'
398− (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
399− let result0 = fmap snd children0
400− result' = fforCheap children' $ fmap snd
401− requests0 :: IntMap (Event t (IntMap (RequesterData request)))
402− requests0 = fmap fst children0
403− requests' :: Event t (p (Event t (IntMap (RequesterData request))))
404− requests' = fforCheap children' $ fmap fst
405− promptRequests :: Event t (IntMap (IntMap (RequesterData request)))
406− promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
407− requests <- holdIncremental requests0 requests'
408− return (result0, result')
409−
410−{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-}
411−traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
412− ( GCompare k
413− , Reflex t
414− , MonadHold t m
415− , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request)))
416− , Patch (p' (Some k) (Event t (IntMap (RequesterData request))))
417− , MonadFix m
418− )
419− => (forall k' v1 v2. GCompare k'
420− => (forall a. k' a -> v1 a -> m (v2 a))
421− -> DMap k' v1
422− -> Event t (p k' v1)
423− -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
424− )
425− -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2)
426− -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
427− -> (forall v2. p' (Some k) v2 -> Map (Some k) v2)
428− -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
429− -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
430− -> DMap k v
431− -> Event t (p k v)
432− -> RequesterT t request response m (DMap k v', Event t (p k v'))
433−traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
434− rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
435− let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
436− responses = fanMap $ fmapCheap unpack response
437− unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
438− unpack = unEntry
439− pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
440− pack = Entry
441− f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
442− f' k (Compose (n, v)) = do
443− (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
444− return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
445− ndm' <- numberOccurrencesFrom 1 dm'
446− (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
447− let result0 = DMap.map (snd . getCompose) children0
448− result' = fforCheap children' $ mapPatch $ snd . getCompose
449− requests0 :: Map (Some k) (Event t (IntMap (RequesterData request)))
450− requests0 = weakenDMapWith (fst . getCompose) children0
451− requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request))))
452− requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
453− promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request)))
454− promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
455− requests <- holdIncremental requests0 requests'
456− return (result0, result')
457−
458−data Decoder rawResponse response =
459− forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
460−
461−-- | Matches incoming responses with previously-sent requests
462−-- and uses the provided request "decoder" function to process
463−-- incoming responses.
464−matchResponsesWithRequests
465− :: forall t rawRequest rawResponse request response m.
466− ( MonadFix m
467− , MonadHold t m
468− , Reflex t
469− )
470− => (forall a. request a -> (rawRequest, rawResponse -> response a))
471− -- ^ Given a request (from 'Requester'), produces the wire format of the
472− -- request and a function used to process the associated response
473− -> Event t (RequesterData request)
474− -- ^ The outgoing requests
475− -> Event t (Int, rawResponse)
476− -- ^ The incoming responses, tagged by an identifying key
477− -> m ( Event t (Map Int rawRequest)
478− , Event t (RequesterData response)
479− )
480− -- ^ A map of outgoing wire-format requests and an event of responses keyed
481− -- by the 'RequesterData' key of the associated outgoing request
482−matchResponsesWithRequests f send recv = do
483− rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
484− waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
485− holdIncremental mempty $ leftmost
486− [ fmap (\(_, outstanding, _) -> outstanding) outgoing
487− , snd <$> incoming
488− ]
489− let outgoing = processOutgoing nextId send
490− incoming = processIncoming waitingFor recv
491− return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming)
492− where
493− -- Tags each outgoing request with an identifying integer key
494− -- and returns the next available key, a map of response decoders
495− -- for requests for which there are outstanding responses, and the
496− -- raw requests to be sent out.
497− processOutgoing
498− :: Behavior t Int
499− -- The next available key
500− -> Event t (RequesterData request)
501− -- The outgoing request
502− -> Event t ( Int
503− , PatchMap Int (Decoder rawResponse response)
504− , Map Int rawRequest )
505− -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests
506− processOutgoing nextId out = flip pushAlways out $ \dm -> do
507− oldNextId <- sample nextId
508− let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do
509− n <- get
510− put $ succ n
511− let (rawReq, rspF) = f v
512− return (n, rawReq, Decoder k rspF)
513− patchWaitingFor = PatchMap $ Map.fromList $
514− (\(n, _, dec) -> (n, Just dec)) <$> result
515− toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result
516− return (newNextId, patchWaitingFor, toSend)
517− -- Looks up the each incoming raw response in a map of response
518− -- decoders and returns the decoded response and a patch that can
519− -- be used to clear the ID of the consumed response out of the queue
520− -- of expected responses.
521− processIncoming
522− :: Incremental t (PatchMap Int (Decoder rawResponse response))
523− -- A map of outstanding expected responses
524− -> Event t (Int, rawResponse)
525− -- A incoming response paired with its identifying key
526− -> Event t (RequesterData response, PatchMap Int v)
527− -- The decoded response and a patch that clears the outstanding responses queue
528− processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do
529− wf <- sample $ currentIncremental waitingFor
530− case Map.lookup n wf of
531− Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
532− Just (Decoder k rspF) -> do
533− let rsp = rspF rawRsp
534− return $ Just
535− ( singletonRequesterData k rsp
536− , PatchMap $ Map.singleton n Nothing
537− )
23+import Reflex.Requester.Base.Internal
src/Reflex/Requester/Base/Internal.hsnew file+310
@@ -0,0 +1,310 @@
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE CPP #-}
3+{-# LANGUAGE EmptyDataDecls #-}
4+{-# LANGUAGE ExistentialQuantification #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE RecursiveDo #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE StandaloneDeriving #-}
13+{-# LANGUAGE TypeApplications #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE TypeOperators #-}
16+{-# LANGUAGE UndecidableInstances #-}
17+{-# LANGUAGE InstanceSigs #-}
18+#ifdef USE_REFLEX_OPTIMIZER
19+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20+#endif
21+module Reflex.Requester.Base.Internal where
22+
23+import Reflex.Class
24+import Reflex.Adjustable.Class
25+import Reflex.Host.Class
26+import Reflex.PerformEvent.Class
27+import Reflex.PostBuild.Class
28+import Reflex.Requester.Class
29+import Reflex.TriggerEvent.Class
30+import Reflex.EventWriter.Base
31+import Reflex.EventWriter.Class
32+
33+import Control.Applicative (liftA2)
34+import Control.Monad.Exception
35+import Control.Monad.Fail
36+import Control.Monad.Identity
37+import Control.Monad.Primitive
38+import Control.Monad.Reader
39+import Control.Monad.Ref
40+import Control.Monad.State
41+import Data.Coerce
42+import Data.Dependent.Map (DSum (..))
43+import qualified Data.List.NonEmpty as NonEmpty
44+import Data.List.NonEmpty.Deferred (NonEmptyDeferred)
45+import qualified Data.List.NonEmpty.Deferred as NonEmptyDeferred
46+import Data.Monoid ((<>))
47+import qualified Data.Semigroup as S
48+import Data.Some (Some(Some))
49+import Data.Type.Equality
50+import Data.TagMap (TagMap)
51+import qualified Data.TagMap as TagMap
52+import Reflex.FanTag
53+import Data.Unique.Tag.Local
54+import Data.GADT.Compare
55+import Data.Witherable
56+
57+import Unsafe.Coerce
58+
59+import Debug.Trace
60+
61+import Data.Map (Map)
62+import qualified Data.Map as Map
63+import qualified Data.IntMap as IntMap
64+import GHC.Exts (Any)
65+
66+data RequestData ps request = forall s. RequestData !(TagGen ps s) !(NonEmptyDeferred (RequestEnvelope s request))
67+
68+data RequestEnvelope s request = forall a. RequestEnvelope {-# UNPACK #-} !(Maybe (Tag s a)) !(request a)
69+
70+data ResponseData ps response = forall s. ResponseData !(TagGen ps s) !(TagMap s response)
71+
72+traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequestData (PrimState m) request -> m (ResponseData (PrimState m) response)
73+traverseRequesterData f (RequestData tg es) = ResponseData tg . TagMap.fromList <$> wither g (NonEmpty.toList $ NonEmptyDeferred.toNonEmpty es)
74+ where g (RequestEnvelope mt req) = case mt of
75+ Just t -> Just . (t :=>) <$> f req
76+ Nothing -> Nothing <$ f req
77+
78+-- | 'traverseRequesterData' with its arguments flipped
79+forRequesterData :: forall request response m. Applicative m => RequestData (PrimState m) request -> (forall a. request a -> m (response a)) -> m (ResponseData (PrimState m) response)
80+forRequesterData r f = traverseRequesterData f r
81+
82+runRequesterT :: forall t request response m a
83+ . ( Reflex t
84+ , PrimMonad m
85+ , MonadFix m
86+ )
87+ => RequesterT t request response m a
88+ -> Event t (ResponseData (PrimState m) response)
89+ -> m (a, Event t (RequestData (PrimState m) request))
90+runRequesterT (RequesterT a) wrappedResponses = withRequesterInternalT $ \requests -> do
91+ (_, tg) <- RequesterInternalT ask
92+ result <- a
93+ let responses = fforMaybe wrappedResponses $ \(ResponseData tg' m) -> case tg `geq` tg' of
94+ Nothing -> trace ("runRequesterT: bad TagGen: expected " <> show tg <> " but got " <> show tg') Nothing --TODO: Warn somehow
95+ Just Refl -> Just m
96+ pure (responses, (result, fmapCheap (RequestData tg) requests))
97+
98+instance MonadTrans (RequesterInternalT s t request response) where
99+ lift = RequesterInternalT . lift . lift
100+
101+instance MonadTrans (RequesterT t request response) where
102+ lift a = RequesterT $ RequesterInternalT $ lift $ lift a
103+
104+-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
105+-- requests are made, and responses should be provided in the input 'Event'.
106+-- The 'Tag' keys will be used to return the responses to the same place the
107+-- requests were issued.
108+withRequesterInternalT
109+ :: forall t m request response a.
110+ ( Reflex t
111+ , PrimMonad m
112+ , MonadFix m
113+ )
114+ => (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom request)) -> RequesterInternalT FakeRequesterStatePhantom t request response m (Event t (TagMap FakeRequesterStatePhantom response), a))
115+ -> m a
116+withRequesterInternalT f = do
117+ stg <- newTagGen
118+ let tg = case stg of
119+ Some tg' -> (unsafeCoerce :: TagGen (PrimState m) s -> TagGen (PrimState m) FakeRequesterStatePhantom) tg'
120+ rec let RequesterInternalT a = (unsafeCoerce :: RequesterInternalT s t request response m (Event t (TagMap FakeRequesterStatePhantom response), a) -> RequesterInternalT FakeRequesterStatePhantom t request response m (Event t (TagMap FakeRequesterStatePhantom response), a)) $ f requests
121+ ((responses, result), requests) <- runEventWriterT $ runReaderT a (fanTag responses, tg)
122+ pure result
123+
124+-- This is because using forall ruins inlining
125+data FakeRequesterStatePhantom
126+
127+newtype RequesterT t (request :: * -> *) (response :: * -> *) m a = RequesterT { unRequesterT :: RequesterInternalT FakeRequesterStatePhantom t request response m a }
128+ deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
129+
130+newtype RequesterInternalT s t request response m a = RequesterInternalT { unRequesterInternalT :: ReaderT (EventSelectorTag t s response, TagGen (PrimState m) s) (EventWriterT t (NonEmptyDeferred (RequestEnvelope s request)) m) a }
131+ deriving
132+ ( Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
133+#if MIN_VERSION_base(4,9,1)
134+ , MonadAsyncException
135+#endif
136+ )
137+
138+-- I don't think this can actually be supported without unsafeCoercing around the fact that the phantoms don't match up. In fact, implementations could probably supply `unmask` functions that would actually do the wrong thing.
139+-- #if MIN_VERSION_base(4,9,1)
140+-- instance MonadAsyncException m => MonadAsyncException (RequesterT t request response m) where
141+-- mask f = RequesterT $ mask $ \unmask -> unRequesterT $ f $ \x -> RequesterT $ unmask $ unRequesterT x
142+-- #endif
143+
144+deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
145+deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
146+deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
147+deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
148+
149+deriving instance MonadSample t m => MonadSample t (RequesterInternalT s t request response m)
150+deriving instance MonadHold t m => MonadHold t (RequesterInternalT s t request response m)
151+deriving instance PostBuild t m => PostBuild t (RequesterInternalT s t request response m)
152+deriving instance TriggerEvent t m => TriggerEvent t (RequesterInternalT s t request response m)
153+
154+instance PrimMonad m => PrimMonad (RequesterT t request response m) where
155+ type PrimState (RequesterT t request response m) = PrimState m
156+ primitive = lift . primitive
157+
158+-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
159+instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
160+ mempty = pure mempty
161+ mappend = liftA2 mappend
162+
163+instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
164+ (<>) = liftA2 (S.<>)
165+
166+instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
167+ type Performable (RequesterT t request response m) = Performable m
168+ performEvent_ = lift . performEvent_
169+ performEvent = lift . performEvent
170+
171+instance MonadRef m => MonadRef (RequesterT t request response m) where
172+ type Ref (RequesterT t request response m) = Ref m
173+ newRef = lift . newRef
174+ readRef = lift . readRef
175+ writeRef r = lift . writeRef r
176+
177+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
178+ newEventWithTrigger = lift . newEventWithTrigger
179+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
180+
181+instance MonadReader r m => MonadReader r (RequesterT t request response m) where
182+ ask = lift ask
183+ local f (RequesterT a) = RequesterT $ RequesterInternalT $ mapReaderT (mapEventWriterT $ local f) $ unRequesterInternalT a
184+ reader = lift . reader
185+
186+instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
187+ type Request (RequesterT t request response m) = request
188+ type Response (RequesterT t request response m) = response
189+ requesting e = RequesterT $ requesting e
190+ requesting_ e = RequesterT $ requesting_ e
191+
192+instance (Reflex t, PrimMonad m) => Requester t (RequesterInternalT s t request response m) where
193+ type Request (RequesterInternalT s t request response m) = request
194+ type Response (RequesterInternalT s t request response m) = response
195+ requesting e = RequesterInternalT $ do
196+ (s, tg) <- ask
197+ t <- lift $ lift $ newTag tg
198+ tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope (Just t)) e
199+ pure $ selectTag s t
200+ requesting_ e = RequesterInternalT $ do
201+ tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope Nothing) e
202+
203+instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterInternalT s t request response m) where
204+ {-# INLINE runWithReplace #-}
205+ runWithReplace (RequesterInternalT a0) a' = RequesterInternalT $ runWithReplace a0 (coerceEvent a')
206+ {-# INLINE traverseIntMapWithKeyWithAdjust #-}
207+ traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseIntMapWithKeyWithAdjust (coerce . f) dm0 dm'
208+ {-# INLINE traverseDMapWithKeyWithAdjust #-}
209+ traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
210+ {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
211+ traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjustWithMove (coerce . f) dm0 dm'
212+
213+instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterT t request response m) where
214+ {-# INLINE runWithReplace #-}
215+ runWithReplace (RequesterT a0) a' = RequesterT $ runWithReplace a0 (fmapCheap unRequesterT a')
216+ {-# INLINE traverseIntMapWithKeyWithAdjust #-}
217+ traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseIntMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
218+ {-# INLINE traverseDMapWithKeyWithAdjust #-}
219+ traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
220+ {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
221+ traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) dm0 dm'
222+
223+
224+data Decoder rawResponse response =
225+ forall s a. Decoder (Tag s a) (rawResponse -> response a)
226+
227+-- | Matches incoming responses with previously-sent requests
228+-- and uses the provided request "decoder" function to process
229+-- incoming responses.
230+matchResponsesWithRequests
231+ :: forall t rawRequest rawResponse request response m.
232+ ( MonadFix m
233+ , MonadHold t m
234+ , PrimMonad m
235+ , Reflex t
236+ )
237+ => (forall a. request a -> (rawRequest, rawResponse -> response a))
238+ -- ^ Given a request (from 'Requester'), produces the wire format of the
239+ -- request and a function used to process the associated response
240+ -> Event t (RequestData (PrimState m) request)
241+ -- ^ The outgoing requests
242+ -> Event t (Int, rawResponse)
243+ -- ^ The incoming responses, tagged by an identifying key
244+ -> m ( Event t (Map Int rawRequest)
245+ , Event t (ResponseData (PrimState m) response)
246+ )
247+ -- ^ A map of outgoing wire-format requests and an event of responses keyed
248+ -- by the 'RequesterData' key of the associated outgoing request
249+matchResponsesWithRequests f send recv = withTagGen $ \tagGen -> mdo
250+ waitingFor <- holdIncremental mempty $
251+ leftmost [ fst <$> outgoing, snd <$> incoming ]
252+ let outgoing = processOutgoing send
253+ incoming = processIncoming waitingFor tagGen recv
254+ return (snd <$> outgoing, fst <$> incoming)
255+ where
256+
257+ -- Tags each outgoing request with an identifying integer key
258+ -- and returns the next available key, a map of response decoders
259+ -- for requests for which there are outstanding responses, and the
260+ -- raw requests to be sent out.
261+ processOutgoing
262+ :: Event t (RequestData (PrimState m) request)
263+ -- The outgoing request
264+ -> Event t ( PatchMap Int (Decoder rawResponse response)
265+ , Map Int rawRequest )
266+ -- A map of requests expecting responses, and the tagged raw requests
267+ processOutgoing eventOutgoingRequest = flip pushAlways eventOutgoingRequest $ \(RequestData _ requestEnvelopes) -> do
268+ let results = ffor (requestEnvelopesToDSums requestEnvelopes) $ \(k :=> v) ->
269+ let (rawRequest, responseDecoder) = f v
270+ in (tagId k, rawRequest, Decoder k responseDecoder)
271+ let patchWaitingFor = PatchMap $ Map.fromList $ (\(n, _, dec) -> (n, Just dec)) <$> results
272+ let toSend = Map.fromList $ (\(n, rawRequest, _) -> (n, rawRequest)) <$> results
273+ return (patchWaitingFor, toSend)
274+
275+ -- Looks up the each incoming raw response in a map of response
276+ -- decoders and returns the decoded response and a patch that can
277+ -- be used to clear the ID of the consumed response out of the queue
278+ -- of expected responses.
279+ processIncoming
280+ :: Incremental t (PatchMap Int (Decoder rawResponse response))
281+ -- A map of outstanding expected responses
282+ -> TagGen (PrimState m) s
283+ -- A tag generator
284+ -> Event t (Int, rawResponse)
285+ -- A incoming response paired with its identifying key
286+ -> Event t (ResponseData (PrimState m) response, PatchMap Int v)
287+ -- The decoded response and a patch that clears the outstanding responses queue
288+ processIncoming incWaitingFor tagGen inc = flip push inc $ \(n, rawRsp) -> do
289+ waitingFor <- sample $ currentIncremental incWaitingFor
290+ case Map.lookup n waitingFor of
291+ Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
292+ Just (Decoder tag responseDecoder) ->
293+ return $ Just
294+ ( singletonResponseData tagGen (unsafeTagFromId n) (responseDecoder rawRsp)
295+ , PatchMap $ Map.singleton n Nothing
296+ )
297+
298+requestEnvelopesToDSums :: forall s request. NonEmptyDeferred (RequestEnvelope s request) -> [DSum (Tag s) request]
299+requestEnvelopesToDSums requestEnvelopes = catMaybes $ f <$> NonEmptyDeferred.toList requestEnvelopes
300+ where f :: (RequestEnvelope s request) -> Maybe (DSum (Tag s) request)
301+ f (RequestEnvelope (Just tag) v) = Just (tag :=> v)
302+ f (RequestEnvelope Nothing _) = Nothing
303+
304+singletonRequestData :: TagGen ps s -> Maybe (Tag s a) -> request a -> RequestData ps request
305+singletonRequestData tagGen maybeTag request =
306+ RequestData tagGen $ NonEmptyDeferred.singleton $ RequestEnvelope maybeTag request
307+
308+singletonResponseData :: TagGen ps s -> Tag ps s -> response a -> ResponseData ps response
309+singletonResponseData tagGen tag response =
310+ ResponseData tagGen $ TagMap.singletonTagMap tag response
src/Reflex/Requester/Class.hs+1−1
@@ -40,7 +40,7 @@ class (Reflex t, Monad m) => Requester t m | m -> t where
4040 requesting :: Event t (Request m a) -> m (Event t (Response m a))
4141 -- | Emit a request whenever the given 'Event' fires, and ignore all responses.
4242 requesting_ :: Event t (Request m a) -> m ()
43−
43+--TODO: Make Requester polykinded, where Request m and Response m both take an argument of the same kind
4444
4545instance Requester t m => Requester t (ReaderT r m) where
4646 type Request (ReaderT r m) = Request m
test/Adjustable.hs+3−2
@@ -22,7 +22,7 @@ main = do
2222 -- If the final counter value in the adjusted widgets corresponds to the number of times it has
2323 -- been incremented, we know that the networks haven't broken.
2424 let expectedCount = length $ ffilter (== Increment) actions
25− -- let !True = last (last os) == [expectedCount,expectedCount] -- TODO re-enable this test after issue #369 has been resolved
25+ let !True = last (last os) == [expectedCount,expectedCount]
2626 return ()
2727
2828data PatchMapTestAction
@@ -59,4 +59,5 @@ testPatchMapWithMove pulse = do
5959 (Map.fromList $ zip [0..] "ab")
6060 (fmapMaybe id pulseAction)
6161 return ()
62− return result
62+ return result
63+
test/EventWriterT.hs+15−11
@@ -24,26 +24,30 @@ import Test.Run
2424
2525main :: IO ()
2626main = do
27− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
27+ os1 <- runApp' (unwrapApp testOrdering) $
2828 [ Just ()
2929 ]
3030 print os1
31− os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
32− <- runApp' (unwrapApp testSimultaneous) $ map Just $
33− [ This ()
34− , That ()
35− , This ()
36− , These () ()
37− ]
31+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
32+ [ This ()
33+ , That ()
34+ , This ()
35+ , These () ()
36+ ]
3837 print os2
39− os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
38+ os3 <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
4039 print os3
41− os4@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
40+ os4 <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
4241 print os4
43− os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
42+ os5 <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
4443 print os5
4544 os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
4645 print os6
46+ let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
47+ let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2
48+ let ![[Nothing, Just [2]]] = os3
49+ let ![[Nothing, Just [2]]] = os4
50+ let ![[Nothing, Just [1, 2]]] = os5
4751 let ![[Nothing, Nothing]] = os6
4852 return ()
4953
test/RequesterT.hs+100−10
@@ -1,20 +1,35 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE BangPatterns #-}
33{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
58{-# LANGUAGE RankNTypes #-}
69{-# LANGUAGE RecursiveDo #-}
710{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TemplateHaskell #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE TypeFamilies #-}
814module Main where
915
10−import Control.Lens
16+import Control.Lens hiding (has)
1117import Control.Monad
18+import Control.Monad.Fail (MonadFail)
1219import Control.Monad.Fix
20+import Control.Monad.IO.Class (MonadIO)
21+import Control.Monad.Primitive
22+import Data.Constraint.Extras
23+import Data.Constraint.Extras.TH
24+import Data.Constraint.Forall
1325import qualified Data.Dependent.Map as DMap
1426import Data.Dependent.Sum
1527import Data.Functor.Misc
28+import Data.Map (Map)
1629import qualified Data.Map as M
1730import Data.These
31+import Data.List.NonEmpty.Deferred
32+import Text.Read (readMaybe)
1833
1934#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
2035import Data.These.Lens
@@ -25,6 +40,8 @@ import Reflex.Requester.Base
2540import Reflex.Requester.Class
2641import Test.Run
2742
43+import Debug.Trace hiding (traceEvent)
44+
2845data RequestInt a where
2946 RequestInt :: Int -> RequestInt Int
3047
@@ -33,7 +50,7 @@ main = do
3350 os1 <- runApp' (unwrapApp testOrdering) $
3451 [ Just ()
3552 ]
36− print os1
53+ --print os1
3754 os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
3855 [ This ()
3956 , That ()
@@ -49,24 +66,36 @@ main = do
4966 print os5
5067 os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
5168 print os6
52− let ![[Just [1,2,3,4,5,6,7,8,9,10]]] = os1 -- The order is reversed here: see the documentation for 'runRequesterT'
53− let ![[Just [9,7,5,3,1]],[Nothing,Nothing],[Just [10,8,6,4,2]],[Just [10,8,6,4,2],Nothing]] = os2
69+ os7 <- runApp' testMatchRequestsWithResponses $ map Just [ TestRequest_Increment 1, TestRequest_Increment 2 ]
70+ print os7
71+ os8 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "abcd" ]
72+ print os8
73+ os9 <- runApp' testMoribundPerformEvent $ map Just [ 1 .. 3 ]
74+ print os9
75+ let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
76+ let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2
5477 let ![[Nothing, Just [2]]] = os3
5578 let ![[Nothing, Just [2]]] = os4
5679 let ![[Nothing, Just [1, 2]]] = os5
57− -- let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
80+ let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
81+ let !(Just [(-9223372036854775808,"2")]) = M.toList <$> head (head os7)
82+ let !(Just [(-9223372036854775808,"dcba")]) = M.toList <$> head (head os8)
83+ let ![[Nothing,Just "0:1"],[Nothing,Just "1:2"],[Nothing,Just "2:3"]] = os9
5884 return ()
5985
60−unwrapRequest :: DSum tag RequestInt -> Int
61−unwrapRequest (_ :=> RequestInt i) = i
62−
63−unwrapApp :: ( Reflex t, Monad m )
86+unwrapApp :: forall t m a.
87+ ( Reflex t
88+ , MonadFix m
89+ , PrimMonad m
90+ )
6491 => (a -> RequesterT t RequestInt Identity m ())
6592 -> a
6693 -> m (Event t [Int])
6794unwrapApp x appIn = do
6895 ((), e) <- runRequesterT (x appIn) never
69− return $ fmap (map unwrapRequest . requesterDataToList) e
96+ let unwrapRequests :: forall x. RequestData (PrimState m) RequestInt -> [Int]
97+ unwrapRequests (RequestData _ es) = fmap (\(RequestEnvelope _ (RequestInt i)) -> i) $ toList es
98+ return $ fmap unwrapRequests e
7099
71100testOrdering :: ( Response m ~ Identity
72101 , Request m ~ RequestInt
@@ -172,3 +201,64 @@ delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do
172201 -- This has the effect of delaying pulse' from pulse
173202 (_, pulse') <- runWithReplace (pure ()) $ pure (RequestInt 1) <$ pulse
174203 requestingIdentity pulse'
204+
205+data TestRequest a where
206+ TestRequest_Reverse :: String -> TestRequest String
207+ TestRequest_Increment :: Int -> TestRequest Int
208+
209+instance Show (TestRequest a) where
210+ show = \case
211+ TestRequest_Reverse str -> "reverse " <> str
212+ TestRequest_Increment i -> "increment " <> show i
213+
214+testMatchRequestsWithResponses
215+ :: forall m t req a
216+ . ( MonadFix m
217+ , MonadHold t m
218+ , Reflex t
219+ , PerformEvent t m
220+ , MonadIO (Performable m)
221+ , ForallF Show req
222+ , Has Read req
223+ , PrimMonad m
224+ , Show (req a)
225+ , Show a
226+ , MonadIO m
227+ )
228+ => Event t (req a) -> m (Event t (Map Int String))
229+testMatchRequestsWithResponses pulse = mdo
230+ (_, requests) <- runRequesterT (requesting pulse) responses
231+ let rawResponseMap = M.map (\v ->
232+ case words v of
233+ ["reverse", str] -> reverse str
234+ ["increment", i] -> show $ succ $ (read i :: Int)
235+ ) <$> rawRequestMap
236+ (rawRequestMap, responses) <- matchResponsesWithRequests reqEncoder requests (head . M.toList <$> rawResponseMap)
237+ pure rawResponseMap
238+ where
239+ reqEncoder :: forall a. req a -> (String, String -> Maybe a)
240+ reqEncoder r =
241+ ( whichever @Show @req @a $ show r
242+ , \x -> has @Read r $ readMaybe x
243+ )
244+
245+-- If a widget is destroyed, and simultaneously it tries to use performEvent, the event does not get performed.
246+-- TODO Determine whether this is actually the behavior we want.
247+testMoribundPerformEvent
248+ :: forall t m
249+ . ( Adjustable t m
250+ , PerformEvent t m
251+ , MonadHold t m
252+ , Reflex t
253+ )
254+ => Event t Int -> m (Event t String)
255+testMoribundPerformEvent pulse = do
256+ (outputInitial, outputReplaced) <- runWithReplace (performPrint 0 pulse) $ ffor pulse $ \i -> performPrint i pulse
257+ switchHold outputInitial outputReplaced
258+ where
259+ performPrint i evt =
260+ performEvent $ ffor evt $ \output ->
261+ return $ show i <> ":" <> show output
262+
263+
264+deriveArgDict ''TestRequest