on develop help sign in
Steer people away from `switchPromptly*`
c2b8ccdJohn Ericson
Quickref.md+2−2
@@ -159,7 +159,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
159[ ] switch :: Behavior (Event a) -> Event a
159[ ] switch :: Behavior (Event a) -> Event a
160
160
161-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
161-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
162−[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
162+[ ] switchDyn :: Dynamic (Event a) -> Event a
163
163
164-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
164-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
165[ ] coincidence :: Event (Event a) -> Event a
165[ ] coincidence :: Event (Event a) -> Event a
@@ -182,7 +182,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
182
182
183-- Similar to above, for Events. Created Event initially tracks the first argument.
183-- Similar to above, for Events. Created Event initially tracks the first argument.
184-- At switchover, the output Event immediately tracks the new Event.
184-- At switchover, the output Event immediately tracks the new Event.
185−[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
185+[H] switchHold :: Event a -> Event (Event a) -> m (Event a)
186```
186```
187
187
188## Typeclasses to introspect and modify an FRP network.
188## Typeclasses to introspect and modify an FRP network.
src/Reflex/Class.hs+44−11
@@ -62,9 +62,10 @@ module Reflex.Class
62 , EitherTag (..)
62 , EitherTag (..)
63 , eitherToDSum
63 , eitherToDSum
64 , dsumToEither
64 , dsumToEither
65− -- ** Switching between 'Event's
65+ -- ** Collapsing 'Event . Event'
66− , switchPromptly
66+ , switchHold
67− , switchPromptOnly
67+ , switchHoldPromptly
68+ , switchHoldPromptOnly
68 -- ** Using 'Event's to sample 'Behavior's
69 -- ** Using 'Event's to sample 'Behavior's
69 , tag
70 , tag
70 , attach
71 , attach
@@ -142,6 +143,8 @@ module Reflex.Class
142 , tagCheap
143 , tagCheap
143 , mergeWithCheap
144 , mergeWithCheap
144 , mergeWithCheap'
145 , mergeWithCheap'
146+ , switchPromptly
147+ , switchPromptOnly
145 -- * Slow, but general, implementations
148 -- * Slow, but general, implementations
146 , slowHeadE
149 , slowHeadE
147#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
150#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
@@ -825,22 +828,45 @@ fanThese e =
825fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
828fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
826fanMap = fan . fmap mapToDMap
829fanMap = fan . fmap mapToDMap
827
830
828−-- | Switches to the new event whenever it receives one. Whenever a new event
831+-- | Switches to the new event whenever it receives one. Only the old event is
832+-- considered the moment a new one is switched in; the output event will fire at
833+-- that moment if only if the old event does.
834+--
835+-- Because the simultaneous firing case is irrelevant, this function imposes
836+-- laxer "timing requirements" on the overall circuit, avoiding many potential
837+-- cyclic dependency / metastability failures. It's also more performant. Use
838+-- this rather than 'switchHoldPromptly' and 'switchHoldPromptOnly' unless you
839+-- are absolutely sure you need to act on the new event in the coincidental
840+-- case.
841+switchHold :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
842+switchHold ea0 eea = switch <$> hold ea0 eea
843+
844+-- | Switches to the new event whenever it receives one. Whenever a new event is
829−-- is provided, if it is firing, its value will be the resulting event's value;
845+-- provided, if it is firing, its value will be the resulting event's value; if
830−-- if it is not firing, but the old one is, the old one's value will be used.
846+-- it is not firing, but the old one is, the old one's value will be used.
847+--
848+-- 'switchHold', by always forwarding the old event the moment it is switched
849+-- out, avoids many potential cyclic dependency problems / metastability
850+-- problems. It's also more performant. Use it instead unless you are sure you
851+-- cannot.
831−switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
852+switchHoldPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
832−switchPromptly ea0 eea = do
853+switchHoldPromptly ea0 eea = do
833 bea <- hold ea0 eea
854 bea <- hold ea0 eea
834 let eLag = switch bea
855 let eLag = switch bea
835 eCoincidences = coincidence eea
856 eCoincidences = coincidence eea
836 return $ leftmost [eCoincidences, eLag]
857 return $ leftmost [eCoincidences, eLag]
837
858
838−-- | 'switch'es to a new event whenever it receives one. At the moment of
859+-- | switches to a new event whenever it receives one. At the moment of
839-- switching, the old event will be ignored if it fires, and the new one will be
860-- switching, the old event will be ignored if it fires, and the new one will be
840-- used if it fires; this is the opposite of 'switch', which will use only the
861-- used if it fires; this is the opposite of 'switch', which will use only the
841-- old value.
862-- old value.
863+--
864+-- 'switchHold', by always forwarding the old event the moment it is switched
865+-- out, avoids many potential cyclic dependency problems / metastability
866+-- problems. It's also more performant. Use it instead unless you are sure you
867+-- cannot.
842−switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
868+switchHoldPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
843−switchPromptOnly e0 e' = do
869+switchHoldPromptOnly e0 e' = do
844 eLag <- switch <$> hold e0 e'
870 eLag <- switch <$> hold e0 e'
845 return $ coincidence $ leftmost [e', eLag <$ eLag]
871 return $ coincidence $ leftmost [e', eLag <$ eLag]
846
872
@@ -1275,3 +1301,10 @@ sequenceThese t = case t of
1275 This ma -> fmap This ma
1301 This ma -> fmap This ma
1276 These ma mb -> liftM2 These ma mb
1302 These ma mb -> liftM2 These ma mb
1277 That mb -> fmap That mb
1303 That mb -> fmap That mb
1304+
1305+{-# DEPRECATED switchPromptly "Use 'switchHoldPromptly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1306+switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1307+switchPromptly = switchHoldPromptly
1308+{-# 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'. " #-}
1309+switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1310+switchPromptOnly = switchHoldPromptOnly
src/Reflex/Dynamic.hs+16
@@ -30,6 +30,7 @@ module Reflex.Dynamic
30 , constDyn
30 , constDyn
31 , count
31 , count
32 , toggle
32 , toggle
33+ , switchDyn
33 , switchPromptlyDyn
34 , switchPromptlyDyn
34 , tagPromptlyDyn
35 , tagPromptlyDyn
35 , attachPromptlyDyn
36 , attachPromptlyDyn
@@ -181,10 +182,25 @@ count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
181toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
182toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
182toggle = foldDyn (const not)
183toggle = foldDyn (const not)
183
184
185+-- | Switches to the new 'Event' whenever it receives one. Only the old event is
186+-- considered the moment a new one is switched in; the output event will fire at
187+-- that moment if only if the old event does.
188+--
189+-- Prefer this to 'switchPromptlyDyn' where possible. The lack of doing double
190+-- work when the outer and (new) inner fires means this imposes fewer "timing
191+-- requirements" and thus is far more easy to use without introducing fresh
192+-- failure cases. 'switchDyn' is also more performant.
193+switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
194+switchDyn d = switch (current d)
195+
184-- | Switches to the new 'Event' whenever it receives one. Switching occurs
196-- | Switches to the new 'Event' whenever it receives one. Switching occurs
185-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
197-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
186-- old and new inner Events fire simultaneously, the output will fire with the
198-- old and new inner Events fire simultaneously, the output will fire with the
187-- value of the *new* 'Event'.
199-- value of the *new* 'Event'.
200+--
201+-- Prefer 'switchDyn' to this where possible. The timing requirements that
202+-- switching before imposes are likely to bring down your app unless you are
203+-- very careful. 'switchDyn' is also more performant.
188switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
204switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
189switchPromptlyDyn de =
205switchPromptlyDyn de =
190 let eLag = switch $ current de
206 let eLag = switch $ current de
test/Reflex/Test/CrossImpl.hs+13−13
@@ -155,35 +155,35 @@ testCases =
155 let e' = leftmost [e, e]
155 let e' = leftmost [e, e]
156 e'' <- switch <$> hold e' (fmap (const e) e)
156 e'' <- switch <$> hold e' (fmap (const e) e)
157 return (b, e'')
157 return (b, e'')
158− , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
158+ , (,) "switchHoldPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
159 let e' = fmap (const e) e
159 let e' = fmap (const e) e
160− e'' <- switchPromptly never e'
160+ e'' <- switchHoldPromptly never e'
161 return (b, e'')
161 return (b, e'')
162− , (,) "switchPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
162+ , (,) "switchHoldPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
163 let e' = fmap (const e) e
163 let e' = fmap (const e) e
164− e'' <- switchPromptly never $ leftmost [e', e']
164+ e'' <- switchHoldPromptly never $ leftmost [e', e']
165 return (b, e'')
165 return (b, e'')
166− , (,) "switchPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
166+ , (,) "switchHoldPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
167 let e' = leftmost [e, e]
167 let e' = leftmost [e, e]
168− e'' <- switchPromptly never (fmap (const e) e')
168+ e'' <- switchHoldPromptly never (fmap (const e) e')
169 return (b, e'')
169 return (b, e'')
170− , (,) "switchPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
170+ , (,) "switchHoldPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
171 let e' = leftmost [e, e]
171 let e' = leftmost [e, e]
172− e'' <- switchPromptly never (fmap (const e') e)
172+ e'' <- switchHoldPromptly never (fmap (const e') e)
173 return (b, e'')
173 return (b, e'')
174 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
174 , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175 let e' = leftmost [e, e]
175 let e' = leftmost [e, e]
176 e'' <- switch <$> hold never (fmap (const e') e)
176 e'' <- switch <$> hold never (fmap (const e') e)
177 return (b, e'')
177 return (b, e'')
178− , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
178+ , (,) "switchHoldPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
179 let e' = flip push e $ \_ -> do
179 let e' = flip push e $ \_ -> do
180 Just <$> headE e
180 Just <$> headE e
181− e'' <- switchPromptly never e'
181+ e'' <- switchHoldPromptly never e'
182 return (b, e'')
182 return (b, e'')
183− , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
183+ , (,) "switchHoldPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
184 let e' = flip pushAlways e $ \_ -> do
184 let e' = flip pushAlways e $ \_ -> do
185− switchPromptly e never
185+ switchHoldPromptly e never
186− e'' <- switchPromptly never e'
186+ e'' <- switchHoldPromptly never e'
187 return (b, e'')
187 return (b, e'')
188 , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
188 , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
189 let e' = flip pushAlways e $ \_ -> return e
189 let e' = flip pushAlways e $ \_ -> return e
test/Reflex/Test/Micro.hs+13−13
@@ -128,36 +128,36 @@ testCases =
128 return $ coincidence $ flip pushAlways e $ const $ do
128 return $ coincidence $ flip pushAlways e $ const $ do
129 switch <$> hold ("x" <$ e) (e <$ e)
129 switch <$> hold ("x" <$ e) (e <$ e)
130
130
131− , testE "switchPromptly-1" $ do
131+ , testE "switchHoldPromptly-1" $ do
132 e <- events1
132 e <- events1
133 let e' = e <$ e
133 let e' = e <$ e
134− switchPromptly never $ e <$ e'
134+ switchHoldPromptly never $ e <$ e'
135
135
136− , testE "switchPromptly-2" $ do
136+ , testE "switchHoldPromptly-2" $ do
137 e <- events1
137 e <- events1
138− switchPromptly never $ deep (e <$ e)
138+ switchHoldPromptly never $ deep (e <$ e)
139
139
140− , testE "switchPromptly-3" $ do
140+ , testE "switchHoldPromptly-3" $ do
141 e <- events1
141 e <- events1
142− switchPromptly never $ (e <$ deep e)
142+ switchHoldPromptly never $ (e <$ deep e)
143
143
144− , testE "switchPromptly-4" $ do
144+ , testE "switchHoldPromptly-4" $ do
145 e <- events1
145 e <- events1
146− switchPromptly never $ (deep e <$ e)
146+ switchHoldPromptly never $ (deep e <$ e)
147
147
148 , testE "switch-5" $ do
148 , testE "switch-5" $ do
149 e <- events1
149 e <- events1
150 switch <$> hold never (deep e <$ e)
150 switch <$> hold never (deep e <$ e)
151
151
152− , testE "switchPromptly-5" $ do
152+ , testE "switchHoldPromptly-5" $ do
153 e <- events1
153 e <- events1
154− switchPromptly never $ flip push e $
154+ switchHoldPromptly never $ flip push e $
155 const (Just <$> headE e)
155 const (Just <$> headE e)
156
156
157− , testE "switchPromptly-6" $ do
157+ , testE "switchHoldPromptly-6" $ do
158 e <- events1
158 e <- events1
159− switchPromptly never $ flip pushAlways e $
159+ switchHoldPromptly never $ flip pushAlways e $
160− const (switchPromptly e never)
160+ const (switchHoldPromptly e never)
161
161
162 , testE "coincidence-1" $ do
162 , testE "coincidence-1" $ do
163 e <- events1
163 e <- events1