on develop help sign in
Merge remote-tracking branch 'origin/develop' into patchmapwithmove2
49e0bb8Ryan Trinkle
CONTRIBUTING.mdnew file+57
@@ -0,0 +1,57 @@
1+# Contribution Guide
2+
3+Contributions and issue reports are encouraged and appreciated!
4+
5+- [Opening Issues](#opening-issues)
6+- [Submitting Changes](#submitting-changes)
7+ - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Code Quality](#code-quality)
9+ - [Documentation](#documentation)
10+
11+## Opening Issues
12+
13+Before opening an issue, please check whether your issue has already been reported. Assuming it has not:
14+
15+* Describe the issue you're encountering or the suggestion you're making
16+* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
17+* Let us know what version of reflex you were using. If you're using a github checkout, provide the git hash.
18+* Describe how you're building reflex (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
19+
20+## Submitting Changes
21+
22+### Guidelines for Commit Messages
23+
24+#### Summary Line
25+The summary line of your commit message should summarize the changes being made. Commit messages should be written in the imperative mood and should describe what happens when the commit is applied.
26+
27+One way to think about it is that your commit message should be able to complete the sentence:
28+"When applied, this commit will..."
29+
30+#### Body
31+For breaking changes, new features, refactors, or other major changes, the body of the commit message should describe the motivation behind the change in greater detail and may include references to the issue tracker. The body shouldn't repeat code/comments from the diff.
32+
33+### Code Quality
34+
35+#### Warnings
36+
37+Your pull request should add no new warnings to the project. It should also generally not disable any warnings.
38+
39+#### Build and Test
40+
41+Make sure the project builds and that the tests pass! This will generally also be checked by CI before merge, but trying it yourself first means you'll catch problems earlier and your contribution can be merged that much sooner!
42+
43+### Documentation
44+
45+#### In the code
46+We're always striving to improve documentation. Please include [haddock](https://haskell-haddock.readthedocs.io/en/latest/index.html) documentation for any added code, and update the documentation for any code you modify.
47+
48+#### In the [Changelog](ChangeLog.md)
49+Add an entry to the changelog when your PR:
50+* Adds a feature
51+* Deprecates something
52+* Includes a breaking change
53+* Makes any other change that will impact users
54+
55+#### In the [Readme](README.md)
56+The readme is the first place a lot of people look for information about the repository. Update any parts of the readme that are affected by your PR.
57+
ChangeLog.mdnew file+21
@@ -0,0 +1,21 @@
1+# Revision history for reflex
2+
3+## Unreleased
4+
5+* Fix `holdDyn` so that it is lazy in its event argument
6+
7+## 0.6.1.0
8+
9+* Re-export all of `Data.Map.Monoidal`
10+* Fix `QueryT` and `RequesterT` tests
11+
12+## 0.6.0.0 -- 2019-03-20
13+
14+* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
15+* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
16+* Remove many deprecated functions.
17+* Add a `Num` instance for `Dynamic`.
18+* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
19+* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
20+* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
21+* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
Quickref.md+1−1
@@ -166,7 +166,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
167[ ] switch :: Behavior (Event a) -> Event a
167[ ] switch :: Behavior (Event a) -> Event a
168
168
169−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
169+-- Flatten Dynamic-of-Event to Event. New Event is used immediately.
170[ ] switchDyn :: Dynamic (Event a) -> Event a
170[ ] switchDyn :: Dynamic (Event a) -> Event a
171
171
172-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
172-- 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/)
2### Practical Functional Reactive Programming
2### Practical Functional Reactive Programming
3
3
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.
5
5
6[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.
6[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.
7
7
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−
10A summary of Reflex functions is available in the [quick reference](Quickref.md).
8A summary of Reflex functions is available in the [quick reference](Quickref.md).
11
9
12−### Additional resources
10+**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
11+
12+### Resources
13[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
13[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
14
14
15[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
15[/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)
17[hackage](https://hackage.haskell.org/package/reflex)
17[hackage](https://hackage.haskell.org/package/reflex)
18
18
19[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
19[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+1−1
@@ -11,7 +11,7 @@
11}:
11}:
12mkDerivation {
12mkDerivation {
13 pname = "reflex";
13 pname = "reflex";
14− version = "0.5.0.1";
14+ version = "0.6.1";
15 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
15 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16 libraryHaskellDepends = [
16 libraryHaskellDepends = [
17 base bifunctors containers dependent-map dependent-sum
17 base bifunctors containers dependent-map dependent-sum
reflex.cabal+6−3
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.6
2+Version: 0.6.1
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5License: BSD3
5License: BSD3
@@ -15,6 +15,7 @@ bug-reports: https://github.com/reflex-frp/reflex/issues
15extra-source-files:
15extra-source-files:
16 README.md
16 README.md
17 Quickref.md
17 Quickref.md
18+ ChangeLog.md
18
19
19flag use-reflex-optimizer
20flag use-reflex-optimizer
20 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
21 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -60,7 +61,7 @@ library
60 semigroups >= 0.16 && < 0.19,
61 semigroups >= 0.16 && < 0.19,
61 stm >= 2.4 && < 2.6,
62 stm >= 2.4 && < 2.6,
62 syb >= 0.5 && < 0.8,
63 syb >= 0.5 && < 0.8,
63− these >= 0.4 && < 0.7.7,
64+ these >= 0.4 && < 0.9,
64 time >= 1.4 && < 1.9,
65 time >= 1.4 && < 1.9,
65 transformers >= 0.2,
66 transformers >= 0.2,
66 transformers-compat >= 0.3,
67 transformers-compat >= 0.3,
@@ -235,14 +236,16 @@ test-suite RequesterT
235 main-is: RequesterT.hs
236 main-is: RequesterT.hs
236 hs-source-dirs: test
237 hs-source-dirs: test
237 build-depends: base
238 build-depends: base
239+ , containers
240+ , deepseq >= 1.3 && < 1.5
238 , dependent-sum
241 , dependent-sum
239 , dependent-map
242 , dependent-map
240 , lens
243 , lens
244+ , mtl
241 , these
245 , these
242 , transformers
246 , transformers
243 , reflex
247 , reflex
244 , ref-tf
248 , ref-tf
245− buildable: False
246 other-modules:
249 other-modules:
247 Reflex.TestPlan
250 Reflex.TestPlan
248 Reflex.Plan.Pure
251 Reflex.Plan.Pure
src/Data/AppendMap.hs+18−9
@@ -7,10 +7,14 @@
7{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
8{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
9{-# OPTIONS_GHC -fno-warn-orphans #-}
9{-# OPTIONS_GHC -fno-warn-orphans #-}
10−-- | 'Data.Map' with a better 'Monoid' instance
10+-- |
11−--
11+-- Module:
12+-- Data.AppendMap
13+-- Description:
14+-- Instances and convenience functions for 'Data.Map.Monoidal'. We use
15+-- monoidal-containers to take advantage of its better monoid instance.
12−-- 'Data.Map' has @mappend = union@, which is left-biased. AppendMap has
16+-- 'Data.Map' has @mappend = union@, which is left-biased. 'MonoidalMap'
13−-- @mappend = unionWith mappend@ instead.
17+-- has @mappend = unionWith mappend@ instead.
14module Data.AppendMap
18module Data.AppendMap
15 ( module Data.AppendMap
19 ( module Data.AppendMap
16 , module Data.Map.Monoidal
20 , module Data.Map.Monoidal
@@ -26,22 +30,24 @@ import qualified Data.Map.Internal.Debug as Map (showTree, showTreeWith)
26#else
30#else
27import qualified Data.Map as Map (showTree, showTreeWith)
31import qualified Data.Map as Map (showTree, showTreeWith)
28#endif
32#endif
29−import Data.Witherable (Filterable(..))
33+import qualified Data.Witherable as W
30−import Data.Map.Monoidal (MonoidalMap(..), delete, null, empty)
34+import Data.Map.Monoidal
31−import qualified Data.Map.Monoidal as M
32
35
33{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
36{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
37+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
34type AppendMap = MonoidalMap
38type AppendMap = MonoidalMap
35
39
36{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
40{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
41+-- | A synonym for 'getMonoidalMap'
37_unAppendMap :: MonoidalMap k v -> Map k v
42_unAppendMap :: MonoidalMap k v -> Map k v
38_unAppendMap = getMonoidalMap
43_unAppendMap = getMonoidalMap
39
44
45+-- | Pattern synonym for 'MonoidalMap'
40pattern AppendMap :: Map k v -> MonoidalMap k v
46pattern AppendMap :: Map k v -> MonoidalMap k v
41pattern AppendMap m = MonoidalMap m
47pattern AppendMap m = MonoidalMap m
42
48
43−instance Filterable (MonoidalMap k) where
49+instance W.Filterable (MonoidalMap k) where
44− mapMaybe = M.mapMaybe
50+ mapMaybe = mapMaybe
45
51
46-- | Deletes a key, returning 'Nothing' if the result is empty.
52-- | Deletes a key, returning 'Nothing' if the result is empty.
47nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
53nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -51,6 +57,7 @@ nonEmptyDelete k vs =
51 then Nothing
57 then Nothing
52 else Just deleted
58 else Just deleted
53
59
60+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
54mapMaybeNoNull :: (a -> Maybe b)
61mapMaybeNoNull :: (a -> Maybe b)
55 -> MonoidalMap token a
62 -> MonoidalMap token a
56 -> Maybe (MonoidalMap token b)
63 -> Maybe (MonoidalMap token b)
@@ -61,9 +68,11 @@ mapMaybeNoNull f as =
61 else Just bs
68 else Just bs
62
69
63-- TODO: Move instances to `Reflex.Patch`
70-- TODO: Move instances to `Reflex.Patch`
71+-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
64showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
72showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
65showTree = coerce (Map.showTree :: Map k a -> String)
73showTree = coerce (Map.showTree :: Map k a -> String)
66
74
75+-- | Displays a 'MonoidalMap' as a tree, using the supplied function to convert nodes to string.
67showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
76showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
68showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
77showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
69
78
src/Data/FastMutableIntMap.hs+17
@@ -1,4 +1,9 @@
1{-# LANGUAGE TypeFamilies #-}
1{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Data.FastMutableIntMap
5+-- Description:
6+-- A mutable version of 'IntMap'
2module Data.FastMutableIntMap
7module Data.FastMutableIntMap
3 ( FastMutableIntMap
8 ( FastMutableIntMap
4 , new
9 , new
@@ -32,33 +37,43 @@ import Data.IORef
32import Reflex.Patch.Class
37import Reflex.Patch.Class
33import Reflex.Patch.IntMap
38import Reflex.Patch.IntMap
34
39
40+-- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO.
41+-- Operations on 'FastMutableIntMap' run in IO.
35newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
42newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
36
43
44+-- | Create a new 'FastMutableIntMap' out of an 'IntMap'
37new :: IntMap a -> IO (FastMutableIntMap a)
45new :: IntMap a -> IO (FastMutableIntMap a)
38new m = FastMutableIntMap <$> newIORef m
46new m = FastMutableIntMap <$> newIORef m
39
47
48+-- | Create a new empty 'FastMutableIntMap'
40newEmpty :: IO (FastMutableIntMap a)
49newEmpty :: IO (FastMutableIntMap a)
41newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
50newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
42
51
52+-- | Insert an element into a 'FastMutableIntMap' at the given key
43insert :: FastMutableIntMap a -> Int -> a -> IO ()
53insert :: FastMutableIntMap a -> Int -> a -> IO ()
44insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
54insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
45
55
56+-- | Attempt to lookup an element by key in a 'FastMutableIntMap'
46lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
57lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
47lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
58lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
48
59
60+-- | Runs the provided action over the intersection of a 'FastMutableIntMap' and an 'IntMap'
49forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
61forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
50forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
62forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
51 a <- liftIO $ readIORef r
63 a <- liftIO $ readIORef r
52 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
64 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
53
65
66+-- | Runs the provided action over the values of a 'FastMutableIntMap'
54for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
67for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
55for_ (FastMutableIntMap r) f = do
68for_ (FastMutableIntMap r) f = do
56 a <- liftIO $ readIORef r
69 a <- liftIO $ readIORef r
57 traverse_ f a
70 traverse_ f a
58
71
72+-- | Checks whether a 'FastMutableIntMap' is empty
59isEmpty :: FastMutableIntMap a -> IO Bool
73isEmpty :: FastMutableIntMap a -> IO Bool
60isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
74isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
61
75
76+-- | Retrieves the size of a 'FastMutableIntMap'
62size :: FastMutableIntMap a -> IO Int
77size :: FastMutableIntMap a -> IO Int
63size (FastMutableIntMap r) = IntMap.size <$> readIORef r
78size (FastMutableIntMap r) = IntMap.size <$> readIORef r
64
79
@@ -69,6 +84,8 @@ getFrozenAndClear (FastMutableIntMap r) = do
69 writeIORef r IntMap.empty
84 writeIORef r IntMap.empty
70 return result
85 return result
71
86
87+-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Reflex.Patch.IntMap'),
88+-- and returns an 'IntMap' with the modified keys and values.
72applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
89applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
73applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
90applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
74 v <- readIORef r
91 v <- readIORef r
src/Data/FastWeakBag.hs+1−1
@@ -50,7 +50,7 @@ newtype FastWeakBag a = FastWeakBag JSVal
50#else
50#else
51data FastWeakBag a = FastWeakBag
51data FastWeakBag a = FastWeakBag
52 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
52 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
53+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
54 }
54 }
55#endif
55#endif
56
56
src/Reflex/Adjustable/Class.hs+30−3
@@ -11,6 +11,13 @@
11#ifdef USE_REFLEX_OPTIMIZER
11#ifdef USE_REFLEX_OPTIMIZER
12{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13#endif
13#endif
14+-- |
15+-- Module:
16+-- Reflex.Adjustable.Class
17+-- Description:
18+-- A class for actions that can be "adjusted" over time based on some 'Event'
19+-- such that, when observed after the firing of any such 'Event', the result
20+-- is as though the action was originally run with the 'Event's value.
14module Reflex.Adjustable.Class
21module Reflex.Adjustable.Class
15 (
22 (
16 -- * The Adjustable typeclass
23 -- * The Adjustable typeclass
@@ -94,13 +101,32 @@ instance Adjustable t m => Adjustable t (ReaderT r m) where
94 r <- ask
101 r <- ask
95 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
102 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
96
103
104+-- | Traverse a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
105+-- to the 'DMap' can add, remove, or update values.
106+sequenceDMapWithAdjust
107+ :: (GCompare k, Adjustable t m)
108+ => DMap k m
109+ -> Event t (PatchDMap k m)
97−sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
110+ -> m (DMap k Identity, Event t (PatchDMap k Identity))
98sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
111sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
99
112
113+-- | Traverses a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
114+-- to the 'DMap' can add, remove, update, move, or swap values.
115+sequenceDMapWithAdjustWithMove
116+ :: (GCompare k, Adjustable t m)
117+ => DMap k m
118+ -> Event t (PatchDMapWithMove k m)
100−sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
119+ -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
101sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
120sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
102
121
122+-- | Traverses a 'Map', running the provided 'Adjustable' action. The provided 'Event' of patches to the 'Map'
123+-- can add, remove, update, move, or swap values.
124+mapMapWithAdjustWithMove
125+ :: forall t m k v v'. (Adjustable t m, Ord k)
126+ => (k -> v -> m v')
127+ -> Map k v
128+ -> Event t (PatchMapWithMove k v)
103−mapMapWithAdjustWithMove :: forall t m k v v'. (Adjustable t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
129+ -> m (Map k v', Event t (PatchMapWithMove k v'))
104mapMapWithAdjustWithMove f m0 m' = do
130mapMapWithAdjustWithMove f m0 m' = do
105 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
131 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
106 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
132 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
@@ -110,4 +136,5 @@ mapMapWithAdjustWithMove f m0 m' = do
110--------------------------------------------------------------------------------
136--------------------------------------------------------------------------------
111
137
112{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
138{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
139+-- | Synonym for 'Adjustable'
113type MonadAdjust = Adjustable
140type MonadAdjust = Adjustable
src/Reflex/Class.hs+103−10
@@ -300,6 +300,8 @@ class ( MonadHold t (PushM t)
300 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
300 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
301
301
302--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
302--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
303+-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
304+-- keys simultaneously.
303mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
305mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
304mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
306mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
305
307
@@ -368,26 +370,91 @@ class MonadSample t m => MonadHold t m where
368 -- the supplied 'Event'.
370 -- the supplied 'Event'.
369 headE :: Event t a -> m (Event t a)
371 headE :: Event t a -> m (Event t a)
370
372
373+-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
374+-- using the combining function to produce a patch.
375+accumIncremental
371−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
376+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
377+ => (PatchTarget p -> b -> p)
378+ -> PatchTarget p
379+ -> Event t b
380+ -> m (Incremental t p)
372accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
381accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
382+
383+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
384+accumMIncremental
373−accumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t p) -> PatchTarget p -> Event t b -> m (Incremental t p)
385+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
386+ => (PatchTarget p -> b -> PushM t p)
387+ -> PatchTarget p
388+ -> Event t b
389+ -> m (Incremental t p)
374accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
390accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
391+
392+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
393+-- combining function produces @Nothing@)
394+accumMaybeIncremental
375−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
395+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
396+ => (PatchTarget p -> b -> Maybe p)
397+ -> PatchTarget p
398+ -> Event t b
399+ -> m (Incremental t p)
376accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
400accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
401+
402+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
403+accumMaybeMIncremental
377−accumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p)) -> PatchTarget p -> Event t b -> m (Incremental t p)
404+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
405+ => (PatchTarget p -> b -> PushM t (Maybe p))
406+ -> PatchTarget p
407+ -> Event t b
408+ -> m (Incremental t p)
378accumMaybeMIncremental f z e = do
409accumMaybeMIncremental f z e = do
379 rec let e' = flip push e $ \o -> do
410 rec let e' = flip push e $ \o -> do
380 v <- sample $ currentIncremental d'
411 v <- sample $ currentIncremental d'
381 f v o
412 f v o
382 d' <- holdIncremental z e'
413 d' <- holdIncremental z e'
383 return d'
414 return d'
415+
416+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event'
417+-- with a function that both accumulates and produces a value to fire
418+-- as an 'Event'. Returns both the accumulated value and the constructed
419+-- 'Event'.
420+mapAccumIncremental
384−mapAccumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
421+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
422+ => (PatchTarget p -> b -> (p, c))
423+ -> PatchTarget p
424+ -> Event t b
425+ -> m (Incremental t p, Event t c)
385mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
426mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
427+
428+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
429+mapAccumMIncremental
386−mapAccumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
430+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
431+ => (PatchTarget p -> b -> PushM t (p, c))
432+ -> PatchTarget p
433+ -> Event t b
434+ -> m (Incremental t p, Event t c)
387mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
435mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
436+
437+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event' with
438+-- a function that both optionally accumulates and optionally produces
439+-- a value to fire as a separate output 'Event'.
440+-- Note that because 'Nothing's are discarded in both cases, the output
441+-- 'Event' may fire even though the output 'Incremental' has not changed, and
442+-- the output 'Incremental' may update even when the output 'Event' is not firing.
443+mapAccumMaybeIncremental
388−mapAccumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
444+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
445+ => (PatchTarget p -> b -> (Maybe p, Maybe c))
446+ -> PatchTarget p
447+ -> Event t b
448+ -> m (Incremental t p, Event t c)
389mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
449mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
450+
451+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
452+mapAccumMaybeMIncremental
390−mapAccumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
453+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
454+ => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c))
455+ -> PatchTarget p
456+ -> Event t b
457+ -> m (Incremental t p, Event t c)
391mapAccumMaybeMIncremental f z e = do
458mapAccumMaybeMIncremental f z e = do
392 rec let e' = flip push e $ \o -> do
459 rec let e' = flip push e $ \o -> do
393 v <- sample $ currentIncremental d'
460 v <- sample $ currentIncremental d'
@@ -398,6 +465,7 @@ mapAccumMaybeMIncremental f z e = do
398 d' <- holdIncremental z $ mapMaybe fst e'
465 d' <- holdIncremental z $ mapMaybe fst e'
399 return (d', mapMaybe snd e')
466 return (d', mapMaybe snd e')
400
467
468+-- | A somewhat slow implementation of 'headE'
401slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
469slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
402slowHeadE e = do
470slowHeadE e = do
403 rec be <- hold e $ fmapCheap (const never) e'
471 rec be <- hold e $ fmapCheap (const never) e'
@@ -418,6 +486,8 @@ newtype EventSelector t k = EventSelector
418 select :: forall a. k a -> Event t a
486 select :: forall a. k a -> Event t a
419 }
487 }
420
488
489+-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
490+-- filtering by key.
421newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
491newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
422
492
423--------------------------------------------------------------------------------
493--------------------------------------------------------------------------------
@@ -801,7 +871,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
801mergeList [] = never
871mergeList [] = never
802mergeList es = mergeWithFoldCheap' id es
872mergeList es = mergeWithFoldCheap' id es
803
873
874+unsafeMapIncremental
875+ :: (Reflex t, Patch p, Patch p')
804−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
876+ => (PatchTarget p -> PatchTarget p')
877+ -> (p -> p')
878+ -> Incremental t p
879+ -> Incremental t p'
805unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
880unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
806
881
807-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
882-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
@@ -918,6 +993,9 @@ coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $
918 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
993 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
919 ]
994 ]
920
995
996+-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
997+-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
998+-- fires with the patched value.
921switchHoldPromptOnlyIncremental
999switchHoldPromptOnlyIncremental
922 :: forall t m p pt w
1000 :: forall t m p pt w
923 . ( Reflex t
1001 . ( Reflex t
@@ -942,8 +1020,12 @@ switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = d
942
1020
943instance Reflex t => Align (Event t) where
1021instance Reflex t => Align (Event t) where
944 nil = never
1022 nil = never
1023+#if MIN_VERSION_these(0, 8, 0)
1024+instance Reflex t => Semialign (Event t) where
1025+#endif
945 align = alignEventWithMaybe Just
1026 align = alignEventWithMaybe Just
946
1027
1028+
947-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
1029-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
948-- the 'Behavior' is true at the time of occurrence.
1030-- the 'Behavior' is true at the time of occurrence.
949gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
1031gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
@@ -1015,7 +1097,16 @@ distributeListOverDyn = distributeListOverDynWith id
1015
1097
1016-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
1098-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
1017distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
1099distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
1018−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
1100+distributeListOverDynWith f =
1101+ fmap (f . map fromDSum . DMap.toAscList) .
1102+ distributeDMapOverDynPure .
1103+ DMap.fromDistinctAscList .
1104+ zipWith toDSum [0..]
1105+ where
1106+ toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
1107+ toDSum k v = Const2 k :=> v
1108+ fromDSum :: DSum (Const2 Int a) Identity -> a
1109+ fromDSum (Const2 _ :=> Identity v) = v
1019
1110
1020-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
1111-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
1021-- unless the second supplied 'Event' occurs simultaneously.
1112-- unless the second supplied 'Event' occurs simultaneously.
@@ -1491,8 +1582,10 @@ mergeWithFoldCheap' f es =
1491--------------------------------------------------------------------------------
1582--------------------------------------------------------------------------------
1492
1583
1493{-# 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'. " #-}
1584{-# 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'. " #-}
1585+-- | See 'switchHoldPromptly'
1494switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1586switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1495switchPromptly = switchHoldPromptly
1587switchPromptly = switchHoldPromptly
1496{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1588{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1589+-- | See 'switchHoldPromptOnly'
1497switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1590switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1498switchPromptOnly = switchHoldPromptOnly
1591switchPromptOnly = switchHoldPromptOnly
src/Reflex/Dynamic.hs+9−11
@@ -203,23 +203,15 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
203
203
204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
205-- non-'Dynamic' elements, preserving the order of the elements.
205-- non-'Dynamic' elements, preserving the order of the elements.
206+{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
206distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
207−distributeListOverDynPure =
208+distributeListOverDynPure = distributeListOverDyn
208− fmap (map fromDSum . DMap.toAscList) .
209− distributeDMapOverDynPure .
210− DMap.fromDistinctAscList .
211− zipWith toDSum [0..]
212− where
213− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
214− toDSum k v = Const2 k :=> v
215− fromDSum :: DSum (Const2 Int a) Identity -> a
216− fromDSum (Const2 _ :=> Identity v) = v
217
209
218--TODO: Generalize this to functors other than Maps
210--TODO: Generalize this to functors other than Maps
219-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
211-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
220-- with the current values of the 'Dynamic's in a map.
212-- with the current values of the 'Dynamic's in a map.
221joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
213joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
222−joinDynThroughMap = join . fmap distributeMapOverDynPure
214+joinDynThroughMap = (distributeMapOverDynPure =<<)
223
215
224-- | Print the value of the 'Dynamic' when it is first read and on each
216-- | Print the value of the 'Dynamic' when it is first read and on each
225-- subsequent change that is observed (as 'traceEvent'), prefixed with the
217-- subsequent change that is observed (as 'traceEvent'), prefixed with the
@@ -301,6 +293,8 @@ maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
301 Left _ -> Nothing
293 Left _ -> Nothing
302 Right a -> Just a
294 Right a -> Just a
303
295
296+-- | Turns a 'Dynamic t (Either a b)' into a 'Dynamic t (Either (Dynamic t a) (Dynamic t b))' such that
297+-- the outer 'Dynamic' is updated only when the 'Either' constructor changes (e.g., from 'Left' to 'Right').
304eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
298eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
305eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
299eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
306 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
300 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
@@ -308,6 +302,9 @@ eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
308 LeftTag :=> Compose a -> Left $ coerceDynamic a
302 LeftTag :=> Compose a -> Left $ coerceDynamic a
309 RightTag :=> Compose b -> Right $ coerceDynamic b
303 RightTag :=> Compose b -> Right $ coerceDynamic b
310
304
305+-- | Factor a 'Dynamic t DSum' into a 'Dynamic' 'DSum' containing nested 'Dynamic' values.
306+-- The outer 'Dynamic' updates only when the key of the 'DSum' changes, while the update of the inner
307+-- 'Dynamic' represents updates within the current key.
311factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
308factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
312 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
309 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
313factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
310factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
@@ -322,6 +319,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
322 inner' <- filterEventKey k (updated d)
319 inner' <- filterEventKey k (updated d)
323 inner <- holdDyn v inner'
320 inner <- holdDyn v inner'
324 return $ k :=> Compose inner
321 return $ k :=> Compose inner
322+
325--------------------------------------------------------------------------------
323--------------------------------------------------------------------------------
326-- Demux
324-- Demux
327--------------------------------------------------------------------------------
325--------------------------------------------------------------------------------
src/Reflex/DynamicWriter/Base.hs+2−2
@@ -88,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
88 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
88 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
89 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
89 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
90
90
91−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
92newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
92newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
93 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
93 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
94
94
@@ -116,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
116 (result, ws) <- runStateT a []
116 (result, ws) <- runStateT a []
117 return (result, mconcat $ reverse ws)
117 return (result, mconcat $ reverse ws)
118
118
119−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
120 tellDyn w = DynamicWriterT $ modify (w :)
120 tellDyn w = DynamicWriterT $ modify (w :)
121
121
122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
src/Reflex/DynamicWriter/Class.hs+10−4
@@ -1,4 +1,5 @@
1−-- | This module defines the 'MonadDynamicWriter' class.
1+-- | This module defines the 'DynamicWriter' class.
2+{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
@@ -8,17 +9,22 @@
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9#endif
10#endif
10module Reflex.DynamicWriter.Class
11module Reflex.DynamicWriter.Class
11− ( MonadDynamicWriter (..)
12+ ( MonadDynamicWriter
13+ , DynamicWriter(..)
12 ) where
14 ) where
13
15
14import Control.Monad.Reader (ReaderT, lift)
16import Control.Monad.Reader (ReaderT, lift)
15import Reflex.Class (Dynamic)
17import Reflex.Class (Dynamic)
16
18
19+{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}
20+-- | Type synonym for 'DynamicWriter'
21+type MonadDynamicWriter = DynamicWriter
22+
17-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
23-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
18-- and combines them monoidally to provide a 'Dynamic' result.
24-- and combines them monoidally to provide a 'Dynamic' result.
19−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
25+class (Monad m, Monoid w) => DynamicWriter t w m | m -> t w where
20 tellDyn :: Dynamic t w -> m ()
26 tellDyn :: Dynamic t w -> m ()
21
27
22−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
28+instance DynamicWriter t w m => DynamicWriter t w (ReaderT r m) where
23 tellDyn = lift . tellDyn
29 tellDyn = lift . tellDyn
24
30
src/Reflex/EventWriter/Base.hs+2−2
@@ -25,7 +25,7 @@ module Reflex.EventWriter.Base
25import Reflex.Adjustable.Class
25import Reflex.Adjustable.Class
26import Reflex.Class
26import Reflex.Class
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
28−import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
29import Reflex.Host.Class
29import Reflex.Host.Class
30import Reflex.PerformEvent.Class
30import Reflex.PerformEvent.Class
31import Reflex.PostBuild.Class
31import Reflex.PostBuild.Class
@@ -278,7 +278,7 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
278 askQueryResult = lift askQueryResult
278 askQueryResult = lift askQueryResult
279 queryIncremental = lift . queryIncremental
279 queryIncremental = lift . queryIncremental
280
280
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
282 tellDyn = lift . tellDyn
282 tellDyn = lift . tellDyn
283
283
284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
src/Reflex/FunctorMaybe.hs+2
@@ -1,6 +1,8 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+#if MIN_VERSION_base(4,9,0)
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE StandaloneDeriving #-}
4{-# LANGUAGE StandaloneDeriving #-}
5+#endif
4
6
5-- |
7-- |
6-- Module:
8-- Module:
src/Reflex/Query/Base.hs+23−30
@@ -74,26 +74,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
74getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
74getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
75getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
75getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
76
76
77−{-
77+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
78−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
78+maskMempty x = if x == mempty then Nothing else Just x
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−-}
86
79
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
88 runWithReplace (QueryT a0) a' = do
81 runWithReplace (QueryT a0) a' = do
89 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
82 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
90 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
83 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
91 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
84 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
92 bs' = fmapCheap snd $ r'
85 bs' = fmapCheap snd $ r'
93 bbs <- hold bs0 bs'
86 bbs <- hold bs0 bs'
94− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
87+ let patches = flip pushCheap bs' $ \newBs -> do
95 oldBs <- sample bbs
88 oldBs <- sample bbs
96− (~~) <$> sampleBs newBs <*> sampleBs oldBs
89+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
97 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
90 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
98 QueryT $ lift $ tellEvent patches
91 QueryT $ lift $ tellEvent patches
99 return (r0, fmapCheap fst r')
92 return (r0, fmapCheap fst r')
@@ -171,19 +164,19 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
171 let p k bs = case Map.lookup k bs0 of
164 let p k bs = case Map.lookup k bs0 of
172 Nothing -> case bs of
165 Nothing -> case bs of
173 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
166 -- 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
175 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
168 -- 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
177 Just oldBs -> case bs of
170 Just oldBs -> case bs of
178 -- 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
171 -- 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
180 -- 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
173 -- 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
181 -- composed with the sampling the child's new state.
174 -- composed with the sampling the child's new state.
182− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
175+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
183 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
176 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
184 -- child patches and wrap them in AdditivePatch.
177 -- child patches and wrap them in AdditivePatch.
185− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
178+ patch <- fold <$> Map.traverseWithKey p bs'
186− return (apply pbs bs0, Just patch)
179+ return (apply pbs bs0, AdditivePatch <$> patch)
187 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
180 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
188 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
181 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
189 return (liftedResult0, liftedResult')
182 return (liftedResult0, liftedResult')
@@ -216,28 +209,28 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
216 p k bs = case Map.lookup k bs0 of
209 p k bs = case Map.lookup k bs0 of
217 Nothing -> case MapWithMove._nodeInfo_from bs of
210 Nothing -> case MapWithMove._nodeInfo_from bs of
218 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
211 -- 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
220 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
213 -- 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
222 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
215 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
223− Nothing -> return mempty
216+ Nothing -> return Nothing
224− Just newBs -> sampleBs newBs
217+ Just newBs -> maskMempty <$> sampleBs newBs
225 Just oldBs -> case MapWithMove._nodeInfo_from bs of
218 Just oldBs -> case MapWithMove._nodeInfo_from bs of
226 -- 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
219 -- 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
228 -- 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
221 -- 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
229 -- composed with the sampling the child's new state.
222 -- 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)
231 MapWithMove.From_Move k'
224 MapWithMove.From_Move k'
232− | k' == k -> return mempty
225+ | k' == k -> return Nothing
233 | otherwise -> case Map.lookup k' bs0 of
226 | otherwise -> case Map.lookup k' bs0 of
234 -- If we are moving from a non-existent key, that is a delete
227 -- If we are moving from a non-existent key, that is a delete
235− Nothing -> negateG <$> sampleBs oldBs
228+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
236− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
229+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
237 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
230 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
238 -- child patches and wrap them in AdditivePatch.
231 -- child patches and wrap them in AdditivePatch.
239− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
232+ patch <- fold <$> Map.traverseWithKey p bs'
240− return (apply pbs bs0, Just patch)
233+ return (apply pbs bs0, AdditivePatch <$> patch)
241 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
234 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
242 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
235 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
243 return (liftedResult0, liftedResult')
236 return (liftedResult0, liftedResult')
@@ -340,5 +333,5 @@ instance Requester t m => Requester t (QueryT t q m) where
340instance EventWriter t w m => EventWriter t w (QueryT t q m) where
333instance EventWriter t w m => EventWriter t w (QueryT t q m) where
341 tellEvent = lift . tellEvent
334 tellEvent = lift . tellEvent
342
335
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
344 tellDyn = lift . tellDyn
337 tellDyn = lift . tellDyn
src/Reflex/Query/Class.hs+24−4
@@ -6,6 +6,13 @@
6{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
9+-- |
10+-- Module:
11+-- Reflex.Query.Class
12+-- Description:
13+-- A class that ties together queries to some data source and their results,
14+-- providing methods for requesting data from the source and accumulating
15+-- streamed results.
9module Reflex.Query.Class
16module Reflex.Query.Class
10 ( Query (..)
17 ( Query (..)
11 , QueryMorphism (..)
18 , QueryMorphism (..)
@@ -32,6 +39,10 @@ import Foreign.Storable
32
39
33import Reflex.Class
40import Reflex.Class
34
41
42+-- | A 'Query' can be thought of as a declaration of interest in some set of data.
43+-- A 'QueryResult' is the set of data associated with that interest set.
44+-- The @crop@ function provides a way to determine what part of a given 'QueryResult'
45+-- is relevant to a given 'Query'.
35class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
46class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
36 type QueryResult a :: *
47 type QueryResult a :: *
37 crop :: a -> QueryResult a -> QueryResult a
48 crop :: a -> QueryResult a -> QueryResult a
@@ -40,8 +51,8 @@ instance (Ord k, Query v) => Query (MonoidalMap k v) where
40 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
51 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
41 crop q r = MonoidalMap.intersectionWith (flip crop) r q
52 crop q r = MonoidalMap.intersectionWith (flip crop) r q
42
53
43−-- | NB: QueryMorphism's must be group homomorphisms when acting on the query type
54+-- | QueryMorphism's must be group homomorphisms when acting on the query type
44−-- and compatible with the query relationship when acting on the query result
55+-- and compatible with the query relationship when acting on the query result.
45data QueryMorphism q q' = QueryMorphism
56data QueryMorphism q q' = QueryMorphism
46 { _queryMorphism_mapQuery :: q -> q'
57 { _queryMorphism_mapQuery :: q -> q'
47 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
58 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
@@ -54,13 +65,16 @@ instance Category QueryMorphism where
54 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
65 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
55 }
66 }
56
67
68+-- | Apply a 'QueryMorphism' to a 'Query'
57mapQuery :: QueryMorphism q q' -> q -> q'
69mapQuery :: QueryMorphism q q' -> q -> q'
58mapQuery = _queryMorphism_mapQuery
70mapQuery = _queryMorphism_mapQuery
59
71
72+-- | Map a 'QueryMorphism' to a 'QueryResult'
60mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
73mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
61mapQueryResult = _queryMorphism_mapQueryResult
74mapQueryResult = _queryMorphism_mapQueryResult
62
75
63−-- | This type keeps track of the multiplicity of elements of the view selector that are being used by the app
76+-- | This type can be used to track of the frequency of interest in a given 'Query'. See note on
77+-- 'combineSelectedCounts'
64newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
78newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
65 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
79 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
66
80
@@ -76,10 +90,14 @@ instance Group SelectedCount where
76
90
77instance Additive SelectedCount
91instance Additive SelectedCount
78
92
93+-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
79−-- | The Semigroup/Monoid/Group instances for a ViewSelector should use this function which returns Nothing if the result is 0. This allows the pruning of leaves that are no longer wanted.
94+-- this function which returns Nothing if the result is 0. This allows the pruning of leaves
95+-- of the 'Query' that are no longer wanted.
80combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
96combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
81combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
97combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
82
98
99+-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
100+-- used interface.
83class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
101class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
84 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
102 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
85 askQueryResult :: m (Dynamic t (QueryResult q))
103 askQueryResult :: m (Dynamic t (QueryResult q))
@@ -90,9 +108,11 @@ instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
90 askQueryResult = lift askQueryResult
108 askQueryResult = lift askQueryResult
91 queryIncremental = lift . queryIncremental
109 queryIncremental = lift . queryIncremental
92
110
111+-- | Produce and send an 'Incremental' 'Query' from a 'Dynamic' 'Query'.
93tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
112tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
94tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
113tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
95
114
115+-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
96queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
116queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
97queryDyn q = do
117queryDyn q = do
98 tellQueryDyn q
118 tellQueryDyn q
src/Reflex/Spider/Internal.hs+4−1
@@ -705,7 +705,7 @@ readBehaviorUntracked b = do
705
705
706data Dynamic x p = Dynamic
706data Dynamic x p = Dynamic
707 { dynamicCurrent :: !(Behavior x (PatchTarget p))
707 { dynamicCurrent :: !(Behavior x (PatchTarget p))
708− , dynamicUpdated :: !(Event x p)
708+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709 }
709 }
710
710
711dynamicHold :: Hold x p -> Dynamic x p
711dynamicHold :: Hold x p -> Dynamic x p
@@ -1133,6 +1133,9 @@ instance HasSpiderTimeline x => Filterable (Event x) where
1133
1133
1134instance HasSpiderTimeline x => Align (Event x) where
1134instance HasSpiderTimeline x => Align (Event x) where
1135 nil = eventNever
1135 nil = eventNever
1136+#if MIN_VERSION_these(0, 8, 0)
1137+instance HasSpiderTimeline x => Semialign (Event x) where
1138+#endif
1136 align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1139 align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1137
1140
1138data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1141data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
test/EventWriterT.hs+18
@@ -1,3 +1,4 @@
1+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE GADTs #-}
3{-# LANGUAGE GADTs #-}
3{-# LANGUAGE RecursiveDo #-}
4{-# LANGUAGE RecursiveDo #-}
@@ -36,6 +37,9 @@ main = do
36 print os4
37 print os4
37 os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
38 os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
38 print os5
39 print os5
40+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
41+ print os6
42+ let ![[Nothing, Nothing]] = os6
39 return ()
43 return ()
40
44
41unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
45unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
@@ -112,3 +116,17 @@ testLiveTellEventDMap pulse = do
112 (mapToDMap $ M.singleton 1 ())
116 (mapToDMap $ M.singleton 1 ())
113 ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
117 ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
114 return ()
118 return ()
119+
120+delayedPulse
121+ :: forall t m
122+ . ( Reflex t
123+ , Adjustable t m
124+ , MonadHold t m
125+ , MonadFix m
126+ )
127+ => Event t ()
128+ -> EventWriterT t [Int] m ()
129+delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do
130+ -- This has the effect of delaying pulse' from pulse
131+ (_, pulse') <- runWithReplace (pure ()) $ pure [1] <$ pulse
132+ tellEvent pulse'
test/RequesterT.hs+111−9
@@ -1,13 +1,18 @@
1+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE GADTs #-}
3{-# LANGUAGE GADTs #-}
3{-# LANGUAGE RankNTypes #-}
4{-# LANGUAGE RankNTypes #-}
5+{-# LANGUAGE RecursiveDo #-}
4{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
5module Main where
7module Main where
6
8
7import Control.Lens
9import Control.Lens
8import Control.Monad
10import Control.Monad
11+import Control.Monad.Fix
9import qualified Data.Dependent.Map as DMap
12import qualified Data.Dependent.Map as DMap
10import Data.Dependent.Sum
13import Data.Dependent.Sum
14+import Data.Functor.Misc
15+import qualified Data.Map as M
11import Data.These
16import Data.These
12
17
13import Reflex
18import Reflex
@@ -20,18 +25,31 @@ data RequestInt a where
20
25
21main :: IO ()
26main :: IO ()
22main = do
27main = do
23− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
28+ os1 <- runApp' (unwrapApp testOrdering) $
24 [ Just ()
29 [ Just ()
25 ]
30 ]
26 print os1
31 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 $
32+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
29− [ This ()
33+ [ This ()
30− , That ()
34+ , That ()
31− , This ()
35+ , This ()
32− , These () ()
36+ , These () ()
33− ]
37+ ]
34 print os2
38 print os2
39+ os3 <- runApp' (unwrapApp testMoribundRequest) [Just ()]
40+ print os3
41+ os4 <- runApp' (unwrapApp testMoribundRequestDMap) [Just ()]
42+ print os4
43+ os5 <- runApp' (unwrapApp testLiveRequestDMap) [Just ()]
44+ print os5
45+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
46+ print os6
47+ let ![[Just [1,2,3,4,5,6,7,8,9,10]]] = os1 -- The order is reversed here: see the documentation for 'runRequesterT'
48+ let ![[Just [9,7,5,3,1]],[Nothing,Nothing],[Just [10,8,6,4,2]],[Just [10,8,6,4,2],Nothing]] = os2
49+ let ![[Nothing, Just [2]]] = os3
50+ let ![[Nothing, Just [2]]] = os4
51+ let ![[Nothing, Just [1, 2]]] = os5
52+ -- let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
35 return ()
53 return ()
36
54
37unwrapRequest :: DSum tag RequestInt -> Int
55unwrapRequest :: DSum tag RequestInt -> Int
@@ -43,7 +61,7 @@ unwrapApp :: ( Reflex t, Monad m )
43 -> m (Event t [Int])
61 -> m (Event t [Int])
44unwrapApp x appIn = do
62unwrapApp x appIn = do
45 ((), e) <- runRequesterT (x appIn) never
63 ((), e) <- runRequesterT (x appIn) never
46− return $ fmap (map unwrapRequest . DMap.toList) e
64+ return $ fmap (map unwrapRequest . requesterDataToList) e
47
65
48testOrdering :: ( Response m ~ Identity
66testOrdering :: ( Response m ~ Identity
49 , Request m ~ RequestInt
67 , Request m ~ RequestInt
@@ -65,3 +83,87 @@ testSimultaneous pulse = do
65 switchE = fmapMaybe (^? there) pulse
83 switchE = fmapMaybe (^? there) pulse
66 forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
84 forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
67 requestingIdentity (RequestInt (i+1) <$ tellE)
85 requestingIdentity (RequestInt (i+1) <$ tellE)
86+
87+-- | Test that a widget requesting and event which fires at the same time it has been replaced
88+-- doesn't count along with the new widget.
89+testMoribundRequest
90+ :: forall t m
91+ . ( Reflex t
92+ , Adjustable t m
93+ , MonadHold t m
94+ , MonadFix m
95+ , Response m ~ Identity
96+ , Request m ~ RequestInt
97+ , Requester t m
98+ )
99+ => Event t ()
100+ -> m ()
101+testMoribundRequest pulse = do
102+ rec let requestIntOnReplace x = requestingIdentity $ RequestInt x <$ rwrFinished
103+ (_, rwrFinished) <- runWithReplace (requestIntOnReplace 1) $ requestIntOnReplace 2 <$ pulse
104+ return ()
105+
106+-- | The equivalent of 'testMoribundRequest' for 'traverseDMapWithKeyWithAdjust'.
107+testMoribundRequestDMap
108+ :: forall t m
109+ . ( Reflex t
110+ , Adjustable t m
111+ , MonadHold t m
112+ , MonadFix m
113+ , Response m ~ Identity
114+ , Request m ~ RequestInt
115+ , Requester t m
116+ )
117+ => Event t ()
118+ -> m ()
119+testMoribundRequestDMap pulse = do
120+ rec let requestIntOnReplace :: Int -> m ()
121+ requestIntOnReplace x = void $ requestingIdentity $ RequestInt x <$ rwrFinished
122+ (_, rwrFinished :: Event t (PatchDMap (Const2 () Int) Identity)) <-
123+ traverseDMapWithKeyWithAdjust
124+ (\(Const2 ()) (Identity v) -> Identity . const v <$> requestIntOnReplace v)
125+ (mapToDMap $ M.singleton () 1)
126+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton () 2) <$ pulse)
127+ return ()
128+
129+-- | Ensures that elements which are _not_ removed can still fire requests
130+-- during the same frame as other elements are updated.
131+testLiveRequestDMap
132+ :: forall t m
133+ . ( Reflex t
134+ , Adjustable t m
135+ , MonadHold t m
136+ , MonadFix m
137+ , Response m ~ Identity
138+ , Request m ~ RequestInt
139+ , Requester t m
140+ )
141+ => Event t ()
142+ -> m ()
143+testLiveRequestDMap pulse = do
144+ rec let requestIntOnReplace :: Int -> m ()
145+ requestIntOnReplace x = void $ requestingIdentity $ RequestInt x <$ rwrFinished
146+ (_, rwrFinished :: Event t (PatchDMap (Const2 Int ()) Identity)) <-
147+ traverseDMapWithKeyWithAdjust
148+ (\(Const2 k) (Identity ()) -> Identity <$> requestIntOnReplace k)
149+ (mapToDMap $ M.singleton 1 ())
150+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
151+ return ()
152+
153+delayedPulse
154+ :: forall t m
155+ . ( Reflex t
156+ , Adjustable t m
157+ , MonadHold t m
158+ , MonadFix m
159+ , Response m ~ Identity
160+ , Request m ~ RequestInt
161+ , PerformEvent t m
162+ , Requester t m
163+ )
164+ => Event t ()
165+ -> m ()
166+delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do
167+ -- This has the effect of delaying pulse' from pulse
168+ (_, pulse') <- runWithReplace (pure ()) $ pure (RequestInt 1) <$ pulse
169+ requestingIdentity pulse'