on develop help sign in
Merge pull request #292 from reflex-frp/release/0.6

Release/0.6

commita083a7664a1bcc881e1b9b2bb95413503a96dea4 authorAli Abrar <aliabrar@gmail.com> dateFri 22 Mar 2019, 19:29 (8 years ago) parentdece710b1c9506
33 files changed+671−471
ChangeLog.md+12 Quickref.md+7−7 README.md+10−5 default.nix+3−3 reflex.cabal+8−4 src/Data/AppendMap.hs+6−16 src/Data/Functor/Misc.hs+1−44 src/Reflex.hs+1 src/Reflex/Class.hs+219−79 src/Reflex/Collection.hs+8−10 src/Reflex/Dynamic.hs+10−158 src/Reflex/Dynamic/TH.hs+1−25 src/Reflex/DynamicWriter/Base.hs+8−3 src/Reflex/DynamicWriter/Class.hs+9−4 src/Reflex/EventWriter/Base.hs+2−2 src/Reflex/FastWeak.hs+10−6 src/Reflex/FunctorMaybe.hs+23−21 src/Reflex/Network.hs+10−4 src/Reflex/NotReady/Class.hs+10 src/Reflex/Optimizer.hs+8−4 src/Reflex/Patch.hs+12−5 src/Reflex/Patch/Class.hs+1−1 src/Reflex/Patch/MapWithMove.hs+1−1 src/Reflex/Profiled.hs+6 src/Reflex/Pure.hs+7−5 src/Reflex/Query/Base.hs+23−30 src/Reflex/Query/Class.hs+1−1 src/Reflex/Requester/Base.hs+107−3 src/Reflex/Spider.hs+5−2 src/Reflex/Spider/Internal.hs+5−5 src/Reflex/Time.hs+113−13 src/Reflex/Workflow.hs+13−1 test/RequesterT.hs+11−9
ChangeLog.mdnew file+12
@@ -0,0 +1,12 @@
1+# Revision history for reflex
2+
3+## 0.6.0.0 -- 2019-03-20
4+
5+* Deprecate FunctorMaybe in favor of Data.Witherable.Filterable. We still export fmapMaybe, ffilter, etc., but they all rely on Filterable now.
6+* Rename MonadDynamicWriter to DynamicWriter and add a deprecation for the old name.
7+* Remove many deprecated functions.
8+* Add a Num instance for Dynamic.
9+* Add matchRequestsWithResponses to make it easier to use Requester with protocols that don't do this matching for you.
10+* Add withRequesterT to map functions over the request and response of a RequesterT.
11+* Suppress nil patches in QueryT as an optimization. The Query type must now have an Eq instance.
12+* Add throttleBatchWithLag to Reflex.Time. See that module for details.
Quickref.md+7−7
@@ -57,15 +57,15 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
5757[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
5858[ ] leftmost :: [Event a] -> Event a
5959[ ] mergeList :: [Event a] -> Event (NonEmpty a)
60−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
60+[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
6161[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
6262
6363-- Efficient one-to-many fanout
64−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
66−[ ] select :: EventSelector k -> k a -> Event a
67−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
64+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65+[ ] fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
66+[ ] select :: EventSelector k -> k a -> Event a
67+[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68+[ ] fanThese :: Event (These a b) -> (Event a, Event b)
6969
7070-- Event to Event via function that can sample current values
7171[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
@@ -166,7 +166,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
166166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
167167[ ] switch :: Behavior (Event a) -> Event a
168168
169−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
169+-- Flatten Dynamic-of-Event to Event. New Event is used immediately.
170170[ ] switchDyn :: Dynamic (Event a) -> Event a
171171
172172-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
README.md+10−5
@@ -1,15 +1,15 @@
1−## Reflex
1+## [Reflex](https://reflex-frp.org/)
22### Practical Functional Reactive Programming
33
4−Reflex is an fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
4+Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
55
66[Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
77
8−Comprehensive documentation is still a work in progress, but a tutorial for Reflex and Reflex-DOM is available at https://github.com/reflex-frp/reflex-platform and an introductory talk given at the New York Haskell Meetup is available here: [Part 1](https://www.youtube.com/watch?v=mYvkcskJbc4) / [Part 2](https://www.youtube.com/watch?v=3qfc9XFVo2c).
9−
108A summary of Reflex functions is available in the [quick reference](Quickref.md).
119
12−### Additional resources
10+**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
11+
12+### Resources
1313[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
1414
1515[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
@@ -17,3 +17,8 @@ A summary of Reflex functions is available in the [quick reference](Quickref.md)
1717[hackage](https://hackage.haskell.org/package/reflex)
1818
1919[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
20+
21+### Hacking
22+
23+Use the `./scripts/hack-on reflex` script in [Reflex Platform](https://github.com/reflex-frp/reflex-platform) to checkout the source code of `reflex` locally in `reflex-platform/reflex` directory.
24+Then do modifications to the source in place, and use the `./try-reflex` or `./scripts/work-on` scripts to create the shell to test your changes.
default.nix+3−3
@@ -6,12 +6,12 @@
66, template-haskell , these, time, transformers
77, transformers-compat, unbounded-delays, prim-uniq
88, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers
9+, monoidal-containers, witherable
1010, useTemplateHaskell ? true
1111}:
1212mkDerivation {
1313 pname = "reflex";
14− version = "0.5";
14+ version = "0.5.0.1";
1515 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
1616 libraryHaskellDepends = [
1717 base bifunctors containers dependent-map dependent-sum
@@ -21,7 +21,7 @@ mkDerivation {
2121 transformers-compat prim-uniq
2222 base bifunctors containers deepseq dependent-map dependent-sum
2323 mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
24+ random time unbounded-delays monoidal-containers witherable
2525 ] ++ (if ghc.isGhcjs or false then [
2626 ghcjs-base
2727 ] else []) ++ (if !useTemplateHaskell then [] else [
reflex.cabal+8−4
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.5
2+Version: 0.6
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -15,6 +15,7 @@ bug-reports: https://github.com/reflex-frp/reflex/issues
1515extra-source-files:
1616 README.md
1717 Quickref.md
18+ ChangeLog.md
1819
1920flag use-reflex-optimizer
2021 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -60,11 +61,12 @@ library
6061 semigroups >= 0.16 && < 0.19,
6162 stm >= 2.4 && < 2.6,
6263 syb >= 0.5 && < 0.8,
63− these >= 0.4 && < 0.7.6,
64+ these >= 0.4 && < 0.7.7,
6465 time >= 1.4 && < 1.9,
6566 transformers >= 0.2,
6667 transformers-compat >= 0.3,
67− unbounded-delays >= 0.1.0.0 && < 0.2
68+ unbounded-delays >= 0.1.0.0 && < 0.2,
69+ witherable >= 0.2 && < 0.4
6870
6971 exposed-modules:
7072 Data.AppendMap,
@@ -233,14 +235,16 @@ test-suite RequesterT
233235 main-is: RequesterT.hs
234236 hs-source-dirs: test
235237 build-depends: base
238+ , containers
239+ , deepseq >= 1.3 && < 1.5
236240 , dependent-sum
237241 , dependent-map
238242 , lens
243+ , mtl
239244 , these
240245 , transformers
241246 , reflex
242247 , ref-tf
243− buildable: False
244248 other-modules:
245249 Reflex.TestPlan
246250 Reflex.Plan.Pure
src/Data/AppendMap.hs+6−16
@@ -1,15 +1,10 @@
11{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveGeneric #-}
4−{-# LANGUAGE DeriveTraversable #-}
52{-# LANGUAGE FlexibleContexts #-}
63{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
84{-# LANGUAGE MultiParamTypeClasses #-}
95{-# LANGUAGE PatternSynonyms #-}
106{-# LANGUAGE RoleAnnotations #-}
117{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE StandaloneDeriving #-}
138{-# LANGUAGE TypeFamilies #-}
149{-# OPTIONS_GHC -fno-warn-orphans #-}
1510-- | 'Data.Map' with a better 'Monoid' instance
@@ -31,9 +26,9 @@ import qualified Data.Map.Internal.Debug as Map (showTree, showTreeWith)
3126#else
3227import qualified Data.Map as Map (showTree, showTreeWith)
3328#endif
34−import Data.Map.Monoidal
35−import Reflex.Class (FunctorMaybe (..))
36−import Reflex.Patch (Additive, Group (..))
29+import Data.Witherable (Filterable(..))
30+import Data.Map.Monoidal (MonoidalMap(..), delete, null, empty)
31+import qualified Data.Map.Monoidal as M
3732
3833{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
3934type AppendMap = MonoidalMap
@@ -45,8 +40,8 @@ _unAppendMap = getMonoidalMap
4540pattern AppendMap :: Map k v -> MonoidalMap k v
4641pattern AppendMap m = MonoidalMap m
4742
48−instance FunctorMaybe (MonoidalMap k) where
49− fmapMaybe = mapMaybe
43+instance Filterable (MonoidalMap k) where
44+ mapMaybe = M.mapMaybe
5045
5146-- | Deletes a key, returning 'Nothing' if the result is empty.
5247nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -60,17 +55,12 @@ mapMaybeNoNull :: (a -> Maybe b)
6055 -> MonoidalMap token a
6156 -> Maybe (MonoidalMap token b)
6257mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
58+ let bs = mapMaybe f as
6459 in if null bs
6560 then Nothing
6661 else Just bs
6762
6863-- TODO: Move instances to `Reflex.Patch`
69−instance (Ord k, Group q) => Group (MonoidalMap k q) where
70− negateG = map negateG
71−
72−instance (Ord k, Additive q) => Additive (MonoidalMap k q)
73−
7464showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
7565showTree = coerce (Map.showTree :: Map k a -> String)
7666
src/Data/Functor/Misc.hs+1−44
@@ -38,17 +38,10 @@ module Data.Functor.Misc
3838 , dmapToThese
3939 , eitherToDSum
4040 , dsumToEither
41− -- * Deprecated functions
42− , sequenceDmap
43− , wrapDMap
44− , rewrapDMap
45− , unwrapDMap
46− , unwrapDMapMaybe
47− , extractFunctorDMap
4841 , ComposeMaybe (..)
4942 ) where
5043
51−import Control.Applicative (Applicative, (<$>))
44+import Control.Applicative ((<$>))
5245import Control.Monad.Identity
5346import Data.Dependent.Map (DMap)
5447import qualified Data.Dependent.Map as DMap
@@ -249,39 +242,3 @@ newtype ComposeMaybe f a =
249242 ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
250243
251244deriving instance Functor f => Functor (ComposeMaybe f)
252−
253−--------------------------------------------------------------------------------
254−-- Deprecated functions
255−--------------------------------------------------------------------------------
256−
257−{-# INLINE sequenceDmap #-}
258−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ -> fmap Identity)' instead" #-}
259−-- | Run the actions contained in the 'DMap'
260−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
261−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
262−
263−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
264−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
265−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
266−wrapDMap f = DMap.map $ f . runIdentity
267−
268−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
269−-- | Replace one functor for a 'DMap''s values with a different functor
270−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
271−rewrapDMap = DMap.map
272−
273−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
274−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
275−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
276−unwrapDMap f = DMap.map $ Identity . f
277−
278−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
279−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
280−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
281−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
282−
283−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
284−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
285−-- underlying functor
286−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
287−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
src/Reflex.hs+1
@@ -19,6 +19,7 @@ import Reflex.Dynamic.TH as X
1919import Reflex.Dynamic.Uniq as X
2020import Reflex.DynamicWriter.Base as X
2121import Reflex.DynamicWriter.Class as X
22+import Reflex.NotReady.Class as X
2223import Reflex.PerformEvent.Base as X
2324import Reflex.PerformEvent.Class as X
2425import Reflex.PostBuild.Base as X
src/Reflex/Class.hs+219−79
@@ -21,9 +21,13 @@
2121{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2222#endif
2323
24−-- | This module contains the Reflex interface, as well as a variety of
25−-- convenience functions for working with 'Event's, 'Behavior's, and other
26−-- signals.
24+-- |
25+-- Module:
26+-- Reflex.Class
27+-- Description:
28+-- This module contains the Reflex interface, as well as a variety of
29+-- convenience functions for working with 'Event's, 'Behavior's, and other
30+-- signals.
2731module Reflex.Class
2832 ( module Reflex.Patch
2933 -- * Primitives
@@ -133,8 +137,10 @@ module Reflex.Class
133137 -- * Unsafe functions
134138 , unsafeDynamic
135139 , unsafeMapIncremental
136− -- * 'FunctorMaybe'
137− , FunctorMaybe (..)
140+ -- * 'Filterable' convenience functions
141+ , FunctorMaybe -- fmapMaybe is purposely not exported from deprecated 'FunctorMaybe' and the new alias is exported instead
142+ , mapMaybe -- Re-exported for convenience
143+ , fmapMaybe
138144 , fforMaybe
139145 , ffilter
140146 , filterLeft
@@ -144,10 +150,11 @@ module Reflex.Class
144150 , ffor2
145151 , ffor3
146152 -- * Deprecated functions
147− , appendEvents
148− , onceE
149− , sequenceThese
153+ , switchPromptly
154+ , switchPromptOnly
155+ -- * "Cheap" functions
150156 , fmapMaybeCheap
157+ , mapMaybeCheap
151158 , fmapCheap
152159 , fforCheap
153160 , fforMaybeCheap
@@ -155,8 +162,6 @@ module Reflex.Class
155162 , tagCheap
156163 , mergeWithCheap
157164 , mergeWithCheap'
158− , switchPromptly
159− , switchPromptOnly
160165 -- * Slow, but general, implementations
161166 , slowHeadE
162167 ) where
@@ -193,7 +198,10 @@ import qualified Data.Some as Some
193198import Data.String
194199import Data.These
195200import Data.Type.Coercion
196−import Reflex.FunctorMaybe
201+import Data.Witherable (Filterable(..))
202+import qualified Data.Witherable as W
203+import Reflex.FunctorMaybe (FunctorMaybe)
204+import qualified Reflex.FunctorMaybe
197205import Reflex.Patch
198206import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
199207
@@ -256,8 +264,7 @@ class ( MonadHold t (PushM t)
256264 -- | Create an 'Event' that will occur whenever the currently-selected input
257265 -- 'Event' occurs
258266 switch :: Behavior t (Event t a) -> Event t a
259− -- | Create an 'Event' that will occur whenever the input event is occurring
260− -- and its occurrence value, another 'Event', is also occurring
267+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring
261268 coincidence :: Event t (Event t a) -> Event t a
262269 -- | Extract the 'Behavior' of a 'Dynamic'.
263270 current :: Dynamic t a -> Behavior t a
@@ -388,8 +395,8 @@ mapAccumMaybeMIncremental f z e = do
388395 return $ case result of
389396 (Nothing, Nothing) -> Nothing
390397 _ -> Just result
391− d' <- holdIncremental z $ fmapMaybe fst e'
392− return (d', fmapMaybe snd e')
398+ d' <- holdIncremental z $ mapMaybe fst e'
399+ return (d', mapMaybe snd e')
393400
394401slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
395402slowHeadE e = do
@@ -399,14 +406,14 @@ slowHeadE e = do
399406
400407-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
401408-- key. This is much more efficient than filtering for each key with
402−-- 'fmapMaybe'.
409+-- 'mapMaybe'.
403410newtype EventSelector t k = EventSelector
404411 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
405412 -- determined by the type of the key, so this can be used to fan-out
406413 -- 'Event's whose sub-'Event's have different types.
407414 --
408415 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
409− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
416+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
410417 -- occurrences of an 'Event'.
411418 select :: forall a. k a -> Event t a
412419 }
@@ -541,6 +548,15 @@ instance (Reflex t, Num a) => Num (Behavior t a) where
541548 negate = fmap negate
542549 signum = fmap signum
543550
551+instance (Num a, Reflex t) => Num (Dynamic t a) where
552+ (+) = liftA2 (+)
553+ (*) = liftA2 (*)
554+ abs = fmap abs
555+ signum = fmap signum
556+ fromInteger = pure . fromInteger
557+ negate = fmap negate
558+ (-) = liftA2 (-)
559+
544560instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
545561 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
546562 sconcat = pull . fmap sconcat . mapM sample
@@ -550,22 +566,25 @@ instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
550566 times1p n = fmap $ times1p n
551567#endif
552568
553−-- | Flipped version of 'fmapMaybe'.
554−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
555−fforMaybe = flip fmapMaybe
569+-- | Alias for 'mapMaybe'
570+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
571+fmapMaybe = mapMaybe
572+
573+-- | Flipped version of 'mapMaybe'.
574+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b
575+fforMaybe = flip mapMaybe
556576
557577-- | Filter 'f a' using the provided predicate.
558−-- Relies on 'fforMaybe'.
559−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
560−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
578+ffilter :: Filterable f => (a -> Bool) -> f a -> f a
579+ffilter = W.filter
561580
562581-- | Filter 'Left's from 'f (Either a b)' into 'a'.
563−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
564−filterLeft = fmapMaybe (either Just (const Nothing))
582+filterLeft :: Filterable f => f (Either a b) -> f a
583+filterLeft = mapMaybe (either Just (const Nothing))
565584
566585-- | Filter 'Right's from 'f (Either a b)' into 'b'.
567−filterRight :: FunctorMaybe f => f (Either a b) -> f b
568−filterRight = fmapMaybe (either (const Nothing) Just)
586+filterRight :: Filterable f => f (Either a b) -> f b
587+filterRight = mapMaybe (either (const Nothing) Just)
569588
570589-- | Left-biased event union (prefers left event on simultaneous
571590-- occurrence).
@@ -583,13 +602,18 @@ instance Reflex t => Bind (Event t) where
583602
584603instance Reflex t => Functor (Event t) where
585604 {-# INLINE fmap #-}
586− fmap f = fmapMaybe $ Just . f
605+ fmap f = mapMaybe $ Just . f
587606 {-# INLINE (<$) #-}
588607 x <$ e = fmapCheap (const x) e
589608
609+-- TODO Remove this instance
590610instance Reflex t => FunctorMaybe (Event t) where
591611 {-# INLINE fmapMaybe #-}
592− fmapMaybe f = push $ return . f
612+ fmapMaybe = mapMaybe
613+
614+instance Reflex t => Filterable (Event t) where
615+ {-# INLINE mapMaybe #-}
616+ mapMaybe f = push $ return . f
593617
594618-- | Never: @'zero' = 'never'@.
595619instance Reflex t => Plus (Event t) where
@@ -807,7 +831,7 @@ fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
807831fanEither e =
808832 let justLeft = either Just (const Nothing)
809833 justRight = either (const Nothing) Just
810− in (fmapMaybe justLeft e, fmapMaybe justRight e)
834+ in (mapMaybe justLeft e, mapMaybe justRight e)
811835
812836-- | Split the event into separate events for 'This' and 'That' values,
813837-- allowing them to fire simultaneously when the input value is 'These'.
@@ -819,7 +843,7 @@ fanThese e =
819843 that (That y) = Just y
820844 that (These _ y) = Just y
821845 that _ = Nothing
822− in (fmapMaybe this e, fmapMaybe that e)
846+ in (mapMaybe this e, mapMaybe that e)
823847
824848-- | Split the event into an 'EventSelector' that allows efficient selection of
825849-- the individual 'Event's.
@@ -998,14 +1022,16 @@ distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) .
9981022difference :: Reflex t => Event t a -> Event t b -> Event t a
9991023difference = alignEventWithMaybe $ \case
10001024 This a -> Just a
1001− _ -> Nothing
1025+ _ -> Nothing
10021026
1027+-- | Zips two values by taking the union of their shapes and combining with the provided function.
1028+-- 'Nothing' values are dropped.
10031029alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1004−alignEventWithMaybe f ea eb =
1005− fmapMaybe (f <=< dmapToThese)
1006− $ merge
1007− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1030+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $
1031+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
10081032
1033+-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that
1034+-- matches the provided key.
10091035filterEventKey
10101036 :: forall t m k v a.
10111037 ( Reflex t
@@ -1023,7 +1049,10 @@ filterEventKey k kv' = do
10231049 Nothing -> Nothing
10241050 takeWhileJustE f kv'
10251051
1026−
1052+-- | "Factor" the input 'DSum' 'Event' to produce an 'Event' which
1053+-- fires when the 'DSum' key changes and contains both the value of the
1054+-- 'DSum' at switchover and an 'Event' of values produced by subsequent
1055+-- firings of the input 'Event' that do not change the 'DSum' key.
10271056factorEvent
10281057 :: forall t m k v a.
10291058 ( Reflex t
@@ -1073,26 +1102,94 @@ class Reflex t => Accumulator t f | f -> t where
10731102 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
10741103 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
10751104
1076−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1105+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1106+-- with the provided function. See 'foldDyn'.
1107+accumDyn
1108+ :: (Reflex t, MonadHold t m, MonadFix m)
1109+ => (a -> b -> a)
1110+ -> a
1111+ -> Event t b
1112+ -> m (Dynamic t a)
10771113accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1078−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
1114+
1115+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1116+-- with the provided 'PushM' action.
1117+accumMDyn
1118+ :: (Reflex t, MonadHold t m, MonadFix m)
1119+ => (a -> b -> PushM t a)
1120+ -> a
1121+ -> Event t b
1122+ -> m (Dynamic t a)
10791123accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1080−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1124+
1125+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1126+-- with the provided function, discarding 'Nothing' results.
1127+accumMaybeDyn
1128+ :: (Reflex t, MonadHold t m, MonadFix m)
1129+ => (a -> b -> Maybe a)
1130+ -> a
1131+ -> Event t b
1132+ -> m (Dynamic t a)
10811133accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1082−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
1134+
1135+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1136+-- with the provided 'PushM' action, discarding 'Nothing' results.
1137+accumMaybeMDyn
1138+ :: (Reflex t, MonadHold t m, MonadFix m)
1139+ => (a -> b -> PushM t (Maybe a))
1140+ -> a
1141+ -> Event t b
1142+ -> m (Dynamic t a)
10831143accumMaybeMDyn f z e = do
10841144 rec let e' = flip push e $ \o -> do
10851145 v <- sample $ current d'
10861146 f v o
10871147 d' <- holdDyn z e'
10881148 return d'
1089−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1149+
1150+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1151+-- with a function that both accumulates and produces a value to fire
1152+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
1153+mapAccumDyn
1154+ :: (Reflex t, MonadHold t m, MonadFix m)
1155+ => (a -> b -> (a, c))
1156+ -> a
1157+ -> Event t b
1158+ -> m (Dynamic t a, Event t c)
10901159mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1091−mapAccumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1160+
1161+-- | Similar to 'mapAccumDyn' except that the combining function is a
1162+-- 'PushM' action.
1163+mapAccumMDyn
1164+ :: (Reflex t, MonadHold t m, MonadFix m)
1165+ => (a -> b -> PushM t (a, c))
1166+ -> a
1167+ -> Event t b
1168+ -> m (Dynamic t a, Event t c)
10921169mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1093−mapAccumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1170+
1171+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event' with
1172+-- a function that both optionally accumulates and optionally produces
1173+-- a value to fire as a separate output 'Event'.
1174+-- Note that because 'Nothing's are discarded in both cases, the output
1175+-- 'Event' may fire even though the output 'Dynamic' has not changed, and
1176+-- the output 'Dynamic' may update even when the output 'Event' is not firing.
1177+mapAccumMaybeDyn
1178+ :: (Reflex t, MonadHold t m, MonadFix m)
1179+ => (a -> b -> (Maybe a, Maybe c))
1180+ -> a
1181+ -> Event t b
1182+ -> m (Dynamic t a, Event t c)
10941183mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1095−mapAccumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1184+
1185+-- | Like 'mapAccumMaybeDyn' except that the combining function is a
1186+-- 'PushM' action.
1187+mapAccumMaybeMDyn
1188+ :: (Reflex t, MonadHold t m, MonadFix m)
1189+ => (a -> b -> PushM t (Maybe a, Maybe c))
1190+ -> a
1191+ -> Event t b
1192+ -> m (Dynamic t a, Event t c)
10961193mapAccumMaybeMDyn f z e = do
10971194 rec let e' = flip push e $ \o -> do
10981195 v <- sample $ current d'
@@ -1100,18 +1197,42 @@ mapAccumMaybeMDyn f z e = do
11001197 return $ case result of
11011198 (Nothing, Nothing) -> Nothing
11021199 _ -> Just result
1103− d' <- holdDyn z $ fmapMaybe fst e'
1104− return (d', fmapMaybe snd e')
1200+ d' <- holdDyn z $ mapMaybe fst e'
1201+ return (d', mapMaybe snd e')
11051202
1203+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1204+-- with the provided function.
11061205{-# INLINE accumB #-}
1107−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1206+accumB
1207+ :: (Reflex t, MonadHold t m, MonadFix m)
1208+ => (a -> b -> a)
1209+ -> a
1210+ -> Event t b
1211+ -> m (Behavior t a)
11081212accumB f = accumMaybeB $ \v o -> Just $ f v o
1213+
1214+-- | Like 'accumB' except that the combining function is a 'PushM' action.
11091215{-# INLINE accumMB #-}
1110−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
1216+accumMB
1217+ :: (Reflex t, MonadHold t m, MonadFix m)
1218+ => (a -> b -> PushM t a)
1219+ -> a
1220+ -> Event t b
1221+ -> m (Behavior t a)
11111222accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1223+
1224+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1225+-- with the provided function, discarding 'Nothing' results.
11121226{-# INLINE accumMaybeB #-}
1113−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1227+accumMaybeB
1228+ :: (Reflex t, MonadHold t m, MonadFix m)
1229+ => (a -> b -> Maybe a)
1230+ -> a
1231+ -> Event t b
1232+ -> m (Behavior t a)
11141233accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1234+
1235+-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action.
11151236{-# INLINE accumMaybeMB #-}
11161237accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
11171238accumMaybeMB f z e = do
@@ -1120,16 +1241,42 @@ accumMaybeMB f z e = do
11201241 f v o
11211242 d' <- hold z e'
11221243 return d'
1244+
1245+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1246+-- with a function that both accumulates and produces a value to fire
1247+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
11231248{-# INLINE mapAccumB #-}
1124−mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1249+mapAccumB
1250+ :: (Reflex t, MonadHold t m, MonadFix m)
1251+ => (a -> b -> (a, c))
1252+ -> a
1253+ -> Event t b
1254+ -> m (Behavior t a, Event t c)
11251255mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1256+
1257+-- | Like 'mapAccumB' except that the combining function is a 'PushM' action.
11261258{-# INLINE mapAccumMB #-}
1127−mapAccumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1259+mapAccumMB
1260+ :: (Reflex t, MonadHold t m, MonadFix m)
1261+ => (a -> b -> PushM t (a, c))
1262+ -> a
1263+ -> Event t b
1264+ -> m (Behavior t a, Event t c)
11281265mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1266+
1267+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event' with
1268+-- a function that both optionally accumulates and optionally produces
1269+-- a value to fire as a separate output 'Event'. 'Nothing's are discarded.
11291270{-# INLINE mapAccumMaybeB #-}
1130−mapAccumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1271+mapAccumMaybeB
1272+ :: (Reflex t, MonadHold t m, MonadFix m)
1273+ => (a -> b -> (Maybe a, Maybe c))
1274+ -> a
1275+ -> Event t b
1276+ -> m (Behavior t a, Event t c)
11311277mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
11321278
1279+-- | LIke 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
11331280{-# INLINE mapAccumMaybeMB #-}
11341281mapAccumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
11351282mapAccumMaybeMB f z e = do
@@ -1139,8 +1286,8 @@ mapAccumMaybeMB f z e = do
11391286 return $ case result of
11401287 (Nothing, Nothing) -> Nothing
11411288 _ -> Just result
1142− d' <- hold z $ fmapMaybe fst e'
1143− return (d', fmapMaybe snd e')
1289+ d' <- hold z $ mapMaybe fst e'
1290+ return (d', mapMaybe snd e')
11441291
11451292-- | Accumulate occurrences of an 'Event', producing an output occurrence each
11461293-- time. Discard the underlying 'Accumulator'.
@@ -1284,38 +1431,53 @@ infixl 4 <@
12841431-- Cheap Functions
12851432------------------
12861433
1434+-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'.
12871435{-# INLINE pushAlwaysCheap #-}
12881436pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
12891437pushAlwaysCheap f = pushCheap (fmap Just . f)
12901438
1439+-- | A "cheap" version of 'mapMaybe'. See the performance note on 'pushCheap'.
1440+{-# INLINE mapMaybeCheap #-}
1441+mapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1442+mapMaybeCheap f = pushCheap $ return . f
1443+
1444+-- | An alias for 'mapMaybeCheap'
12911445{-# INLINE fmapMaybeCheap #-}
12921446fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1293−fmapMaybeCheap f = pushCheap $ return . f
1447+fmapMaybeCheap = mapMaybeCheap
1448+
12941449
1450+-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'.
12951451{-# INLINE fforMaybeCheap #-}
12961452fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1297−fforMaybeCheap = flip fmapMaybeCheap
1453+fforMaybeCheap = flip mapMaybeCheap
12981454
1455+-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'.
12991456{-# INLINE fforCheap #-}
13001457fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
13011458fforCheap = flip fmapCheap
13021459
1460+-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'.
13031461{-# INLINE fmapCheap #-}
13041462fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
13051463fmapCheap f = pushCheap $ return . Just . f
13061464
1465+-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'.
13071466{-# INLINE tagCheap #-}
13081467tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
13091468tagCheap b = pushAlwaysCheap $ \_ -> sample b
13101469
1470+-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
13111471{-# INLINE mergeWithCheap #-}
13121472mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
13131473mergeWithCheap = mergeWithCheap' id
13141474
1475+-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'.
13151476{-# INLINE mergeWithCheap' #-}
13161477mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
13171478mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
13181479
1480+-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
13191481{-# INLINE mergeWithFoldCheap' #-}
13201482mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
13211483mergeWithFoldCheap' f es =
@@ -1328,28 +1490,6 @@ mergeWithFoldCheap' f es =
13281490-- Deprecated functions
13291491--------------------------------------------------------------------------------
13301492
1331−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1332−-- occurs. If both occur at the same time they are combined using 'mappend'.
1333−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1334−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1335−appendEvents = alignWith $ mergeThese mappend
1336−
1337−-- | Alias for 'headE'
1338−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1339−onceE :: MonadHold t m => Event t a -> m (Event t a)
1340−onceE = headE
1341−
1342−-- | Run both sides of a 'These' monadically, combining the results.
1343−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1344−#ifdef USE_TEMPLATE_HASKELL
1345−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1346−#endif
1347−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1348−sequenceThese t = case t of
1349− This ma -> fmap This ma
1350− These ma mb -> liftM2 These ma mb
1351− That mb -> fmap That mb
1352−
13531493{-# 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'. " #-}
13541494switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
13551495switchPromptly = switchHoldPromptly
src/Reflex/Collection.hs+8−10
@@ -10,12 +10,14 @@
1010#ifdef USE_REFLEX_OPTIMIZER
1111{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1212#endif
13+-- |
14+-- Module:
15+-- Reflex.Collection
1316module Reflex.Collection
1417 (
1518 -- * Widgets on Collections
1619 listHoldWithKey
1720 , listWithKey
18− , listWithKey'
1921 , listWithKeyShallowDiff
2022 , listViewWithKey
2123 , selectViewListWithKey
@@ -38,6 +40,9 @@ import Reflex.Adjustable.Class
3840import Reflex.Dynamic
3941import Reflex.PostBuild.Class
4042
43+-- | Create a set of widgets based on the provided 'Map'. When the
44+-- input 'Event' fires, remove widgets for keys with the value 'Nothing'
45+-- and add/replace widgets for keys with 'Just' values.
4146listHoldWithKey
4247 :: forall t m k v a
4348 . (Ord k, Adjustable t m, MonadHold t m)
@@ -96,15 +101,6 @@ listWithKey vals mkChild = do
96101 listHoldWithKey Map.empty changeVals $ \k v ->
97102 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
98103
99−{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
100−listWithKey'
101− :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m)
102− => Map k v
103− -> Event t (Map k (Maybe v))
104− -> (k -> v -> Event t v -> m a)
105− -> m (Dynamic t (Map k a))
106−listWithKey' = listWithKeyShallowDiff
107−
108104-- | Display the given map of items (in key order) using the builder
109105-- function provided, and update it with the given event. 'Nothing'
110106-- update entries will delete the corresponding children, and 'Just'
@@ -182,6 +178,8 @@ selectViewListWithKey selection vals mkChild = do
182178 return $ fmap ((,) k) selectSelf
183179 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
184180
181+-- | Like 'selectViewListWithKey' but discards the value of the list
182+-- item widget's output 'Event'.
185183selectViewListWithKey_
186184 :: forall t m k v a
187185 . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
src/Reflex/Dynamic.hs+10−158
@@ -17,8 +17,12 @@
1717#ifdef USE_REFLEX_OPTIMIZER
1818{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1919#endif
20−-- | This module contains various functions for working with 'Dynamic' values.
21−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
20+-- |
21+-- Module:
22+-- Reflex.Dynamic
23+-- Description:
24+-- This module contains various functions for working with 'Dynamic' values.
25+-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
2226module Reflex.Dynamic
2327 ( -- * Basics
2428 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
@@ -70,32 +74,12 @@ module Reflex.Dynamic
7074 , distributeFHListOverDynPure
7175 -- * Unsafe
7276 , unsafeDynamic
73− -- * Deprecated functions
74− , apDyn
75− , attachDyn
76− , attachDynWith
77− , attachDynWithMaybe
78− , collectDyn
79− , combineDyn
80− , distributeDMapOverDyn
81− , distributeFHListOverDyn
82− , forDyn
83− , getDemuxed
84− , joinDyn
85− , mapDyn
86− , mconcatDyn
87− , nubDyn
88− , splitDyn
89− , tagDyn
90− , uniqDyn
91− , uniqDynBy
9277 ) where
9378
9479import Data.Functor.Compose
9580import Data.Functor.Misc
9681import Reflex.Class
9782
98−import Control.Applicative ((<*>))
9983import Control.Monad
10084import Control.Monad.Fix
10185import Control.Monad.Identity
@@ -103,11 +87,10 @@ import Data.Align
10387import Data.Dependent.Map (DMap)
10488import qualified Data.Dependent.Map as DMap
10589import Data.Dependent.Sum (DSum (..))
106−import Data.Functor.Product
10790import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
10891import Data.Map (Map)
10992import Data.Maybe
110−import Data.Monoid hiding (Product)
93+import Data.Monoid ((<>))
11194import Data.These
11295
11396import Debug.Trace
@@ -236,7 +219,7 @@ distributeListOverDynPure =
236219-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
237220-- with the current values of the 'Dynamic's in a map.
238221joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
239−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
222+joinDynThroughMap = join . fmap distributeMapOverDynPure
240223
241224-- | Print the value of the 'Dynamic' when it is first read and on each
242225-- subsequent change that is observed (as 'traceEvent'), prefixed with the
@@ -352,7 +335,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
352335--
353336-- > demuxed (demux d) k === fmap (== k) d
354337--
355−-- However, the when getDemuxed is used multiple times, the complexity is only
338+-- However, when getDemuxed is used multiple times, the complexity is only
356339-- /O(log(n))/, rather than /O(n)/ for fmap.
357340data Demux t k = Demux { demuxValue :: Behavior t k
358341 , demuxSelector :: EventSelector t (Const2 k Bool)
@@ -506,7 +489,7 @@ instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
506489
507490-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
508491-- single 'Dynamic' whose value represents all the current values of the input's
509−-- consitutent 'Dynamic's.
492+-- constituent 'Dynamic's.
510493collectDynPure :: ( RebuildSortedHList (HListElems b)
511494 , IsHList a, IsHList b
512495 , AllAreFunctors (Dynamic t) (HListElems b)
@@ -547,134 +530,3 @@ instance IsHList (a, b, c, d, e, f) where
547530#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
548531 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
549532#endif
550−
551−--------------------------------------------------------------------------------
552−-- Deprecated functions
553−--------------------------------------------------------------------------------
554−
555−-- | Map a function over a 'Dynamic'.
556−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
557−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
558−mapDyn f = return . fmap f
559−
560−-- | Flipped version of 'mapDyn'.
561−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
562−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
563−forDyn a = return . ffor a
564−
565−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
566−-- of the tuple.
567−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
568−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
569−splitDyn = return . splitDynPure
570−
571−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
572−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
573−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
574−mconcatDyn = return . mconcat
575−
576−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
577−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
578−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
579−distributeDMapOverDyn = return . distributeDMapOverDynPure
580−
581−-- | Merge two 'Dynamic's into a new one using the provided function. The new
582−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
583−-- its value.
584−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
585−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
586−combineDyn f a b = return $ zipDynWith f a b
587−
588−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
589−--
590−-- > do
591−-- > person <- Person `mapDyn` dynFirstName
592−-- > `apDyn` dynListName
593−-- > `apDyn` dynAge
594−-- > `apDyn` dynAddress
595−{-# DEPRECATED apDyn "Use 'ffor m (<*> a)' instead of 'apDyn m a'; consider eliminating monadic style, since Dynamics are now Applicative and can be used with applicative style directly" #-}
596−#ifdef USE_TEMPLATE_HASKELL
597−{-# ANN apDyn "HLint: ignore Use fmap" #-}
598−#endif
599−apDyn :: forall t m a b. (Reflex t, Monad m)
600− => m (Dynamic t (a -> b))
601− -> Dynamic t a
602− -> m (Dynamic t b)
603−apDyn m a = fmap (<*> a) m
604−
605−--TODO: The pattern of using hold (sample b0) can be reused in various places as a safe way of building certain kinds of Dynamics; see if we can factor this out
606−-- | This function no longer needs to be monadic, so it has been replaced by
607−-- 'demuxed', which is pure.
608−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
609−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
610−getDemuxed d = return . demuxed d
611−
612−-- | This function no longer needs to be monadic, so it has been replaced by
613−-- 'distributeFHListOverDynPure', which is pure.
614−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
615−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
616−distributeFHListOverDyn = return . distributeFHListOverDynPure
617−
618−-- | This function no longer needs to be monadic, so it has been replaced by
619−-- 'collectDynPure', which is pure.
620−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
621−collectDyn :: ( RebuildSortedHList (HListElems b)
622− , IsHList a, IsHList b
623− , AllAreFunctors (Dynamic t) (HListElems b)
624− , Reflex t, Monad m
625− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
626− ) => a -> m (Dynamic t b)
627−collectDyn = return . collectDynPure
628−
629−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
630−-- semantics.
631−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
632−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
633−tagDyn = tagPromptlyDyn
634−
635−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
636−-- semantics.
637−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
638−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
639−attachDyn = attachPromptlyDyn
640−
641−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
642−-- semantics.
643−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
644−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
645−attachDynWith = attachPromptlyDynWith
646−
647−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
648−-- its semantics.
649−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
650−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
651−attachDynWithMaybe = attachPromptlyDynWithMaybe
652−
653−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
654−-- current value will always be equal to the current value's current value, and
655−-- will change whenever either the inner or the outer (or both) values change.
656−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
657−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
658−joinDyn = join
659−
660−-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
661−-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
662−-- circumstances. Also, note that, unlike 'nub', this function does not prevent
663−-- all recurrences of a value, only consecutive recurrences.
664−{-# DEPRECATED nubDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
665−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
666−nubDyn = uniqDyn
667−
668−-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
669−-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
670−-- circumstances.
671−{-# DEPRECATED uniqDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
672−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
673−uniqDyn = uniqDynBy (==)
674−
675−-- | WARNING: This function is impure. Use 'holdUniqDynBy' instead.
676−{-# DEPRECATED uniqDynBy "Use 'holdUniqDynBy' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
677−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
678−uniqDynBy eq d =
679− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
680− in unsafeDynamic (current d) e'
src/Reflex/Dynamic/TH.hs+1−25
@@ -13,9 +13,6 @@ module Reflex.Dynamic.TH
1313 ( qDynPure
1414 , unqDyn
1515 , mkDynPure
16− -- * Deprecated functions
17− , qDyn
18− , mkDyn
1916 ) where
2017
2118import Reflex.Dynamic
@@ -23,7 +20,7 @@ import Reflex.Dynamic
2320import Control.Monad.State
2421import Data.Data
2522import Data.Generics
26−import Data.Monoid
23+import Data.Monoid ((<>))
2724import qualified Language.Haskell.Exts as Hs
2825import qualified Language.Haskell.Meta.Syntax.Translate as Hs
2926import Language.Haskell.TH
@@ -107,24 +104,3 @@ mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ H
107104 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
108105 | modName == modName' && occName == occName' = 'unqMarker
109106 reinstateUnqDyn x = x
110−
111−--------------------------------------------------------------------------------
112−-- Deprecated
113−--------------------------------------------------------------------------------
114−
115−{-# DEPRECATED qDyn "Instead of $(qDyn x), use return $(qDynPure x)" #-}
116−-- | Like 'qDynPure', but wraps its result monadically using 'return'. This is
117−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
118−qDyn :: Q Exp -> Q Exp
119−qDyn qe = [| return $(qDynPure qe) |]
120−
121−{-# DEPRECATED mkDyn "Instead of [mkDyn| x |], use return [mkDynPure| x |]" #-}
122−-- | Like 'mkDynPure', but wraps its result monadically using 'return'. This is
123−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
124−mkDyn :: QuasiQuoter
125−mkDyn = QuasiQuoter
126− { quoteExp = \s -> [| return $(mkDynExp s) |]
127− , quotePat = error "mkDyn: pattern splices are not supported"
128− , quoteType = error "mkDyn: type splices are not supported"
129− , quoteDec = error "mkDyn: declaration splices are not supported"
130− }
src/Reflex/DynamicWriter/Base.hs+8−3
@@ -22,6 +22,7 @@ module Reflex.DynamicWriter.Base
2222import Control.Monad.Exception
2323import Control.Monad.Identity
2424import Control.Monad.IO.Class
25+import Control.Monad.Primitive
2526import Control.Monad.Reader
2627import Control.Monad.Ref
2728import Control.Monad.State.Strict
@@ -34,7 +35,7 @@ import Data.IntMap (IntMap)
3435import qualified Data.IntMap as IntMap
3536import Data.Map (Map)
3637import qualified Data.Map as Map
37−import Data.Semigroup
38+import Data.Semigroup (Semigroup(..))
3839import Data.Some (Some)
3940import Data.These
4041
@@ -87,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
8788 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
8889 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
8990
90−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
9192newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
9293 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
9394
@@ -115,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
115116 (result, ws) <- runStateT a []
116117 return (result, mconcat $ reverse ws)
117118
118−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
119+instance (Monad m, Monoid w, Reflex t) => DynamicWriter t w (DynamicWriterT t w m) where
119120 tellDyn w = DynamicWriterT $ modify (w :)
120121
121122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
@@ -140,6 +141,10 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where
140141 get = lift get
141142 put = lift . put
142143
144+instance PrimMonad m => PrimMonad (DynamicWriterT t w m) where
145+ type PrimState (DynamicWriterT t w m) = PrimState m
146+ primitive = lift . primitive
147+
143148newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
144149
145150-- | When the execution of a 'DynamicWriterT' action is adjusted using
src/Reflex/DynamicWriter/Class.hs+9−4
@@ -1,4 +1,5 @@
1−-- | This module defines the 'MonadDynamicWriter' class.
1+-- | This module defines the 'DynamicWriter' class.
2+{-# LANGUAGE ConstraintKinds #-}
23{-# LANGUAGE CPP #-}
34{-# LANGUAGE FlexibleInstances #-}
45{-# LANGUAGE FunctionalDependencies #-}
@@ -8,17 +9,21 @@
89{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
910#endif
1011module Reflex.DynamicWriter.Class
11− ( MonadDynamicWriter (..)
12+ ( MonadDynamicWriter
13+ , DynamicWriter(..)
1214 ) where
1315
1416import Control.Monad.Reader (ReaderT, lift)
1517import Reflex.Class (Dynamic)
1618
19+{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}
20+type MonadDynamicWriter = DynamicWriter
21+
1722-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
1823-- and combines them monoidally to provide a 'Dynamic' result.
19−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
24+class (Monad m, Monoid w) => DynamicWriter t w m | m -> t w where
2025 tellDyn :: Dynamic t w -> m ()
2126
22−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
27+instance DynamicWriter t w m => DynamicWriter t w (ReaderT r m) where
2328 tellDyn = lift . tellDyn
2429
src/Reflex/EventWriter/Base.hs+2−2
@@ -25,7 +25,7 @@ module Reflex.EventWriter.Base
2525import Reflex.Adjustable.Class
2626import Reflex.Class
2727import Reflex.EventWriter.Class (EventWriter, tellEvent)
28−import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
2929import Reflex.Host.Class
3030import Reflex.PerformEvent.Class
3131import Reflex.PostBuild.Class
@@ -278,7 +278,7 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
278278 askQueryResult = lift askQueryResult
279279 queryIncremental = lift . queryIncremental
280280
281−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (EventWriterT t v m) where
281+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where
282282 tellDyn = lift . tellDyn
283283
284284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
src/Reflex/FastWeak.hs+10−6
@@ -6,14 +6,18 @@
66{-# LANGUAGE JavaScriptFFI #-}
77#endif
88
9−-- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
10−-- referred to by a 'FastWeak' stays live while the ticket is held (live).
9+-- |
10+-- Module:
11+-- Reflex.FastWeak
12+-- Description:
13+-- 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
14+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
1115--
12−-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
13−-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
16+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
17+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
1418--
15−-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
16−-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
19+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
20+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
1721module Reflex.FastWeak
1822 ( FastWeakTicket
1923 , FastWeak
src/Reflex/FunctorMaybe.hs+23−21
@@ -1,43 +1,45 @@
1−-- | This module defines the FunctorMaybe class, which extends Functors with the
2−-- ability to delete values.
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE StandaloneDeriving #-}
4+
5+-- |
6+-- Module:
7+-- Reflex.FunctorMaybe
8+-- Description:
9+-- This module defines the FunctorMaybe class, which extends Functors with the
10+-- ability to delete values.
311module Reflex.FunctorMaybe
412 ( FunctorMaybe (..)
513 ) where
614
715import Data.IntMap (IntMap)
8−import qualified Data.IntMap as IntMap
916import Data.Map (Map)
10−import qualified Data.Map as Map
11−import Data.Maybe
17+#if MIN_VERSION_base(4,9,0)
18+import Data.Semigroup (Option(..))
19+#endif
20+import Data.Witherable
1221
1322--TODO: See if there's a better class in the standard libraries already
1423
1524-- | A class for values that combines filtering and mapping using 'Maybe'.
16−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
17−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
18−-- and e.g. 'Event' is instance of neither.
19−--
20−-- A definition of 'fmapMaybe' must satisfy the following laws:
21−--
22−-- [/identity/]
23−-- @'fmapMaybe' 'Just' ≡ 'id'@
24−--
25−-- [/composition/]
26−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
25+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.
26+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-}
2727class FunctorMaybe f where
2828 -- | Combined mapping and filtering function.
2929 fmapMaybe :: (a -> Maybe b) -> f a -> f b
3030
31−-- | @fmapMaybe = (=<<)
3231instance FunctorMaybe Maybe where
33− fmapMaybe = (=<<)
32+ fmapMaybe = mapMaybe
33+
34+#if MIN_VERSION_base(4,9,0)
35+deriving instance FunctorMaybe Option
36+#endif
3437
35−-- | @fmapMaybe = mapMaybe@
3638instance FunctorMaybe [] where
3739 fmapMaybe = mapMaybe
3840
3941instance FunctorMaybe (Map k) where
40− fmapMaybe = Map.mapMaybe
42+ fmapMaybe = mapMaybe
4143
4244instance FunctorMaybe IntMap where
43− fmapMaybe = IntMap.mapMaybe
45+ fmapMaybe = mapMaybe
src/Reflex/Network.hs+10−4
@@ -3,6 +3,11 @@
33#ifdef USE_REFLEX_OPTIMIZER
44{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
55#endif
6+-- |
7+-- Module:
8+-- Reflex.Network
9+-- Description:
10+-- This module provides combinators for building FRP graphs/networks and modifying them dynamically.
611module Reflex.Network
712 ( networkView
813 , networkHold
@@ -14,16 +19,17 @@ import Reflex.Adjustable.Class
1419import Reflex.NotReady.Class
1520import Reflex.PostBuild.Class
1621
17−-- | Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
18−-- The returned Event of network results occurs when the Dynamic does.
19−-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events that would typically be flattened (via 'switchPromptly').
22+-- | A 'Dynamic' "network": Takes a 'Dynamic' of network-creating actions and replaces the network whenever the 'Dynamic' updates.
23+-- The returned Event of network results fires when the 'Dynamic' updates.
24+-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events, where the outer 'Event' fires
25+-- when switching networks. Such an 'Event' would typically be flattened (via 'switchPromptly').
2026networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
2127networkView child = do
2228 postBuild <- getPostBuild
2329 let newChild = leftmost [updated child, tagCheap (current child) postBuild]
2430 snd <$> runWithReplace notReady newChild
2531
26−-- | Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the Event fires.
32+-- | Given an initial "network" and an 'Event' of network-creating actions, create a network that is recreated whenever the Event fires.
2733-- The returned Dynamic of network results occurs when the Event does.
2834-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
2935networkHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
src/Reflex/NotReady/Class.hs+10
@@ -15,7 +15,9 @@ module Reflex.NotReady.Class
1515
1616import Control.Monad.Reader (ReaderT)
1717import Control.Monad.Trans
18+import Control.Monad.Trans.Writer (WriterT)
1819
20+import Reflex.BehaviorWriter.Base (BehaviorWriterT)
1921import Reflex.Class
2022import Reflex.DynamicWriter.Base (DynamicWriterT)
2123import Reflex.EventWriter.Base (EventWriterT)
@@ -39,6 +41,10 @@ instance NotReady t m => NotReady t (ReaderT r m) where
3941 notReadyUntil = lift . notReadyUntil
4042 notReady = lift notReady
4143
44+instance (NotReady t m, Monoid w) => NotReady t (WriterT w m) where
45+ notReadyUntil = lift . notReadyUntil
46+ notReady = lift notReady
47+
4248instance NotReady t m => NotReady t (PostBuildT t m) where
4349 notReadyUntil = lift . notReadyUntil
4450 notReady = lift notReady
@@ -51,6 +57,10 @@ instance NotReady t m => NotReady t (DynamicWriterT t w m) where
5157 notReadyUntil = lift . notReadyUntil
5258 notReady = lift notReady
5359
60+instance NotReady t m => NotReady t (BehaviorWriterT t w m) where
61+ notReadyUntil = lift . notReadyUntil
62+ notReady = lift notReady
63+
5464instance NotReady t m => NotReady t (QueryT t q m) where
5565 notReadyUntil = lift . notReadyUntil
5666 notReady = lift notReady
src/Reflex/Optimizer.hs+8−4
@@ -1,10 +1,14 @@
1−-- | This module provides a GHC plugin designed to improve code that uses
2−-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
3−-- definition that doesn't have an explicit inlining pragma. In the future,
4−-- additional optimizations are likely to be added.
51{-# LANGUAGE CPP #-}
62{-# LANGUAGE LambdaCase #-}
73{-# LANGUAGE OverloadedStrings #-}
4+-- |
5+-- Module:
6+-- Reflex.Optimizer
7+-- Description:
8+-- This module provides a GHC plugin designed to improve code that uses
9+-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
10+-- definition that doesn't have an explicit inlining pragma. In the future,
11+-- additional optimizations are likely to be added.
812module Reflex.Optimizer
913 ( plugin
1014 ) where
src/Reflex/Patch.hs+12−5
@@ -1,6 +1,10 @@
1−-- | This module defines the 'Patch' class, which is used by Reflex to manage
2−-- changes to 'Reflex.Class.Incremental' values.
31{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Reflex.Patch
5+-- Description:
6+-- This module defines the 'Patch' class, which is used by Reflex to manage
7+-- changes to 'Reflex.Class.Incremental' values.
48module Reflex.Patch
59 ( module Reflex.Patch
610 , module X
@@ -17,11 +21,9 @@ import Reflex.Patch.Map as X
1721import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
1822 patchMapWithMoveNewElementsMap, unPatchMapWithMove,
1923 unsafePatchMapWithMove)
20−
24+import Data.Map.Monoidal (MonoidalMap)
2125import Data.Semigroup (Semigroup (..), (<>))
2226
23−---- Patches based on commutative groups
24−
2527-- | A 'Group' is a 'Monoid' where every element has an inverse.
2628class (Semigroup q, Monoid q) => Group q where
2729 negateG :: q -> q
@@ -37,3 +39,8 @@ newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
3739instance Additive p => Patch (AdditivePatch p) where
3840 type PatchTarget (AdditivePatch p) = p
3941 apply (AdditivePatch p) q = Just $ p <> q
42+
43+instance (Ord k, Group q) => Group (MonoidalMap k q) where
44+ negateG = fmap negateG
45+
46+instance (Ord k, Additive q) => Additive (MonoidalMap k q)
src/Reflex/Patch/Class.hs+1−1
@@ -4,7 +4,7 @@ module Reflex.Patch.Class where
44
55import Control.Monad.Identity
66import Data.Maybe
7−import Data.Semigroup
7+import Data.Semigroup (Semigroup(..))
88
99-- | A 'Patch' type represents a kind of change made to a datastructure.
1010--
src/Reflex/Patch/MapWithMove.hs+1−1
@@ -22,7 +22,7 @@ import qualified Data.Map as Map
2222import Data.Maybe
2323import Data.Semigroup (Semigroup (..), (<>))
2424import qualified Data.Set as Set
25−import Data.These
25+import Data.These (These(..))
2626import Data.Tuple
2727
2828-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
src/Reflex/Profiled.hs+6
@@ -8,6 +8,12 @@
88{-# LANGUAGE StandaloneDeriving #-}
99{-# LANGUAGE TypeFamilies #-}
1010{-# LANGUAGE UndecidableInstances #-}
11+-- |
12+-- Module:
13+-- Reflex.Profiled
14+-- Description:
15+-- This module contains an instance of the 'Reflex' class that provides
16+-- profiling/cost-center information.
1117module Reflex.Profiled where
1218
1319import Control.Lens hiding (children)
src/Reflex/Pure.hs+7−5
@@ -13,11 +13,13 @@
1313-- * MonadSample (Pure t) ((->) t)
1414-- * MonadHold (Pure t) ((->) t)
1515{-# OPTIONS_GHC -fno-warn-orphans #-}
16−
17−-- | This module provides a pure implementation of Reflex, which is intended to
18−-- serve as a reference for the semantics of the Reflex class. All
19−-- implementations of Reflex should produce the same results as this
20−-- implementation, although performance and laziness/strictness may differ.
16+-- |
17+-- Module: Reflex.Pure
18+-- Description:
19+-- This module provides a pure implementation of Reflex, which is intended to
20+-- serve as a reference for the semantics of the Reflex class. All
21+-- implementations of Reflex should produce the same results as this
22+-- implementation, although performance and laziness/strictness may differ.
2123module Reflex.Pure
2224 ( Pure
2325 , Behavior (..)
src/Reflex/Query/Base.hs+23−30
@@ -74,26 +74,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
7474getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
7575getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
7676
77−{-
78−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
79− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
80− bs' = fmapCheap snd $ r'
81− patches = unsafeBuildIncremental (sampleBs bs0) $
82− flip pushCheap bs' $ \bs -> do
83− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
84− return (Just (AdditivePatch p))
85−-}
77+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
78+maskMempty x = if x == mempty then Nothing else Just x
8679
87−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
80+instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
8881 runWithReplace (QueryT a0) a' = do
8982 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
9083 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
9184 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
9285 bs' = fmapCheap snd $ r'
9386 bbs <- hold bs0 bs'
94− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
87+ let patches = flip pushCheap bs' $ \newBs -> do
9588 oldBs <- sample bbs
96− (~~) <$> sampleBs newBs <*> sampleBs oldBs
89+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
9790 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
9891 QueryT $ lift $ tellEvent patches
9992 return (r0, fmapCheap fst r')
@@ -171,19 +164,19 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
171164 let p k bs = case Map.lookup k bs0 of
172165 Nothing -> case bs of
173166 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
174− Nothing -> return mempty
167+ Nothing -> return Nothing
175168 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
176− Just newBs -> sampleBs newBs
169+ Just newBs -> maskMempty <$> sampleBs newBs
177170 Just oldBs -> case bs of
178171 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
179− Nothing -> negateG <$> sampleBs oldBs
172+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
180173 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
181174 -- composed with the sampling the child's new state.
182− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
175+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
183176 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
184177 -- child patches and wrap them in AdditivePatch.
185− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
186− return (apply pbs bs0, Just patch)
178+ patch <- fold <$> Map.traverseWithKey p bs'
179+ return (apply pbs bs0, AdditivePatch <$> patch)
187180 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
188181 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
189182 return (liftedResult0, liftedResult')
@@ -216,28 +209,28 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
216209 p k bs = case Map.lookup k bs0 of
217210 Nothing -> case MapWithMove._nodeInfo_from bs of
218211 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
219− MapWithMove.From_Delete -> return mempty
212+ MapWithMove.From_Delete -> return Nothing
220213 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
221− MapWithMove.From_Insert newBs -> sampleBs newBs
214+ MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs
222215 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
223− Nothing -> return mempty
224− Just newBs -> sampleBs newBs
216+ Nothing -> return Nothing
217+ Just newBs -> maskMempty <$> sampleBs newBs
225218 Just oldBs -> case MapWithMove._nodeInfo_from bs of
226219 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
227− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
220+ MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs
228221 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
229222 -- composed with the sampling the child's new state.
230− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
223+ MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
231224 MapWithMove.From_Move k'
232− | k' == k -> return mempty
225+ | k' == k -> return Nothing
233226 | otherwise -> case Map.lookup k' bs0 of
234227 -- If we are moving from a non-existent key, that is a delete
235− Nothing -> negateG <$> sampleBs oldBs
236− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
228+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
229+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
237230 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
238231 -- child patches and wrap them in AdditivePatch.
239− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
240− return (apply pbs bs0, Just patch)
232+ patch <- fold <$> Map.traverseWithKey p bs'
233+ return (apply pbs bs0, AdditivePatch <$> patch)
241234 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
242235 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
243236 return (liftedResult0, liftedResult')
@@ -340,5 +333,5 @@ instance Requester t m => Requester t (QueryT t q m) where
340333instance EventWriter t w m => EventWriter t w (QueryT t q m) where
341334 tellEvent = lift . tellEvent
342335
343−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (QueryT t q m) where
336+instance DynamicWriter t w m => DynamicWriter t w (QueryT t q m) where
344337 tellDyn = lift . tellDyn
src/Reflex/Query/Class.hs+1−1
@@ -27,7 +27,7 @@ import Data.Data
2727import Data.Ix
2828import Data.Map.Monoidal (MonoidalMap)
2929import qualified Data.Map.Monoidal as MonoidalMap
30−import Data.Semigroup
30+import Data.Semigroup (Semigroup(..))
3131import Foreign.Storable
3232
3333import Reflex.Class
src/Reflex/Requester/Base.hs+107−3
@@ -21,6 +21,7 @@
2121module Reflex.Requester.Base
2222 ( RequesterT (..)
2323 , runRequesterT
24+ , withRequesterT
2425 , runWithReplaceRequesterTWith
2526 , traverseIntMapWithKeyWithAdjustRequesterTWith
2627 , traverseDMapWithKeyWithAdjustRequesterTWith
@@ -30,6 +31,10 @@ module Reflex.Requester.Base
3031 , forRequesterData
3132 , requesterDataToList
3233 , singletonRequesterData
34+ , matchResponsesWithRequests
35+ , multiEntry
36+ , unMultiEntry
37+ , requesting'
3338 ) where
3439
3540import Reflex.Class
@@ -44,6 +49,7 @@ import Reflex.TriggerEvent.Class
4449import Control.Applicative (liftA2)
4550import Control.Monad.Exception
4651import Control.Monad.Identity
52+import Control.Monad.Primitive
4753import Control.Monad.Reader
4854import Control.Monad.Ref
4955import Control.Monad.State.Strict
@@ -244,6 +250,10 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
244250deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
245251deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
246252
253+instance PrimMonad m => PrimMonad (RequesterT t request response m) where
254+ type PrimState (RequesterT t request response m) = PrimState m
255+ primitive = lift . primitive
256+
247257-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
248258instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
249259 mempty = pure mempty
@@ -257,7 +267,6 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response
257267-- requests are made, and responses should be provided in the input 'Event'.
258268-- The 'Tag' keys will be used to return the responses to the same place the
259269-- requests were issued.
260−
261270runRequesterT :: (Reflex t, Monad m)
262271 => RequesterT t request response m a
263272 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
@@ -267,6 +276,20 @@ runRequesterT (RequesterT a) responses = do
267276 coerceEvent responses
268277 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
269278
279+-- | Map a function over the request and response of a 'RequesterT'
280+withRequesterT
281+ :: (Reflex t, MonadFix m)
282+ => (forall x. req x -> req' x) -- ^ The function to map over the request
283+ -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response
284+ -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed
285+ -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT'
286+withRequesterT freq frsp child = do
287+ rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp'
288+ (a, req) <- lift $ runRequesterT child rsp
289+ rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $
290+ fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req
291+ return a
292+
270293instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
271294 type Request (RequesterT t request response m) = request
272295 type Response (RequesterT t request response m) = response
@@ -370,7 +393,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
370393 pack = Entry
371394 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
372395 f' k (n, v) = do
373− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
396+ (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?
374397 return (fmapCheap (IntMap.singleton n) myRequests, result)
375398 ndm' <- numberOccurrencesFrom 1 dm'
376399 (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
@@ -418,7 +441,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
418441 pack = Entry
419442 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
420443 f' k (Compose (n, v)) = do
421− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
444+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
422445 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
423446 ndm' <- numberOccurrencesFrom 1 dm'
424447 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
@@ -432,3 +455,84 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
432455 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
433456 requests <- holdIncremental requests0 requests'
434457 return (result0, result')
458+
459+data Decoder rawResponse response =
460+ forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
461+
462+-- | Matches incoming responses with previously-sent requests
463+-- and uses the provided request "decoder" function to process
464+-- incoming responses.
465+matchResponsesWithRequests
466+ :: forall t rawRequest rawResponse request response m.
467+ ( MonadFix m
468+ , MonadHold t m
469+ , Reflex t
470+ )
471+ => (forall a. request a -> (rawRequest, rawResponse -> response a))
472+ -- ^ Given a request (from 'Requester'), produces the wire format of the
473+ -- request and a function used to process the associated response
474+ -> Event t (RequesterData request)
475+ -- ^ The outgoing requests
476+ -> Event t (Int, rawResponse)
477+ -- ^ The incoming responses, tagged by an identifying key
478+ -> m ( Event t (Map Int rawRequest)
479+ , Event t (RequesterData response)
480+ )
481+ -- ^ A map of outgoing wire-format requests and an event of responses keyed
482+ -- by the 'RequesterData' key of the associated outgoing request
483+matchResponsesWithRequests f send recv = do
484+ rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
485+ waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
486+ holdIncremental mempty $ leftmost
487+ [ fmap (\(_, outstanding, _) -> outstanding) outgoing
488+ , snd <$> incoming
489+ ]
490+ let outgoing = processOutgoing nextId send
491+ incoming = processIncoming waitingFor recv
492+ return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming)
493+ where
494+ -- Tags each outgoing request with an identifying integer key
495+ -- and returns the next available key, a map of response decoders
496+ -- for requests for which there are outstanding responses, and the
497+ -- raw requests to be sent out.
498+ processOutgoing
499+ :: Behavior t Int
500+ -- The next available key
501+ -> Event t (RequesterData request)
502+ -- The outgoing request
503+ -> Event t ( Int
504+ , PatchMap Int (Decoder rawResponse response)
505+ , Map Int rawRequest )
506+ -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests
507+ processOutgoing nextId out = flip pushAlways out $ \dm -> do
508+ oldNextId <- sample nextId
509+ let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do
510+ n <- get
511+ put $ succ n
512+ let (rawReq, rspF) = f v
513+ return (n, rawReq, Decoder k rspF)
514+ patchWaitingFor = PatchMap $ Map.fromList $
515+ (\(n, _, dec) -> (n, Just dec)) <$> result
516+ toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result
517+ return (newNextId, patchWaitingFor, toSend)
518+ -- Looks up the each incoming raw response in a map of response
519+ -- decoders and returns the decoded response and a patch that can
520+ -- be used to clear the ID of the consumed response out of the queue
521+ -- of expected responses.
522+ processIncoming
523+ :: Incremental t (PatchMap Int (Decoder rawResponse response))
524+ -- A map of outstanding expected responses
525+ -> Event t (Int, rawResponse)
526+ -- A incoming response paired with its identifying key
527+ -> Event t (RequesterData response, PatchMap Int v)
528+ -- The decoded response and a patch that clears the outstanding responses queue
529+ processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do
530+ wf <- sample $ currentIncremental waitingFor
531+ case Map.lookup n wf of
532+ Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
533+ Just (Decoder k rspF) -> do
534+ let rsp = rspF rawRsp
535+ return $ Just
536+ ( singletonRequesterData k rsp
537+ , PatchMap $ Map.singleton n Nothing
538+ )
src/Reflex/Spider.hs+5−2
@@ -1,6 +1,9 @@
11{-# LANGUAGE CPP #-}
2−-- | This module exports all of the user-facing functionality of the 'Spider'
3−-- 'Reflex' engine
2+-- |
3+-- Module:
4+-- Reflex.Spider
5+-- Description:
6+-- This module exports all of the user-facing functionality of the 'Spider' 'Reflex' engine
47module Reflex.Spider
58 ( Spider
69 , SpiderTimeline
src/Reflex/Spider/Internal.hs+5−5
@@ -49,16 +49,16 @@ import Data.GADT.Compare
4949import Data.IntMap.Strict (IntMap)
5050import qualified Data.IntMap.Strict as IntMap
5151import Data.IORef
52−import Data.Maybe
52+import Data.Maybe hiding (mapMaybe)
5353import Data.Monoid ((<>))
5454import Data.Proxy
5555import Data.These
5656import Data.Traversable
57+import Data.Witherable (Filterable, mapMaybe)
5758import GHC.Exts
5859import GHC.IORef (IORef (..))
5960import GHC.Stack
6061import Reflex.FastWeak
61−import Reflex.FunctorMaybe
6262import System.IO.Unsafe
6363import System.Mem.Weak
6464import Unsafe.Coerce
@@ -1128,12 +1128,12 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
11281128newInvalidatorPull :: Pull x a -> IO (Invalidator x)
11291129newInvalidatorPull p = return $! InvalidatorPull p
11301130
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1132− fmapMaybe f = push $ return . f
1131+instance HasSpiderTimeline x => Filterable (Event x) where
1132+ mapMaybe f = push $ return . f
11331133
11341134instance HasSpiderTimeline x => Align (Event x) where
11351135 nil = eventNever
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1136+ align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
11371137
11381138data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11391139 | BuildDyn !(EventM x (PatchTarget p), Event x p)
src/Reflex/Time.hs+113−13
@@ -1,5 +1,7 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveGeneric #-}
4+{-# LANGUAGE DeriveTraversable #-}
35{-# LANGUAGE FlexibleContexts #-}
46{-# LANGUAGE MultiParamTypeClasses #-}
57{-# LANGUAGE RecursiveDo #-}
@@ -9,6 +11,11 @@
911#ifdef USE_TEMPLATE_HASKELL
1012{-# LANGUAGE TemplateHaskell #-}
1113#endif
14+-- |
15+-- Module:
16+-- Reflex.Time
17+-- Description:
18+-- Clocks, timers, and other time-related functions.
1219module Reflex.Time where
1320
1421import Reflex.Class
@@ -24,46 +31,50 @@ import Control.Monad
2431import Control.Monad.Fix
2532import Control.Monad.IO.Class
2633import Data.Align
34+import Data.Data (Data)
2735import Data.Fixed
36+import Data.Semigroup (Semigroup(..))
2837import Data.Sequence (Seq, (|>))
2938import qualified Data.Sequence as Seq
3039import Data.These
3140import Data.Time.Clock
3241import Data.Typeable
42+import GHC.Generics (Generic)
3343import System.Random
3444
45+-- | Metadata associated with a timer "tick"
3546data TickInfo
3647 = TickInfo { _tickInfo_lastUTC :: UTCTime
3748 -- ^ UTC time immediately after the last tick.
3849 , _tickInfo_n :: Integer
39− -- ^ Number of time periods since t0
50+ -- ^ Number of time periods or ticks since the start of the timer
4051 , _tickInfo_alreadyElapsed :: NominalDiffTime
41− -- ^ Amount of time already elapsed in the current tick period.
52+ -- ^ Amount of time that has elapsed in the current tick period.
4253 }
4354 deriving (Eq, Ord, Show, Typeable)
4455
45−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
46−-- tick thread.
56+-- | Fires an 'Event' once every time provided interval elapses, approximately.
57+-- The provided 'UTCTime' is used bootstrap the determination of how much time has elapsed with each tick.
58+-- This is a special case of 'tickLossyFrom' that uses the post-build event to start the tick thread.
4759tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
4860tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
4961
50−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
51−-- tick thread and the time of the post-build as the tick basis time.
62+-- | Fires an 'Event' once every time provided interval elapses, approximately.
63+-- This is a special case of 'tickLossyFrom' that uses the post-build event to start the tick thread and the time of the post-build as the tick basis time.
5264tickLossyFromPostBuildTime :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> m (Event t TickInfo)
5365tickLossyFromPostBuildTime dt = do
5466 postBuild <- getPostBuild
5567 postBuildTime <- performEvent $ liftIO getCurrentTime <$ postBuild
5668 tickLossyFrom' $ (dt,) <$> postBuildTime
5769
58−-- | Send events over time with the given basis time and interval
59−-- If the system starts running behind, occurrences will be dropped rather than buffered
60−-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time
70+-- | Fires an 'Event' approximately each time the provided interval elapses. If the system starts running behind, occurrences will be dropped rather than buffered.
71+-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the provided initial time.
6172tickLossyFrom
6273 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
6374 => NominalDiffTime
6475 -- ^ The length of a tick interval
6576 -> UTCTime
66− -- ^ The basis time from which intervals count
77+ -- ^ The basis time from which intervals count and with which the initial calculation of elapsed time will be made.
6778 -> Event t a
6879 -- ^ Event that starts a tick generation thread. Usually you want this to
6980 -- be something like the result of getPostBuild that only fires once. But
@@ -71,12 +82,12 @@ tickLossyFrom
7182 -> m (Event t TickInfo)
7283tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e
7384
74−-- | Generalization of tickLossyFrom that takes dt and t0 in the event.
85+-- | Generalization of tickLossyFrom that takes the delay and initial time as an 'Event'.
7586tickLossyFrom'
7687 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
7788 => Event t (NominalDiffTime, UTCTime)
7889 -- ^ Event that starts a tick generation thread. Usually you want this to
79− -- be something like the result of getPostBuild that only fires once. But
90+ -- be something like the result of 'getPostBuild' that only fires once. But
8091 -- there could be uses for starting multiple timer threads.
8192 -> m (Event t TickInfo)
8293tickLossyFrom' e = do
@@ -87,12 +98,16 @@ tickLossyFrom' e = do
8798 Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000
8899 cb (tick, pair)
89100
101+-- | Like 'tickLossy', but immediately calculates the first tick and provides a 'Dynamic' that is updated as ticks fire.
90102clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m, MonadFix m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
91103clockLossy dt t0 = do
92104 initial <- liftIO $ getCurrentTick dt t0
93105 e <- tickLossy dt t0
94106 holdDyn initial e
95107
108+-- | Generates a 'TickInfo', given the specified interval and timestamp. The 'TickInfo' will include the
109+-- current time, the number of ticks that have elapsed since the timestamp, and the amount of time that
110+-- has elapsed since the start time of this tick.
96111getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo
97112getCurrentTick dt t0 = do
98113 t <- getCurrentTime
@@ -266,6 +281,92 @@ throttle t e = do
266281 delayed <- delay t outE
267282 return outE
268283
284+data ThrottleState b
285+ = ThrottleState_Immediate
286+ | ThrottleState_Buffered (ThrottleBuffer b)
287+ deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic, Data, Typeable)
288+
289+data ThrottleBuffer b
290+ = ThrottleBuffer_Empty -- Empty conflicts with lens, and hiding it would require turning
291+ -- on PatternSynonyms
292+ | ThrottleBuffer_Full b
293+ deriving (Eq, Ord, Show, Functor, Foldable, Traversable, Generic, Data, Typeable)
294+
295+instance Semigroup b => Semigroup (ThrottleBuffer b) where
296+ x <> y = case x of
297+ ThrottleBuffer_Empty -> y
298+ ThrottleBuffer_Full b1 -> case y of
299+ ThrottleBuffer_Empty -> x
300+ ThrottleBuffer_Full b2 -> ThrottleBuffer_Full $ b1 <> b2
301+ {-# INLINE (<>) #-}
302+
303+instance Semigroup b => Monoid (ThrottleBuffer b) where
304+ mempty = ThrottleBuffer_Empty
305+ {-# INLINE mempty #-}
306+ mappend = (<>)
307+ {-# INLINE mappend #-}
308+
309+-- | Throttle an input event, ensuring that the output event doesn't occur more often than you are ready for it. If the input event occurs too
310+-- frequently, the output event will contain semigroup-based summaries of the input firings that happened since the last output firing.
311+-- If the output event has not occurred recently, occurrences of the input event will cause the output event to fire immediately.
312+-- The first parameter is a function that receives access to the output event, and should construct an event that fires when the receiver is
313+-- ready for more input. For example, using @delay 20@ would give a simple time-based throttle.
314+--
315+-- NB: The provided lag function must *actually* delay the event.
316+throttleBatchWithLag :: (MonadFix m, MonadHold t m, PerformEvent t m, Semigroup a) => (Event t () -> m (Event t ())) -> Event t a -> m (Event t a)
317+-- Invariants:
318+-- * Immediate mode must turn off whenever output is produced.
319+-- * Output must be produced whenever immediate mode turns from on to off.
320+-- * Immediate mode can only go from off to on when the delayed event fires.
321+-- * Every input firing must go into either an immediate output firing or the
322+-- buffer, but not both.
323+-- * An existing full buffer must either stay in the buffer or go to output,
324+-- but not both.
325+throttleBatchWithLag lag e = do
326+ let f state x = case x of -- (Just $ newState, out)
327+ This a -> -- If only the input event fires
328+ case state of
329+ ThrottleState_Immediate -> -- and we're in immediate mode
330+ -- Immediate mode turns off, and the buffer is empty.
331+ -- We fire the output event with the input event value immediately.
332+ (Just $ ThrottleState_Buffered $ ThrottleBuffer_Empty, Just a)
333+ ThrottleState_Buffered b -> -- and we're not in immediate mode
334+ -- Immediate mode remains off, and we accumulate the input value.
335+ -- We don't fire the output event.
336+ (Just $ ThrottleState_Buffered $ b <> ThrottleBuffer_Full a, Nothing)
337+ That _ -> -- If only the delayed output event fires,
338+ case state of
339+ ThrottleState_Immediate -> -- and we're in immediate mode
340+ -- Nothing happens.
341+ (Nothing, Nothing)
342+ ThrottleState_Buffered ThrottleBuffer_Empty -> -- and the buffer is empty:
343+ -- Immediate mode turns back on, and the buffer remains empty.
344+ -- We don't fire.
345+ (Just ThrottleState_Immediate, Nothing)
346+ ThrottleState_Buffered (ThrottleBuffer_Full b) -> -- and the buffer is full:
347+ -- Immediate mode remains off, and the buffer is cleared.
348+ -- We fire with the buffered value.
349+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just b)
350+ These a _ -> -- If both the input and delayed output event fire simultaneously:
351+ case state of
352+ ThrottleState_Immediate -> -- and we're in immediate mode
353+ -- Immediate mode turns off, and the buffer is empty.
354+ -- We fire with the input event's value, as it is the most recent we have seen at this moment.
355+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just a)
356+ ThrottleState_Buffered ThrottleBuffer_Empty -> -- and the buffer is empty:
357+ -- Immediate mode stays off, and the buffer remains empty.
358+ -- We fire with the input event's value.
359+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just a)
360+ ThrottleState_Buffered (ThrottleBuffer_Full b) -> -- and the buffer is full:
361+ -- Immediate mode remains off, and the buffer is cleared.
362+ -- We fire with everything including the buffered value.
363+ (Just $ ThrottleState_Buffered ThrottleBuffer_Empty, Just (b <> a))
364+ rec (_stateDyn, outE) <- mapAccumMaybeDyn f
365+ ThrottleState_Immediate -- We start in immediate mode with an empty buffer.
366+ (align e delayed)
367+ delayed <- lag (void outE)
368+ return outE
369+
269370#ifdef USE_TEMPLATE_HASKELL
270371makeLensesWith (lensRules & simpleLenses .~ True) ''TickInfo
271372#else
@@ -281,4 +382,3 @@ tickInfo_alreadyElapsed :: Lens' TickInfo NominalDiffTime
281382tickInfo_alreadyElapsed f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 x2 y) <$> f x3
282383{-# INLINE tickInfo_alreadyElapsed #-}
283384#endif
284−
src/Reflex/Workflow.hs+13−1
@@ -1,7 +1,13 @@
11{-# LANGUAGE RecursiveDo #-}
22{-# LANGUAGE ScopedTypeVariables #-}
3+-- |
4+-- Module:
5+-- Reflex.Workflow
6+-- Description:
7+-- Provides a convenient way to describe a series of interrelated widgets that
8+-- can send data to, invoke, and replace one another. Useful for modeling user interface
9+-- "workflows."
310module Reflex.Workflow (
4− -- * Workflows
511 Workflow (..)
612 , workflow
713 , workflowView
@@ -18,21 +24,27 @@ import Reflex.Network
1824import Reflex.NotReady.Class
1925import Reflex.PostBuild.Class
2026
27+-- | A widget in a workflow
28+-- When the 'Event' returned by a 'Workflow' fires, the current 'Workflow' is replaced by the one inside the firing 'Event'. A series of 'Workflow's must share the same return type.
2129newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
2230
31+-- | Runs a 'Workflow' and returns the 'Dynamic' result of the 'Workflow' (i.e., a 'Dynamic' of the value produced by the current 'Workflow' node, and whose update 'Event' fires whenever one 'Workflow' is replaced by another).
2332workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
2433workflow w0 = do
2534 rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
2635 return $ fmap fst eResult
2736
37+-- | Similar to 'workflow', but outputs an 'Event' that fires whenever the current 'Workflow' is replaced by the next 'Workflow'.
2838workflowView :: forall t m a. (Reflex t, NotReady t m, Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
2939workflowView w0 = do
3040 rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
3141 eReplace <- fmap switch $ hold never $ fmap snd eResult
3242 return $ fmap fst eResult
3343
44+-- | Map a function over a 'Workflow', possibly changing the resturn type.
3445mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
3546mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
3647
48+-- | Map a "cheap" function over a 'Workflow'. Refer to the documentation for 'pushCheap' for more information and performance considerations.
3749mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
3850mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
test/RequesterT.hs+11−9
@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
12{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE GADTs #-}
34{-# LANGUAGE RankNTypes #-}
@@ -20,18 +21,19 @@ data RequestInt a where
2021
2122main :: IO ()
2223main = do
23− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
24+ os1 <- runApp' (unwrapApp testOrdering) $
2425 [ Just ()
2526 ]
2627 print os1
27− os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
28− <- runApp' (unwrapApp testSimultaneous) $ map Just $
29− [ This ()
30− , That ()
31− , This ()
32− , These () ()
33− ]
28+ let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
29+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
30+ [ This ()
31+ , That ()
32+ , This ()
33+ , These () ()
34+ ]
3435 print os2
36+ let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2
3537 return ()
3638
3739unwrapRequest :: DSum tag RequestInt -> Int
@@ -43,7 +45,7 @@ unwrapApp :: ( Reflex t, Monad m )
4345 -> m (Event t [Int])
4446unwrapApp x appIn = do
4547 ((), e) <- runRequesterT (x appIn) never
46− return $ fmap (map unwrapRequest . DMap.toList) e
48+ return $ fmap (map unwrapRequest . requesterDataToList) e
4749
4850testOrdering :: ( Response m ~ Identity
4951 , Request m ~ RequestInt