on develop help sign in
Merge branch 'develop' into develop
commit5f1754f5f05cb7989cbbe9dd5e74b29c95178092 authorJohn Ericson <John.Ericson@Obsidian.Systems> dateMon 9 Dec 2019, 18:38 (7 years ago) parent976e89d18595a1
68 files changed+2829−1074
.travis.yml+165−10 CONTRIBUTING.md+66 ChangeLog.md+63 Quickref.md+24−7 README.md+13−7 cabal.haskell-ci+16 cabal.project+1 cabal.project.freeze+1 default.nix+18−8 dep/reflex-platform/default.nix+7 dep/reflex-platform/github.json+7 reflex.cabal+76−34 release.nix+38 src/Control/Monad/ReaderIO.hs+60 src/Data/AppendMap.hs+22−19 src/Data/FastMutableIntMap.hs+17 src/Data/FastWeakBag.hs+12−7 src/Data/Functor/Misc.hs+3−56 src/Data/Map/Misc.hs+26−10 src/Data/WeakBag.hs+10−5 src/Reflex.hs+3 src/Reflex/Adjustable/Class.hs+30−4 src/Reflex/BehaviorWriter/Base.hs+202 src/Reflex/BehaviorWriter/Class.hs+25 src/Reflex/Class.hs+506−132 src/Reflex/Collection.hs+157−53 src/Reflex/Dynamic.hs+35−185 src/Reflex/Dynamic/TH.hs+2−26 src/Reflex/DynamicWriter/Base.hs+13−4 src/Reflex/DynamicWriter/Class.hs+10−5 src/Reflex/EventWriter/Base.hs+68−54 src/Reflex/EventWriter/Class.hs−1 src/Reflex/FastWeak.hs+59−5 src/Reflex/FunctorMaybe.hs+31−17 src/Reflex/Host/Class.hs−1 src/Reflex/Network.hs+10−4 src/Reflex/NotReady/Class.hs+10−1 src/Reflex/Optimizer.hs+13−4 src/Reflex/Patch.hs+12−5 src/Reflex/Patch/Class.hs+1−1 src/Reflex/Patch/DMap.hs+22−8 src/Reflex/Patch/DMapWithMove.hs+9−9 src/Reflex/Patch/IntMap.hs+20−10 src/Reflex/Patch/Map.hs+1−10 src/Reflex/Patch/MapWithMove.hs+59−17 src/Reflex/PerformEvent/Base.hs+1−1 src/Reflex/PerformEvent/Class.hs−1 src/Reflex/PostBuild/Class.hs−1 src/Reflex/Profiled.hs+30−16 src/Reflex/Pure.hs+32−26 src/Reflex/Query/Base.hs+33−37 src/Reflex/Query/Class.hs+25−6 src/Reflex/Requester/Base.hs+113−5 src/Reflex/Requester/Class.hs−1 src/Reflex/Spider.hs+5−2 src/Reflex/Spider/Internal.hs+291−218 src/Reflex/Time.hs+113−13 src/Reflex/TriggerEvent/Class.hs−1 src/Reflex/Workflow.hs+13−1 test/EventWriterT.hs+76−7 test/GC.hs+4−3 test/QueryT.hs+10−1 test/Reflex/Bench/Focused.hs+12 test/Reflex/Plan/Pure.hs+4−4 test/Reflex/Test/CrossImpl.hs+5 test/Reflex/TestPlan.hs+2−2 test/RequesterT.hs+116−9 test/hlint.hs+1
.travis.yml+165−10
@@ -1,16 +1,171 @@
1−# Cache Buster: 0
2−language: nix
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.3.20190814
8+#
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
315cache:
416 directories:
5− - /nix
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
628matrix:
729 include:
8− - env: COMPILER=ghc7103
9− - env: COMPILER=ghc821
10− - env: COMPILER=ghc802
11− - env: COMPILER=ghcjs
30+ - compiler: ghcjs-8.4
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32+ - compiler: ghc-8.6.5
33+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
34+ - compiler: ghc-8.4.4
35+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
36+ - compiler: ghc-8.2.2
37+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
38+ - compiler: ghc-8.0.2
39+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
40+before_install:
41+ - |
42+ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
43+ sudo add-apt-repository -y ppa:hvr/ghcjs;
44+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
45+ sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
46+ sudo apt-get update;
47+ sudo apt-get install $CC cabal-install-3.0 nodejs;
48+ fi
49+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
50+ - WITHCOMPILER="-w $HC"
51+ - |
52+ if echo $CC | grep -q ghcjs; then
53+ GHCJS=true
54+ HC=${HC}js
55+ WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
56+ else
57+ GHCJS=false;
58+ fi
59+ - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
60+ - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
61+ - HCPKG="$HC-pkg"
62+ - unset CC
63+ - CABAL=/opt/ghc/bin/cabal
64+ - CABALHOME=$HOME/.cabal
65+ - export PATH="$CABALHOME/bin:$PATH"
66+ - TOP=$(pwd)
67+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
68+ - echo $HCNUMVER
69+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
70+ - set -o pipefail
71+ - |
72+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
73+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
74+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
75+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
76+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
77+ echo ' if (state == "cabal") {' >> .colorful.awk
78+ echo ' print blue($0)' >> .colorful.awk
79+ echo ' } else {' >> .colorful.awk
80+ echo ' print $0' >> .colorful.awk
81+ echo ' }' >> .colorful.awk
82+ echo '}' >> .colorful.awk
83+ - cat .colorful.awk
84+ - |
85+ color_cabal_output () {
86+ awk -f $TOP/.colorful.awk
87+ }
88+ - echo text | color_cabal_output
89+install:
90+ - ${CABAL} --version
91+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
92+ - node --version
93+ - echo $GHCJS
94+ - TEST=--enable-tests
95+ - BENCH=--enable-benchmarks
96+ - BENCH=--disable-benchmarks
97+ - HEADHACKAGE=false
98+ - rm -f $CABALHOME/config
99+ - |
100+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
101+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
102+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
103+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
104+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
105+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
106+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
107+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
108+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
109+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
110+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
111+ echo "install-dirs user" >> $CABALHOME/config
112+ echo " prefix: $CABALHOME" >> $CABALHOME/config
113+ echo "repository hackage.haskell.org" >> $CABALHOME/config
114+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
115+ - cat $CABALHOME/config
116+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
117+ - travis_retry ${CABAL} v2-update -v
118+ - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
119+ # Generate cabal.project
120+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
121+ - touch cabal.project
122+ - |
123+ echo "packages: ." >> cabal.project
124+ - |
125+ - cat cabal.project || true
126+ - cat cabal.project.local || true
127+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
128+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
129+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
130+ - rm cabal.project.freeze
12131script:
13− - set -e
14− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
15− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
132+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
133+ # Packaging...
134+ - ${CABAL} v2-sdist all | color_cabal_output
135+ # Unpacking...
136+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
137+ - cd ${DISTDIR} || false
138+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
139+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
140+ - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')"
141+ # Generate cabal.project
142+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
143+ - touch cabal.project
144+ - |
145+ echo "packages: ${PKGDIR_reflex}" >> cabal.project
146+ - |
147+ - cat cabal.project || true
148+ - cat cabal.project.local || true
149+ # Building...
150+ # this builds all libraries and executables (without tests/benchmarks)
151+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
152+ # Building with tests and benchmarks...
153+ # build & run tests, build benchmarks
154+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
155+ # Testing...
156+ - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
157+ # cabal check...
158+ - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check)
159+ # haddock...
160+ - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
161+ # Constraint sets
162+ - rm -rf cabal.project.local
163+ # Constraint set no-th
164+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output
165+ # Constraint set old-these
166+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output
167+ # Constraint set old-witherable
168+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
16169
170+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
171+# EOF
CONTRIBUTING.mdnew file+66
@@ -0,0 +1,66 @@
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+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
9+ - [Code Quality](#code-quality)
10+ - [Documentation](#documentation)
11+
12+## Opening Issues
13+
14+Before opening an issue, please check whether your issue has already been reported. Assuming it has not:
15+
16+* Describe the issue you're encountering or the suggestion you're making
17+* 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.
18+* Let us know what version of reflex you were using. If you're using a github checkout, provide the git hash.
19+* 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.
20+
21+## Submitting Changes
22+
23+### Guidelines for Commit Messages
24+
25+#### Summary Line
26+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.
27+
28+One way to think about it is that your commit message should be able to complete the sentence:
29+"When applied, this commit will..."
30+
31+#### Body
32+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.
33+
34+### Guidelines for Pull Requests
35+
36+Wherever possible, pull requests should add a single feature or fix a single bug. Pull requests should not bundle several unrelated changes.
37+
38+### Code Quality
39+
40+#### Warnings
41+
42+Your pull request should add no new warnings to the project. It should also generally not disable any warnings.
43+
44+#### Build and Test
45+
46+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!
47+
48+#### Dependencies
49+
50+Include version bounds whenever adding a dependency to the library stanza of the cabal file. Note that libraries added to reflex.cabal also need to be added to default.nix.
51+
52+### Documentation
53+
54+#### In the code
55+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.
56+
57+#### In the [Changelog](ChangeLog.md)
58+Add an entry to the changelog when your PR:
59+* Adds a feature
60+* Deprecates something
61+* Includes a breaking change
62+* Makes any other change that will impact users
63+
64+#### In the [Readme](README.md)
65+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.
66+
ChangeLog.mdnew file+63
@@ -0,0 +1,63 @@
1+# Revision history for reflex
2+
3+## Unreleased
4+
5+* Data.WeakBag.traverse and Data.FastWeakBag.traverse have been
6+ renamed to Data.WeakBag.traverse_ and Data.FastWeakBag.traverse_
7+ respectively.
8+
9+## 0.6.2.4
10+
11+* Update to monoidal-containers 0.6
12+
13+## 0.6.2.3
14+
15+* Add an upper-bound to witherable
16+
17+## 0.6.2.2
18+
19+* Support these >= 1. Add `split-these` flag to control whether to use new these/semialign combination or not.
20+* Update version bounds to fix some CI failures
21+* Add travis CI configuration
22+
23+## 0.6.2.1
24+
25+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
26+ functor:
27+ * `fan` to `fanG`
28+ * `EventSelectorG` for `fanG` result selector.
29+
30+* Reduce the amount of unsafeCoerce in coercing newtypes under Event/Dynamic/Behavior.
31+ * Add fused ReaderIO for the purpose of coercion (ReaderT's third argument has nominal role preventing automated coerce)
32+ * Add incrementalCoercion/coerceIncremental to go with dynamicCoercion/coerceDynamic
33+
34+* Generalize merging functions:
35+ `merge` to `mergeG`,
36+ `mergeIncremental` to `mergeIncrementalG`,
37+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
38+
39+* Generalize distribute function:
40+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
41+
42+## 0.6.2.0
43+
44+* Fix `holdDyn` so that it is lazy in its event argument
45+ These produce `DMap`s whose values needn't be `Identity`.
46+* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
47+* Fix `holdDyn` so that it is lazy in its event argument.
48+
49+## 0.6.1.0
50+
51+* Re-export all of `Data.Map.Monoidal`
52+* Fix `QueryT` and `RequesterT` tests
53+
54+## 0.6.0.0 -- 2019-03-20
55+
56+* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
57+* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
58+* Remove many deprecated functions.
59+* Add a `Num` instance for `Dynamic`.
60+* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
61+* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
62+* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
63+* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
Quickref.md+24−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.
@@ -292,3 +292,20 @@ Th typeclasses and their associated annotations include:
292292[P,T] delay :: NominalDiffTime -> Event t a -> m (Event t a)
293293```
294294
295+## Networks
296+
297+```haskell
298+-- Functions from Reflex.Network used to deal with Dynamics/Events carrying (m a)
299+
300+-- Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
301+-- The returned Event of network results occurs when the Dynamic does. Note: Often, the type a is an Event,
302+-- in which case the return value is an Event-of-Events that would typically be flattened (via switchHold).
303+[P,A] networkView :: Dynamic (m a) -> m (Event a)
304+
305+-- Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the
306+-- Event fires. The returned Dynamic of network results occurs when the Event does. Note: Often, the type a is an
307+-- Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
308+[H,A] networkHold :: m a -> Event (m a) -> m (Dynamic a)
309+
310+-- Render a placeholder network to be shown while another network is not yet done building
311+[P,A] untilReady :: m a -> m b -> m (a, Event b)
README.md+13−7
@@ -1,19 +1,25 @@
1−## Reflex
1+## [Reflex](https://reflex-frp.org/)
2+
3+[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex)
4+
25### Practical Functional Reactive Programming
36
4−Reflex is an fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
7+Reflex is a fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
58
69[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.
710
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−
1011A summary of Reflex functions is available in the [quick reference](Quickref.md).
1112
12−### Additional resources
13+**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
14+
15+### Resources
1316[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
1417
1518[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
1619
17−[hackage](https://hackage.haskell.org/package/reflex)
18−
1920[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
21+
22+### Hacking
23+
24+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.
25+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.
cabal.haskell-cinew file+16
@@ -0,0 +1,16 @@
1+distribution: xenial
2+benchmarks: False
3+unconstrained: False
4+installed: -all
5+
6+-- https://github.com/haskell/cabal/issues/6106
7+install-dependencies: False
8+
9+constraint-set no-th
10+ constraints: reflex -use-template-haskell
11+
12+constraint-set old-these
13+ constraints: these <1
14+
15+constraint-set old-witherable
16+ constraints: witherable <0.3.2
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
cabal.project.freezenew file+1
@@ -0,0 +1 @@
1+constraints: any.text < 1.2.4.0
default.nix+18−8
@@ -6,13 +6,19 @@
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, profunctors
10+, splitThese ? (semialign != null), semialign ? null, these-lens ? null
1011, useTemplateHaskell ? true
1112}:
1213mkDerivation {
1314 pname = "reflex";
14− version = "0.5";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
15+ version = "0.6.2.4";
16+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
17+ "default.nix"
18+ "release.nix"
19+ ".git"
20+ "dist"
21+ ])) ./.;
1622 libraryHaskellDepends = [
1723 base bifunctors containers dependent-map dependent-sum
1824 exception-transformers lens
@@ -21,18 +27,22 @@ mkDerivation {
2127 transformers-compat prim-uniq
2228 base bifunctors containers deepseq dependent-map dependent-sum
2329 mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
30+ random time unbounded-delays monoidal-containers witherable
31+ profunctors
2532 ] ++ (if ghc.isGhcjs or false then [
2633 ghcjs-base
2734 ] else []) ++ (if !useTemplateHaskell then [] else [
2835 haskell-src-exts haskell-src-meta
29− ]);
36+ ]) ++ (if splitThese then [
37+ semialign
38+ these-lens
39+ ] else []);
3040 testHaskellDepends = if ghc.isGhcjs or false then [] else [
3141 hlint filepath directory filemanip
3242 ];
33− configureFlags = if useTemplateHaskell then [] else [
34− "-f-use-template-haskell"
35− ];
43+ configureFlags =
44+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
45+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
3646 homepage = "https://github.com/reflex-frp/reflex";
3747 description = "Higher-order Functional Reactive Programming";
3848 license = stdenv.lib.licenses.bsd3;
dep/reflex-platform/default.nixnew file+7
@@ -0,0 +1,7 @@
1+# DO NOT HAND-EDIT THIS FILE
2+import ((import <nixpkgs> {}).fetchFromGitHub (
3+ let json = builtins.fromJSON (builtins.readFile ./github.json);
4+ in { inherit (json) owner repo rev sha256;
5+ private = json.private or false;
6+ }
7+))
dep/reflex-platform/github.jsonnew file+7
@@ -0,0 +1,7 @@
1+{
2+ "owner": "reflex-frp",
3+ "repo": "reflex-platform",
4+ "branch": "jailbreakTheselens",
5+ "rev": "4284ed527c96c373538dc7e31776d9a50ca6aa91",
6+ "sha256": "1m1nkxq7ng9wnqbd77xn8d81f2iz8g61y5m0r3gp0gi0q59jm8ay"
7+}
reflex.cabal+76−34
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.5
2+Version: 0.6.2.4
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -12,6 +12,14 @@ Build-type: Simple
1212Cabal-version: >=1.9.2
1313homepage: https://reflex-frp.org
1414bug-reports: https://github.com/reflex-frp/reflex/issues
15+extra-source-files:
16+ README.md
17+ Quickref.md
18+ ChangeLog.md
19+
20+tested-with:
21+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5,
22+ GHCJS ==8.4
1523
1624flag use-reflex-optimizer
1725 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -33,37 +41,51 @@ flag fast-weak
3341 default: False
3442 manual: True
3543
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
3649library
3750 hs-source-dirs: src
3851 build-depends:
3952 MemoTrie == 0.6.*,
40− base >= 4.7 && < 4.13,
53+ base >= 4.9 && < 4.13,
4154 bifunctors >= 5.2 && < 5.6,
42− comonad,
43− containers >= 0.5 && < 0.7,
55+ comonad >= 5.0.4 && < 5.1,
56+ constraints-extras >= 0.3 && < 0.4,
57+ containers >= 0.6 && < 0.7,
4458 data-default >= 0.5 && < 0.8,
45− dependent-map >= 0.2.4 && < 0.3,
59+ dependent-map >= 0.3 && < 0.4,
4660 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
4762 lens >= 4.7 && < 5,
4863 monad-control >= 1.0.1 && < 1.1,
49− monoidal-containers == 0.5.*,
5064 mtl >= 2.1 && < 2.3,
5165 prim-uniq >= 0.1.0.1 && < 0.2,
52− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
5368 random == 1.1.*,
5469 ref-tf == 0.4.*,
5570 reflection == 2.1.*,
5671 semigroupoids >= 4.0 && < 6,
57− semigroups >= 0.16 && < 0.19,
5872 stm >= 2.4 && < 2.6,
5973 syb >= 0.5 && < 0.8,
60− these >= 0.4 && < 0.8,
6174 time >= 1.4 && < 1.9,
62− transformers >= 0.2,
63− transformers-compat >= 0.3,
64− unbounded-delays >= 0.1.0.0 && < 0.2
75+ transformers >= 0.5.6.0 && < 0.6,
76+ unbounded-delays >= 0.1.0.0 && < 0.2,
77+ witherable >= 0.3 && < 0.3.2
78+
79+ if flag(split-these)
80+ build-depends: these >= 1 && <1.1,
81+ semialign >=1 && <1.2,
82+ monoidal-containers >= 0.6 && < 0.7
83+ else
84+ build-depends: these >= 0.4 && <0.9,
85+ monoidal-containers == 0.4.0.0
6586
6687 exposed-modules:
88+ Control.Monad.ReaderIO
6789 Data.AppendMap,
6890 Data.FastMutableIntMap,
6991 Data.FastWeakBag,
@@ -73,6 +95,8 @@ library
7395 Reflex,
7496 Reflex.Class,
7597 Reflex.Adjustable.Class,
98+ Reflex.BehaviorWriter.Base,
99+ Reflex.BehaviorWriter.Class,
76100 Reflex.Collection,
77101 Reflex.Dynamic,
78102 Reflex.Dynamic.Uniq,
@@ -126,8 +150,8 @@ library
126150 if flag(use-template-haskell)
127151 cpp-options: -DUSE_TEMPLATE_HASKELL
128152 build-depends:
129− dependent-sum >= 0.3 && < 0.5,
130− haskell-src-exts >= 1.16 && < 1.21,
153+ dependent-sum >= 0.6 && < 0.7,
154+ haskell-src-exts >= 1.16 && < 1.22,
131155 haskell-src-meta >= 0.6 && < 0.9,
132156 template-haskell >= 2.9 && < 2.15
133157 exposed-modules:
@@ -135,7 +159,7 @@ library
135159 other-extensions: TemplateHaskell
136160 else
137161 build-depends:
138− dependent-sum == 0.4.*
162+ dependent-sum >= 0.6 && < 0.7
139163
140164 if flag(fast-weak) && impl(ghcjs)
141165 cpp-options: -DGHCJS_FAST_WEAK
@@ -152,14 +176,14 @@ test-suite semantics
152176 base,
153177 bifunctors,
154178 containers,
155− deepseq >= 1.3 && < 1.5,
179+ deepseq,
156180 dependent-map,
157181 dependent-sum,
158182 mtl,
159183 ref-tf,
160184 reflex,
161185 split,
162− transformers >= 0.3
186+ transformers
163187 other-modules:
164188 Reflex.Bench.Focused
165189 Reflex.Plan.Pure
@@ -178,7 +202,7 @@ test-suite CrossImpl
178202 containers,
179203 dependent-map,
180204 dependent-sum,
181− deepseq >= 1.3 && < 1.5,
205+ deepseq,
182206 mtl,
183207 transformers,
184208 ref-tf,
@@ -197,7 +221,7 @@ test-suite hlint
197221 , directory
198222 , filepath
199223 , filemanip
200− , hlint
224+ , hlint < 2.1 || >= 2.2.2
201225 if impl(ghcjs)
202226 buildable: False
203227
@@ -207,7 +231,8 @@ test-suite EventWriterT
207231 hs-source-dirs: test
208232 build-depends: base
209233 , containers
210− , deepseq >= 1.3 && < 1.5
234+ , deepseq
235+ , dependent-map
211236 , dependent-sum
212237 , lens
213238 , mtl
@@ -215,6 +240,10 @@ test-suite EventWriterT
215240 , transformers
216241 , reflex
217242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
218247 other-modules:
219248 Reflex.Test
220249 Reflex.TestPlan
@@ -222,22 +251,30 @@ test-suite EventWriterT
222251 Reflex.Plan.Pure
223252 Test.Run
224253
254+
225255test-suite RequesterT
226256 type: exitcode-stdio-1.0
227257 main-is: RequesterT.hs
228258 hs-source-dirs: test
229259 build-depends: base
260+ , containers
261+ , deepseq
230262 , dependent-sum
231263 , dependent-map
232264 , lens
265+ , mtl
233266 , these
234267 , transformers
235268 , reflex
236269 , ref-tf
237− buildable: False
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
238274 other-modules:
239275 Reflex.TestPlan
240276 Reflex.Plan.Pure
277+ Test.Run
241278
242279test-suite QueryT
243280 type: exitcode-stdio-1.0
@@ -247,15 +284,18 @@ test-suite QueryT
247284 , containers
248285 , dependent-map
249286 , dependent-sum
250− , deepseq >= 1.3 && < 1.5
287+ , deepseq
251288 , lens
252289 , monoidal-containers
253290 , mtl
254291 , ref-tf
255292 , reflex
256− , semigroups
257293 , these
258294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
259299 other-modules:
260300 Test.Run
261301 Reflex.TestPlan
@@ -270,14 +310,16 @@ test-suite GC-Semantics
270310 , containers
271311 , dependent-sum
272312 , dependent-map
273− , deepseq >= 1.3 && < 1.5
313+ , deepseq
274314 , mtl
275315 , these
276316 , transformers
277317 , reflex
278318 , ref-tf
279− if impl(ghc < 8)
280− build-depends: semigroups
319+
320+ if flag(split-these)
321+ build-depends: semialign
322+
281323 other-modules:
282324 Reflex.Plan.Pure
283325 Reflex.Plan.Reflex
@@ -290,7 +332,7 @@ test-suite rootCleanup
290332 hs-source-dirs: test
291333 build-depends: base
292334 , containers
293− , deepseq >= 1.3 && < 1.5
335+ , deepseq
294336 , dependent-sum
295337 , mtl
296338 , reflex
@@ -309,8 +351,8 @@ benchmark spider-bench
309351 build-depends:
310352 base,
311353 containers,
312− criterion >= 1.1 && < 1.6,
313− deepseq >= 1.3 && < 1.5,
354+ criterion,
355+ deepseq,
314356 dependent-map,
315357 dependent-sum,
316358 ref-tf,
@@ -319,7 +361,7 @@ benchmark spider-bench
319361 reflex,
320362 split,
321363 stm,
322− transformers >= 0.3
364+ transformers
323365 other-modules:
324366 Reflex.TestPlan
325367 Reflex.Plan.Reflex
@@ -333,9 +375,9 @@ benchmark saulzar-bench
333375 ghc-options: -Wall -O2 -rtsopts -threaded
334376 build-depends:
335377 base,
336− containers >= 0.5 && < 0.7,
337− criterion >= 1.1 && < 1.6,
338− deepseq >= 1.3 && < 1.5,
378+ containers,
379+ criterion,
380+ deepseq,
339381 dependent-map,
340382 dependent-sum,
341383 loch-th,
@@ -347,7 +389,7 @@ benchmark saulzar-bench
347389 split,
348390 stm,
349391 time,
350− transformers >= 0.3
392+ transformers
351393 other-modules:
352394 Reflex.TestPlan
353395 Reflex.Plan.Reflex
release.nixnew file+38
@@ -0,0 +1,38 @@
1+{ reflex-platform-fun ? import ./dep/reflex-platform
2+}:
3+
4+let
5+ native-reflex-platform = reflex-platform-fun {};
6+ inherit (native-reflex-platform.nixpkgs) lib;
7+ systems = ["x86_64-linux" "x86_64-darwin"];
8+
9+ perPlatform = lib.genAttrs systems (system: let
10+ reflex-platform = reflex-platform-fun { inherit system; };
11+ compilers = [
12+ "ghc"
13+ "ghcjs"
14+ ] ++ lib.optionals (reflex-platform.androidSupport) [
15+ "ghcAndroidAarch64"
16+ "ghcAndroidAarch32"
17+ ] ++ lib.optionals (reflex-platform.iosSupport) [
18+ "ghcIosAarch64"
19+ ];
20+ hsPkgs = lib.genAttrs compilers (ghc: let
21+ ghc' = reflex-platform.${ghc}.override {
22+ overrides = self: super: {
23+ reflex-dontUseTemplateHaskell = self.callPackage ./. { useTemplateHaskell = false; };
24+ reflex = self.callPackage ./. { useTemplateHaskell = true; };
25+ };
26+ };
27+ in {
28+ inherit (ghc') reflex reflex-dontUseTemplateHaskell;
29+ });
30+ in hsPkgs // {
31+ cache = reflex-platform.pinBuildInputs "reflex-${system}"
32+ (lib.concatLists (map builtins.attrValues (builtins.attrValues hsPkgs)));
33+ });
34+
35+ metaCache = native-reflex-platform.pinBuildInputs "reflex-everywhere"
36+ (map (a: a.cache) (builtins.attrValues perPlatform));
37+
38+in perPlatform // { inherit metaCache; }
src/Control/Monad/ReaderIO.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# language RoleAnnotations #-}
2+{-# language MultiParamTypeClasses #-}
3+{-# language FlexibleInstances #-}
4+{-# language CPP #-}
5+module Control.Monad.ReaderIO
6+ (
7+ ReaderIO (..)
8+ )
9+ where
10+
11+import Control.Monad.Fix
12+#if MIN_VERSION_base(4,10,0)
13+import Control.Applicative
14+#endif
15+import Control.Monad
16+import Control.Monad.Reader.Class
17+import Control.Monad.IO.Class
18+
19+-- | An approximate clone of @RIO@ from the @rio@ package, but not based on
20+-- @ReaderT@. The trouble with @ReaderT@ is that its third type argument has a
21+-- @nominal@ role, so we can't coerce through it when it's wrapped in some
22+-- other @data@ type. Ugh.
23+newtype ReaderIO e a = ReaderIO { runReaderIO :: e -> IO a }
24+type role ReaderIO representational representational
25+
26+instance Functor (ReaderIO e) where
27+ fmap = liftM
28+ {-# INLINE fmap #-}
29+ a <$ m = m >> pure a
30+ {-# INLINE (<$) #-}
31+
32+instance Applicative (ReaderIO e) where
33+ pure a = ReaderIO $ \_ -> pure a
34+ {-# INLINE pure #-}
35+ (<*>) = ap
36+ {-# INLINE (<*>) #-}
37+#if MIN_VERSION_base(4,10,0)
38+ liftA2 = liftM2
39+ {-# INLINE liftA2 #-}
40+#endif
41+ (*>) = (>>)
42+ {-# INLINE (*>) #-}
43+
44+instance Monad (ReaderIO e) where
45+ ReaderIO q >>= f = ReaderIO $ \e -> q e >>= \a -> runReaderIO (f a) e
46+ {-# INLINE (>>=) #-}
47+
48+instance MonadFix (ReaderIO e) where
49+ mfix f = ReaderIO $ \e -> mfix $ \r -> runReaderIO (f r) e
50+ {-# INLINE mfix #-}
51+
52+instance MonadIO (ReaderIO e) where
53+ liftIO m = ReaderIO $ \_ -> m
54+ {-# INLINE liftIO #-}
55+
56+instance MonadReader e (ReaderIO e) where
57+ ask = ReaderIO pure
58+ {-# INLINE ask #-}
59+ local f (ReaderIO m) = ReaderIO (m . f)
60+ {-# INLINE local #-}
src/Data/AppendMap.hs+22−19
@@ -1,21 +1,20 @@
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 #-}
15−-- | 'Data.Map' with a better 'Monoid' instance
16−--
17−-- 'Data.Map' has @mappend = union@, which is left-biased. AppendMap has
18−-- @mappend = unionWith mappend@ instead.
10+-- |
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.
16+-- 'Data.Map' has @mappend = union@, which is left-biased. 'MonoidalMap'
17+-- has @mappend = unionWith mappend@ instead.
1918module Data.AppendMap
2019 ( module Data.AppendMap
2120 , module Data.Map.Monoidal
@@ -31,22 +30,28 @@ import qualified Data.Map.Internal.Debug as Map (showTree, showTreeWith)
3130#else
3231import qualified Data.Map as Map (showTree, showTreeWith)
3332#endif
33+import qualified Data.Witherable as W
3434import Data.Map.Monoidal
35−import Reflex.Class (FunctorMaybe (..))
36−import Reflex.Patch (Additive, Group (..))
35+import qualified Data.Map.Monoidal as MonoidalMap
36+
3737
3838{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
39+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
3940type AppendMap = MonoidalMap
4041
4142{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
43+-- | A synonym for 'getMonoidalMap'
4244_unAppendMap :: MonoidalMap k v -> Map k v
4345_unAppendMap = getMonoidalMap
4446
47+-- | Pattern synonym for 'MonoidalMap'
4548pattern AppendMap :: Map k v -> MonoidalMap k v
4649pattern AppendMap m = MonoidalMap m
4750
48−instance FunctorMaybe (MonoidalMap k) where
49− fmapMaybe = mapMaybe
51+#if !MIN_VERSION_witherable(0,3,2)
52+instance W.Filterable (MonoidalMap k) where
53+ mapMaybe = MonoidalMap.mapMaybe
54+#endif
5055
5156-- | Deletes a key, returning 'Nothing' if the result is empty.
5257nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -56,24 +61,22 @@ nonEmptyDelete k vs =
5661 then Nothing
5762 else Just deleted
5863
64+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
5965mapMaybeNoNull :: (a -> Maybe b)
6066 -> MonoidalMap token a
6167 -> Maybe (MonoidalMap token b)
6268mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
69+ let bs = mapMaybe f as
6470 in if null bs
6571 then Nothing
6672 else Just bs
6773
6874-- 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−
75+-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
7476showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
7577showTree = coerce (Map.showTree :: Map k a -> String)
7678
79+-- | Displays a 'MonoidalMap' as a tree, using the supplied function to convert nodes to string.
7780showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
7881showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
7982
src/Data/FastMutableIntMap.hs+17
@@ -1,4 +1,9 @@
11{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Data.FastMutableIntMap
5+-- Description:
6+-- A mutable version of 'IntMap'
27module Data.FastMutableIntMap
38 ( FastMutableIntMap
49 , new
@@ -32,33 +37,43 @@ import Data.IORef
3237import Reflex.Patch.Class
3338import Reflex.Patch.IntMap
3439
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.
3542newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
3643
44+-- | Create a new 'FastMutableIntMap' out of an 'IntMap'
3745new :: IntMap a -> IO (FastMutableIntMap a)
3846new m = FastMutableIntMap <$> newIORef m
3947
48+-- | Create a new empty 'FastMutableIntMap'
4049newEmpty :: IO (FastMutableIntMap a)
4150newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
4251
52+-- | Insert an element into a 'FastMutableIntMap' at the given key
4353insert :: FastMutableIntMap a -> Int -> a -> IO ()
4454insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
4555
56+-- | Attempt to lookup an element by key in a 'FastMutableIntMap'
4657lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
4758lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
4859
60+-- | Runs the provided action over the intersection of a 'FastMutableIntMap' and an 'IntMap'
4961forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
5062forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
5163 a <- liftIO $ readIORef r
5264 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
5365
66+-- | Runs the provided action over the values of a 'FastMutableIntMap'
5467for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
5568for_ (FastMutableIntMap r) f = do
5669 a <- liftIO $ readIORef r
5770 traverse_ f a
5871
72+-- | Checks whether a 'FastMutableIntMap' is empty
5973isEmpty :: FastMutableIntMap a -> IO Bool
6074isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
6175
76+-- | Retrieves the size of a 'FastMutableIntMap'
6277size :: FastMutableIntMap a -> IO Int
6378size (FastMutableIntMap r) = IntMap.size <$> readIORef r
6479
@@ -69,6 +84,8 @@ getFrozenAndClear (FastMutableIntMap r) = do
6984 writeIORef r IntMap.empty
7085 return result
7186
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.
7289applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
7390applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
7491 v <- readIORef r
src/Data/FastWeakBag.hs+12−7
@@ -18,6 +18,7 @@ module Data.FastWeakBag
1818 , isEmpty
1919 , insert
2020 , traverse
21+ , traverse_
2122 , remove
2223 -- * Internal functions
2324 -- These will not always be available.
@@ -42,15 +43,15 @@ import Data.IORef
4243import System.Mem.Weak
4344#endif
4445
45−-- | A @FastWeakBag a@ holds a set of values of type @a@, but does not retain them -
46−-- that is, they can still be garbage-collected. As long as the @a@s remain
46+-- | A 'FastWeakBag' holds a set of values of type @/a/@, but does not retain them -
47+-- that is, they can still be garbage-collected. As long as the @/a/@ values remain
4748-- alive, the 'FastWeakBag' will continue to refer to them.
4849#ifdef GHCJS_FAST_WEAK
4950newtype FastWeakBag a = FastWeakBag JSVal
5051#else
5152data FastWeakBag a = FastWeakBag
5253 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
54+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
5455 }
5556#endif
5657
@@ -116,14 +117,14 @@ foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.len
116117isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117118#endif
118119
119−{-# INLINE traverse #-}
120+{-# INLINE traverse_ #-}
120121-- | Visit every node in the given list. If new nodes are appended during the
121122-- traversal, they will not be visited. Every live node that was in the list
122123-- when the traversal began will be visited exactly once; however, no guarantee
123124-- is made about the order of the traversal.
124−traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125+traverse_ :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125126#ifdef GHCJS_FAST_WEAK
126−traverse wb f = do
127+traverse_ wb f = do
127128 let go cursor = when (not $ js_isNull cursor) $ do
128129 val <- liftIO $ js_getTicketValue cursor
129130 f $ unsafeFromRawJSVal val
@@ -134,13 +135,17 @@ foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO
134135--TODO: Fix the race condition where if a cursor is deleted (presumably using 'remove', below) while we're holding it, it can't find its way back to the correct bag
135136foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
136137#else
137−traverse (FastWeakBag _ children) f = {-# SCC "traverse" #-} do
138+traverse_ (FastWeakBag _ children) f = {-# SCC "traverse_" #-} do
138139 cs <- liftIO $ readIORef children
139140 forM_ cs $ \c -> do
140141 ma <- liftIO $ deRefWeak c
141142 mapM_ f ma
142143#endif
143144
145+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
146+traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
147+traverse = traverse_
148+
144149-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
145150-- on the same 'FastWeakBagTicket'.
146151{-# INLINE remove #-}
src/Data/Functor/Misc.hs+3−56
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE LambdaCase #-}
98{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE PatternSynonyms #-}
@@ -38,17 +37,10 @@ module Data.Functor.Misc
3837 , dmapToThese
3938 , eitherToDSum
4039 , dsumToEither
41− -- * Deprecated functions
42− , sequenceDmap
43− , wrapDMap
44− , rewrapDMap
45− , unwrapDMap
46− , unwrapDMapMaybe
47− , extractFunctorDMap
4840 , ComposeMaybe (..)
4941 ) where
5042
51−import Control.Applicative (Applicative, (<$>))
43+import Control.Applicative ((<$>))
5244import Control.Monad.Identity
5345import Data.Dependent.Map (DMap)
5446import qualified Data.Dependent.Map as DMap
@@ -59,8 +51,7 @@ import Data.IntMap (IntMap)
5951import qualified Data.IntMap as IntMap
6052import Data.Map (Map)
6153import qualified Data.Map as Map
62−import Data.Some (Some)
63−import qualified Data.Some as Some
54+import Data.Some (Some(Some))
6455import Data.These
6556import Data.Typeable hiding (Refl)
6657
@@ -86,9 +77,6 @@ deriving instance Read k => Read (Const2 k v v)
8677instance Show k => GShow (Const2 k v) where
8778 gshowsPrec n x@(Const2 _) = showsPrec n x
8879
89−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
90− showTaggedPrec (Const2 _) = showsPrec
91−
9280instance Eq k => GEq (Const2 k v) where
9381 geq (Const2 a) (Const2 b) =
9482 if a == b
@@ -131,7 +119,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
131119-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
132120-- the keys, using a function to remove the wrapping 'Functor'
133121weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
134−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f v)) . DMap.toAscList
122+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
135123
136124--------------------------------------------------------------------------------
137125-- WrapArg
@@ -221,11 +209,6 @@ instance GShow (EitherTag l r) where
221209 LeftTag -> showString "LeftTag"
222210 RightTag -> showString "RightTag"
223211
224−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
225− showTaggedPrec t n (Identity a) = case t of
226− LeftTag -> showsPrec n a
227− RightTag -> showsPrec n a
228−
229212-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
230213eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
231214eitherToDSum = \case
@@ -249,39 +232,3 @@ newtype ComposeMaybe f a =
249232 ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
250233
251234deriving 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/Data/Map/Misc.hs+26−10
@@ -1,4 +1,5 @@
11{-# LANGUAGE LambdaCase #-}
2+-- | Additional functions for manipulating 'Map's.
23module Data.Map.Misc
34 (
45 -- * Working with Maps
@@ -10,7 +11,6 @@ module Data.Map.Misc
1011 ) where
1112
1213import Data.Align
13−import Data.Either (isLeft)
1414import Data.Map (Map)
1515import qualified Data.Map as Map
1616import Data.Maybe
@@ -18,12 +18,21 @@ import Data.Set (Set)
1818import qualified Data.Set as Set
1919import Data.These
2020
21+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new@ respectively. @Just@ represents an association present in @new@ and @Nothing@
22+-- represents an association only present in @old@ but no longer present in @new@.
23+--
24+-- Similar to 'diffMap' but doesn't require 'Eq' on the values, thus can't tell if a value has changed or not.
2125diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
2226diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
2327 This _ -> Just Nothing
2428 These _ new -> Just $ Just new
2529 That new -> Just $ Just new
2630
31+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new respectively. @Just@ represents an association present in @new@ and either not
32+-- present in @old@ or where the value has changed. @Nothing@ represents an association only present in @old@ but no longer present in @new@.
33+--
34+-- See also 'diffMapNoEq' for a similar but weaker version which does not require 'Eq' on the values but thus can't indicated a value not changing between
35+-- @old@ and @new@ with @Nothing@.
2736diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
2837diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
2938 This _ -> Just Nothing
@@ -32,23 +41,30 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
3241 | otherwise -> Just $ Just new
3342 That new -> Just $ Just new
3443
44+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new map from the given input @'Map' k v@.
45+--
46+-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
3547applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
3648applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
37− where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
49+ where (deletions, insertions) = Map.mapEither maybeToEither patch
3850 maybeToEither = \case
3951 Nothing -> Left ()
4052 Just r -> Right r
4153
54+-- |Split a @'Map' k (Either a b)@ into @Map k a@ and @Map k b@, equivalent to @'Map.mapEither' id@
55+{-# DEPRECATED mapPartitionEithers "Use 'mapEither' instead" #-}
4256mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
43−mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
44− where (ls, rs) = Map.partition isLeft m
45− fromLeft (Left l) = l
46− fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
47− fromRight (Right r) = r
48− fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
57+mapPartitionEithers = Map.mapEither id
4958
50−-- | Apply a map patch to a set
51−-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
59+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new @'Set' k@ from the given input set.
60+--
61+-- Equivalent to:
62+--
63+-- @
64+-- applyMapKeysSet patch ('Map.keysSet' m) == 'Map.keysSet' ('applyMap' patch m)
65+-- @
66+--
67+-- but avoids the intervening @Map@ and needs no values.
5268applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
5369applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
5470 where (insertions, deletions) = Map.partition isJust patch
src/Data/WeakBag.hs+10−5
@@ -15,6 +15,7 @@ module Data.WeakBag
1515 , singleton
1616 , insert
1717 , traverse
18+ , traverse_
1819 , remove
1920 -- * Internal functions
2021 -- These will not always be available.
@@ -31,8 +32,8 @@ import qualified Data.IntMap.Strict as IntMap
3132import Data.IORef
3233import System.Mem.Weak
3334
34−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
35−-- that is, they can still be garbage-collected. As long as the @a@s remain
35+-- | A 'WeakBag' holds a set of values of type @/a/@, but does not retain them -
36+-- that is, they can still be garbage-collected. As long as the @/a/@ values remain
3637-- alive, the 'WeakBag' will continue to refer to them.
3738data WeakBag a = WeakBag
3839 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
@@ -99,18 +100,22 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99100 ticket <- insert a bag wbRef finalizer
100101 return (bag, ticket)
101102
102−{-# INLINE traverse #-}
103+{-# INLINE traverse_ #-}
103104-- | Visit every node in the given list. If new nodes are appended during the
104105-- traversal, they will not be visited. Every live node that was in the list
105106-- when the traversal began will be visited exactly once; however, no guarantee
106107-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
108+traverse_ :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109+traverse_ (WeakBag _ children) f = {-# SCC "traverse" #-} do
109110 cs <- liftIO $ readIORef children
110111 forM_ cs $ \c -> do
111112 ma <- liftIO $ deRefWeak c
112113 mapM_ f ma
113114
115+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
116+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
117+traverse = traverse_
118+
114119-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115120-- on the same 'WeakBagTicket'.
116121{-# INLINE remove #-}
src/Reflex.hs+3
@@ -7,6 +7,8 @@ module Reflex
77
88import Reflex.Class as X
99import Reflex.Adjustable.Class as X
10+import Reflex.BehaviorWriter.Base as X
11+import Reflex.BehaviorWriter.Class as X
1012import Reflex.Collection as X
1113import Reflex.Dynamic as X
1214import Reflex.EventWriter.Base as X
@@ -17,6 +19,7 @@ import Reflex.Dynamic.TH as X
1719import Reflex.Dynamic.Uniq as X
1820import Reflex.DynamicWriter.Base as X
1921import Reflex.DynamicWriter.Class as X
22+import Reflex.NotReady.Class as X
2023import Reflex.PerformEvent.Base as X
2124import Reflex.PerformEvent.Class as X
2225import Reflex.PostBuild.Base as X
src/Reflex/Adjustable/Class.hs+30−4
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
@@ -11,6 +10,13 @@
1110#ifdef USE_REFLEX_OPTIMIZER
1211{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1312#endif
13+-- |
14+-- Module:
15+-- Reflex.Adjustable.Class
16+-- Description:
17+-- A class for actions that can be "adjusted" over time based on some 'Event'
18+-- such that, when observed after the firing of any such 'Event', the result
19+-- is as though the action was originally run with the 'Event's value.
1420module Reflex.Adjustable.Class
1521 (
1622 -- * The Adjustable typeclass
@@ -94,13 +100,32 @@ instance Adjustable t m => Adjustable t (ReaderT r m) where
94100 r <- ask
95101 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
96102
97−sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
103+-- | Traverse a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
104+-- to the 'DMap' can add, remove, or update values.
105+sequenceDMapWithAdjust
106+ :: (GCompare k, Adjustable t m)
107+ => DMap k m
108+ -> Event t (PatchDMap k m)
109+ -> m (DMap k Identity, Event t (PatchDMap k Identity))
98110sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
99111
100−sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
112+-- | Traverses a 'DMap' of 'Adjustable' actions, running each of them. The provided 'Event' of patches
113+-- to the 'DMap' can add, remove, update, move, or swap values.
114+sequenceDMapWithAdjustWithMove
115+ :: (GCompare k, Adjustable t m)
116+ => DMap k m
117+ -> Event t (PatchDMapWithMove k m)
118+ -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
101119sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
102120
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'))
121+-- | Traverses a 'Map', running the provided 'Adjustable' action. The provided 'Event' of patches to the 'Map'
122+-- can add, remove, update, move, or swap values.
123+mapMapWithAdjustWithMove
124+ :: forall t m k v v'. (Adjustable t m, Ord k)
125+ => (k -> v -> m v')
126+ -> Map k v
127+ -> Event t (PatchMapWithMove k v)
128+ -> m (Map k v', Event t (PatchMapWithMove k v'))
104129mapMapWithAdjustWithMove f m0 m' = do
105130 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
106131 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
@@ -110,4 +135,5 @@ mapMapWithAdjustWithMove f m0 m' = do
110135--------------------------------------------------------------------------------
111136
112137{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
138+-- | Synonym for 'Adjustable'
113139type MonadAdjust = Adjustable
src/Reflex/BehaviorWriter/Base.hsnew file+202
@@ -0,0 +1,202 @@
1+{-|
2+Module: Reflex.BehaviorWriter.Base
3+Description: Implementation of MonadBehaviorWriter
4+-}
5+{-# LANGUAGE CPP #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+{-# LANGUAGE StandaloneDeriving #-}
15+#ifdef USE_REFLEX_OPTIMIZER
16+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
17+#endif
18+module Reflex.BehaviorWriter.Base
19+ ( BehaviorWriterT (..)
20+ , runBehaviorWriterT
21+ , withBehaviorWriterT
22+ ) where
23+
24+import Control.Monad.Exception
25+import Control.Monad.Identity
26+import Control.Monad.IO.Class
27+import Control.Monad.Reader
28+import Control.Monad.Ref
29+import Control.Monad.State.Strict
30+import Data.Dependent.Map (DMap)
31+import qualified Data.Dependent.Map as DMap
32+import Data.Functor.Misc
33+import Data.IntMap (IntMap)
34+import qualified Data.IntMap as IntMap
35+import Data.Map (Map)
36+import qualified Data.Map as Map
37+import Data.Some (Some)
38+
39+import Reflex.Class
40+import Reflex.Adjustable.Class
41+import Reflex.BehaviorWriter.Class
42+import Reflex.Host.Class
43+import Reflex.PerformEvent.Class
44+import Reflex.PostBuild.Class
45+import Reflex.Query.Class
46+import Reflex.Requester.Class
47+import Reflex.TriggerEvent.Class
48+
49+-- | A basic implementation of 'MonadBehaviorWriter'.
50+newtype BehaviorWriterT t w m a = BehaviorWriterT { unBehaviorWriterT :: StateT [Behavior t w] m a }
51+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
52+
53+-- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided
54+-- along with the result of the action.
55+runBehaviorWriterT :: (Monad m, Reflex t, Monoid w) => BehaviorWriterT t w m a -> m (a, Behavior t w)
56+runBehaviorWriterT (BehaviorWriterT a) = do
57+ (result, ws) <- runStateT a []
58+ return (result, mconcat $ reverse ws)
59+
60+-- | Map a function over the output of a 'BehaviorWriterT'.
61+withBehaviorWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m)
62+ => (w -> w')
63+ -> BehaviorWriterT t w m a
64+ -> BehaviorWriterT t w' m a
65+withBehaviorWriterT f dw = do
66+ (r, d) <- lift $ do
67+ (r, d) <- runBehaviorWriterT dw
68+ let d' = fmap f d
69+ return (r, d')
70+ tellBehavior d
71+ return r
72+
73+deriving instance MonadHold t m => MonadHold t (BehaviorWriterT t w m)
74+deriving instance MonadSample t m => MonadSample t (BehaviorWriterT t w m)
75+
76+instance MonadTrans (BehaviorWriterT t w) where
77+ lift = BehaviorWriterT . lift
78+
79+instance MonadRef m => MonadRef (BehaviorWriterT t w m) where
80+ type Ref (BehaviorWriterT t w m) = Ref m
81+ newRef = lift . newRef
82+ readRef = lift . readRef
83+ writeRef r = lift . writeRef r
84+
85+instance MonadAtomicRef m => MonadAtomicRef (BehaviorWriterT t w m) where
86+ atomicModifyRef r = lift . atomicModifyRef r
87+
88+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (BehaviorWriterT t w m) where
89+ newEventWithTrigger = lift . newEventWithTrigger
90+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
91+
92+instance (Monad m, Monoid w, Reflex t) => MonadBehaviorWriter t w (BehaviorWriterT t w m) where
93+ tellBehavior w = BehaviorWriterT $ modify (w :)
94+
95+instance MonadReader r m => MonadReader r (BehaviorWriterT t w m) where
96+ ask = lift ask
97+ local f (BehaviorWriterT a) = BehaviorWriterT $ mapStateT (local f) a
98+ reader = lift . reader
99+
100+instance PerformEvent t m => PerformEvent t (BehaviorWriterT t w m) where
101+ type Performable (BehaviorWriterT t w m) = Performable m
102+ performEvent_ = lift . performEvent_
103+ performEvent = lift . performEvent
104+
105+instance TriggerEvent t m => TriggerEvent t (BehaviorWriterT t w m) where
106+ newTriggerEvent = lift newTriggerEvent
107+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
108+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
109+
110+instance PostBuild t m => PostBuild t (BehaviorWriterT t w m) where
111+ getPostBuild = lift getPostBuild
112+
113+instance MonadState s m => MonadState s (BehaviorWriterT t w m) where
114+ get = lift get
115+ put = lift . put
116+
117+instance Requester t m => Requester t (BehaviorWriterT t w m) where
118+ type Request (BehaviorWriterT t w m) = Request m
119+ type Response (BehaviorWriterT t w m) = Response m
120+ requesting = lift . requesting
121+ requesting_ = lift . requesting_
122+
123+instance (MonadQuery t q m, Monad m) => MonadQuery t q (BehaviorWriterT t w m) where
124+ tellQueryIncremental = lift . tellQueryIncremental
125+ askQueryResult = lift askQueryResult
126+ queryIncremental = lift . queryIncremental
127+
128+instance (Adjustable t m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (BehaviorWriterT t w m) where
129+ runWithReplace a0 a' = do
130+ (result0, result') <- lift $ runWithReplace (runBehaviorWriterT a0) $ runBehaviorWriterT <$> a'
131+ tellBehavior . join =<< hold (snd result0) (snd <$> result')
132+ return (fst result0, fst <$> result')
133+ traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust
134+ traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith
135+
136+traverseIntMapWithKeyWithAdjustImpl
137+ :: forall t w v' p p' v m.
138+ ( PatchTarget (p' (Behavior t w)) ~ IntMap (Behavior t w)
139+ , Patch (p' (Behavior t w))
140+ , Monoid w
141+ , Reflex t
142+ , MonadHold t m
143+ , Functor p
144+ , p ~ p'
145+ )
146+ => ( (IntMap.Key -> v -> m (v', Behavior t w))
147+ -> IntMap v
148+ -> Event t (p v)
149+ -> m (IntMap (v', Behavior t w), Event t (p (v', Behavior t w)))
150+ )
151+ -> (IntMap.Key -> v -> BehaviorWriterT t w m v')
152+ -> IntMap v
153+ -> Event t (p v)
154+ -> BehaviorWriterT t w m (IntMap v', Event t (p v'))
155+traverseIntMapWithKeyWithAdjustImpl base f (dm0 :: IntMap v) dm' = do
156+ (result0, result') <- lift $ base (\k v -> runBehaviorWriterT $ f k v) dm0 dm'
157+ let liftedResult0 = fmap fst result0
158+ liftedResult' = fmap (fmap fst) result'
159+ liftedWritten0 :: IntMap (Behavior t w)
160+ liftedWritten0 = fmap snd result0
161+ liftedWritten' = fmap (fmap snd) result'
162+ i <- holdIncremental liftedWritten0 liftedWritten'
163+ tellBehavior $ pull $ do
164+ m <- sample $ currentIncremental i
165+ mconcat . IntMap.elems <$> traverse sample m
166+ return (liftedResult0, liftedResult')
167+
168+newtype BehaviorWriterTLoweredResult t w v a = BehaviorWriterTLoweredResult (v a, Behavior t w)
169+
170+traverseDMapWithKeyWithAdjustImpl
171+ :: forall t w k v' p p' v m.
172+ ( PatchTarget (p' (Some k) (Behavior t w)) ~ Map (Some k) (Behavior t w)
173+ , Patch (p' (Some k) (Behavior t w))
174+ , Monoid w
175+ , Reflex t
176+ , MonadHold t m
177+ )
178+ => ( (forall a. k a -> v a -> m (BehaviorWriterTLoweredResult t w v' a))
179+ -> DMap k v
180+ -> Event t (p k v)
181+ -> m (DMap k (BehaviorWriterTLoweredResult t w v'), Event t (p k (BehaviorWriterTLoweredResult t w v')))
182+ )
183+ -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> v' a) -> p k (BehaviorWriterTLoweredResult t w v') -> p k v')
184+ -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> Behavior t w) -> p k (BehaviorWriterTLoweredResult t w v') -> p' (Some k) (Behavior t w))
185+ -> (forall a. k a -> v a -> BehaviorWriterT t w m (v' a))
186+ -> DMap k v
187+ -> Event t (p k v)
188+ -> BehaviorWriterT t w m (DMap k v', Event t (p k v'))
189+traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith f (dm0 :: DMap k v) dm' = do
190+ (result0, result') <- lift $ base (\k v -> fmap BehaviorWriterTLoweredResult $ runBehaviorWriterT $ f k v) dm0 dm'
191+ let getValue (BehaviorWriterTLoweredResult (v, _)) = v
192+ getWritten (BehaviorWriterTLoweredResult (_, w)) = w
193+ liftedResult0 = DMap.map getValue result0
194+ liftedResult' = ffor result' $ mapPatch getValue
195+ liftedWritten0 :: Map (Some k) (Behavior t w)
196+ liftedWritten0 = weakenDMapWith getWritten result0
197+ liftedWritten' = ffor result' $ weakenPatchWith getWritten
198+ i <- holdIncremental liftedWritten0 liftedWritten'
199+ tellBehavior $ pull $ do
200+ m <- sample $ currentIncremental i
201+ mconcat . Map.elems <$> traverse sample m
202+ return (liftedResult0, liftedResult')
src/Reflex/BehaviorWriter/Class.hsnew file+25
@@ -0,0 +1,25 @@
1+{-|
2+Module: Reflex.BehaviorWriter.Class
3+Description: This module defines the 'MonadBehaviorWriter' class
4+-}
5+{-# LANGUAGE CPP #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+#ifdef USE_REFLEX_OPTIMIZER
10+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11+#endif
12+module Reflex.BehaviorWriter.Class
13+ ( MonadBehaviorWriter (..)
14+ ) where
15+
16+import Control.Monad.Reader (ReaderT, lift)
17+import Reflex.Class (Behavior)
18+
19+-- | 'MonadBehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
20+-- and combines them monoidally to provide a 'Behavior' result.
21+class (Monad m, Monoid w) => MonadBehaviorWriter t w m | m -> t w where
22+ tellBehavior :: Behavior t w -> m ()
23+
24+instance MonadBehaviorWriter t w m => MonadBehaviorWriter t w (ReaderT r m) where
25+ tellBehavior = lift . tellBehavior
src/Reflex/Class.hs+506−132
@@ -7,7 +7,6 @@
77{-# LANGUAGE FunctionalDependencies #-}
88{-# LANGUAGE GADTs #-}
99{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
1110{-# LANGUAGE NoMonomorphismRestriction #-}
1211{-# LANGUAGE PolyKinds #-}
1312{-# LANGUAGE RankNTypes #-}
@@ -17,13 +16,18 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
2020#ifdef USE_REFLEX_OPTIMIZER
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
@@ -32,27 +36,36 @@ module Reflex.Class
3236 , coerceBehavior
3337 , coerceEvent
3438 , coerceDynamic
39+ , coerceIncremental
3540 , MonadSample (..)
3641 , MonadHold (..)
37− -- ** 'fan'-related types
42+ -- ** 'fan' related types
3843 , EventSelector (..)
44+ , EventSelectorG (..)
3945 , EventSelectorInt (..)
40− -- ** 'Incremental'-related types
4146 -- * Convenience functions
4247 , constDyn
4348 , pushAlways
4449 -- ** Combining 'Event's
4550 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4654 , mergeMap
55+ , mergeIntMap
4756 , mergeMapIncremental
4857 , mergeMapIncrementalWithMove
4958 , mergeIntMapIncremental
59+ , coincidencePatchMap
60+ , coincidencePatchMapWithMove
61+ , coincidencePatchIntMap
5062 , mergeList
5163 , mergeWith
5264 , difference
5365 , alignEventWithMaybe
5466 -- ** Breaking up 'Event's
5567 , splitE
68+ , fan
5669 , fanEither
5770 , fanThese
5871 , fanMap
@@ -67,6 +80,7 @@ module Reflex.Class
6780 , switchHold
6881 , switchHoldPromptly
6982 , switchHoldPromptOnly
83+ , switchHoldPromptOnlyIncremental
7084 -- ** Using 'Event's to sample 'Behavior's
7185 , tag
7286 , tagMaybe
@@ -77,6 +91,7 @@ module Reflex.Class
7791 , gate
7892 -- ** Combining 'Dynamic's
7993 , distributeDMapOverDynPure
94+ , distributeDMapOverDynPureG
8095 , distributeListOverDyn
8196 , distributeListOverDynWith
8297 , zipDyn
@@ -130,8 +145,10 @@ module Reflex.Class
130145 -- * Unsafe functions
131146 , unsafeDynamic
132147 , unsafeMapIncremental
133− -- * 'FunctorMaybe'
134− , FunctorMaybe (..)
148+ -- * 'Filterable' convenience functions
149+ , FunctorMaybe -- fmapMaybe is purposely not exported from deprecated 'FunctorMaybe' and the new alias is exported instead
150+ , mapMaybe -- Re-exported for convenience
151+ , fmapMaybe
135152 , fforMaybe
136153 , ffilter
137154 , filterLeft
@@ -141,10 +158,11 @@ module Reflex.Class
141158 , ffor2
142159 , ffor3
143160 -- * Deprecated functions
144− , appendEvents
145− , onceE
146− , sequenceThese
161+ , switchPromptly
162+ , switchPromptOnly
163+ -- * "Cheap" functions
147164 , fmapMaybeCheap
165+ , mapMaybeCheap
148166 , fmapCheap
149167 , fforCheap
150168 , fforMaybeCheap
@@ -152,12 +170,21 @@ module Reflex.Class
152170 , tagCheap
153171 , mergeWithCheap
154172 , mergeWithCheap'
155− , switchPromptly
156− , switchPromptOnly
157173 -- * Slow, but general, implementations
158174 , slowHeadE
159175 ) where
160176
177+#ifdef MIN_VERSION_semialign
178+import Prelude hiding (zip, zipWith)
179+
180+#if MIN_VERSION_these(0,8,0)
181+import Data.These.Combinators (justThese)
182+#endif
183+#if MIN_VERSION_semialign(1,1,0)
184+import Data.Zip (Zip (..))
185+#endif
186+#endif
187+
161188import Control.Applicative
162189import Control.Monad.Identity
163190import Control.Monad.Reader
@@ -185,18 +212,21 @@ import qualified Data.IntMap.Strict as IntMap
185212import Data.List.NonEmpty (NonEmpty (..))
186213import Data.Map (Map)
187214import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
188−import Data.Some (Some)
189−import qualified Data.Some as Some
215+import Data.Some (Some(Some))
190216import Data.String
191217import Data.These
192218import Data.Type.Coercion
193−import Reflex.FunctorMaybe
219+import Data.Witherable (Filterable(..))
220+import qualified Data.Witherable as W
221+import Reflex.FunctorMaybe (FunctorMaybe)
222+import qualified Reflex.FunctorMaybe
194223import Reflex.Patch
224+import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
195225
196226import Debug.Trace (trace)
197227
198228-- | The 'Reflex' class contains all the primitive functionality needed for
199−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
229+-- Functional Reactive Programming (FRP). The @/t/@ type parameter indicates
200230-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
201231-- and the type of the timeline determines the FRP engine to be used. For most
202232-- purposes, the 'Reflex.Spider' implementation is recommended.
@@ -244,16 +274,20 @@ class ( MonadHold t (PushM t)
244274 -- | Merge a collection of events; the resulting 'Event' will only occur if at
245275 -- least one input event is occurring, and will contain all of the input keys
246276 -- that are occurring simultaneously
247− merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
248− -- | Efficiently fan-out an event to many destinations. This function should
249− -- be partially applied, and then the result applied repeatedly to create
250− -- child events
251− fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
277+
278+ --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
279+ mergeG :: GCompare k => (forall a. q a -> Event t (v a))
280+ -> DMap k q -> Event t (DMap k v)
281+
282+ -- | Efficiently fan-out an event to many destinations. You should save the
283+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
284+ -- create child events
285+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
286+
252287 -- | Create an 'Event' that will occur whenever the currently-selected input
253288 -- 'Event' occurs
254289 switch :: Behavior t (Event t a) -> Event t a
255− -- | Create an 'Event' that will occur whenever the input event is occurring
256− -- and its occurrence value, another 'Event', is also occurring
290+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring
257291 coincidence :: Event t (Event t a) -> Event t a
258292 -- | Extract the 'Behavior' of a 'Dynamic'.
259293 current :: Dynamic t a -> Behavior t a
@@ -262,14 +296,19 @@ class ( MonadHold t (PushM t)
262296 -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
263297 -- recent firing of the given 'Event', if any.
264298 unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
265− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
299+ -- | Create a new 'Incremental'. The given "PullM"'s value must always change
266300 -- in the same way that the accumulated application of patches would change
267301 -- that value.
268302 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
269303 -- | Create a merge whose parents can change over time
270− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
304+ mergeIncrementalG :: GCompare k
305+ => (forall a. q a -> Event t (v a))
306+ -> Incremental t (PatchDMap k q)
307+ -> Event t (DMap k v)
271308 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
272− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
309+ mergeIncrementalWithMoveG :: GCompare k
310+ => (forall a. q a -> Event t (v a))
311+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
273312 -- | Extract the 'Behavior' component of an 'Incremental'
274313 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
275314 -- | Extract the 'Event' component of an 'Incremental'
@@ -285,10 +324,28 @@ class ( MonadHold t (PushM t)
285324 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
286325 -- occurrence type
287326 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
327+ -- | Construct a 'Coercion' for an 'Incremental' given 'Coercion's for its
328+ -- patch target and patch types.
329+ incrementalCoercion
330+ :: Coercion (PatchTarget a) (PatchTarget b) -> Coercion a b -> Coercion (Incremental t a) (Incremental t b)
288331 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
289332 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
290333
334+-- | Efficiently fan-out an event to many destinations. You should save the
335+-- result in a @let@-binding, and then repeatedly 'select' on the result to
336+-- create child events
337+fan :: forall t k. (Reflex t, GCompare k)
338+ => Event t (DMap k Identity) -> EventSelector t k
339+ --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
340+fan e = EventSelector (fixup (selectG (fanG e) :: k a -> Event t (Identity a)) :: forall a. k a -> Event t a)
341+ where
342+ fixup :: forall a. (k a -> Event t (Identity a)) -> k a -> Event t a
343+ fixup = case eventCoercion Coercion :: Coercion (Event t (Identity a)) (Event t a) of
344+ Coercion -> coerce
345+
291346--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
347+-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
348+-- keys simultaneously.
292349mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
293350mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
294351
@@ -304,8 +361,14 @@ coerceEvent = coerceWith $ eventCoercion Coercion
304361coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
305362coerceDynamic = coerceWith $ dynamicCoercion Coercion
306363
364+-- | Coerce an 'Incremental' between representationally-equivalent value types
365+coerceIncremental
366+ :: (Reflex t, Coercible a b, Coercible (PatchTarget a) (PatchTarget b))
367+ => Incremental t a -> Incremental t b
368+coerceIncremental = coerceWith $ incrementalCoercion Coercion Coercion
369+
307370-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
308−-- _must_ change when and only when the 'Event' fires, such that the
371+-- __must__ change when and only when the 'Event' fires, such that the
309372-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
310373-- if this is not the case, the resulting 'Dynamic' will behave
311374-- nondeterministically.
@@ -333,7 +396,7 @@ class MonadSample t m => MonadHold t m where
333396 -- value and will be updated whenever the given 'Event' occurs. The update
334397 -- takes effect immediately after the 'Event' occurs; if the occurrence that
335398 -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
336− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
399+ -- the 'Behavior', it will see the __old__ value of the 'Behavior', not the new
337400 -- one.
338401 hold :: a -> Event t a -> m (Behavior t a)
339402 default hold :: (m ~ f m', MonadTrans f, MonadHold t m') => a -> Event t a -> m (Behavior t a)
@@ -357,26 +420,91 @@ class MonadSample t m => MonadHold t m where
357420 -- the supplied 'Event'.
358421 headE :: Event t a -> m (Event t a)
359422
360−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
423+-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
424+-- using the combining function to produce a patch.
425+accumIncremental
426+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
427+ => (PatchTarget p -> b -> p)
428+ -> PatchTarget p
429+ -> Event t b
430+ -> m (Incremental t p)
361431accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
362−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)
432+
433+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
434+accumMIncremental
435+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
436+ => (PatchTarget p -> b -> PushM t p)
437+ -> PatchTarget p
438+ -> Event t b
439+ -> m (Incremental t p)
363440accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
364−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
441+
442+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
443+-- combining function produces @Nothing@)
444+accumMaybeIncremental
445+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
446+ => (PatchTarget p -> b -> Maybe p)
447+ -> PatchTarget p
448+ -> Event t b
449+ -> m (Incremental t p)
365450accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
366−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)
451+
452+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
453+accumMaybeMIncremental
454+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
455+ => (PatchTarget p -> b -> PushM t (Maybe p))
456+ -> PatchTarget p
457+ -> Event t b
458+ -> m (Incremental t p)
367459accumMaybeMIncremental f z e = do
368460 rec let e' = flip push e $ \o -> do
369461 v <- sample $ currentIncremental d'
370462 f v o
371463 d' <- holdIncremental z e'
372464 return d'
373−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)
465+
466+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event'
467+-- with a function that both accumulates and produces a value to fire
468+-- as an 'Event'. Returns both the accumulated value and the constructed
469+-- 'Event'.
470+mapAccumIncremental
471+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
472+ => (PatchTarget p -> b -> (p, c))
473+ -> PatchTarget p
474+ -> Event t b
475+ -> m (Incremental t p, Event t c)
374476mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
375−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)
477+
478+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
479+mapAccumMIncremental
480+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
481+ => (PatchTarget p -> b -> PushM t (p, c))
482+ -> PatchTarget p
483+ -> Event t b
484+ -> m (Incremental t p, Event t c)
376485mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
377−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)
486+
487+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event' with
488+-- a function that both optionally accumulates and optionally produces
489+-- a value to fire as a separate output 'Event'.
490+-- Note that because 'Nothing's are discarded in both cases, the output
491+-- 'Event' may fire even though the output 'Incremental' has not changed, and
492+-- the output 'Incremental' may update even when the output 'Event' is not firing.
493+mapAccumMaybeIncremental
494+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
495+ => (PatchTarget p -> b -> (Maybe p, Maybe c))
496+ -> PatchTarget p
497+ -> Event t b
498+ -> m (Incremental t p, Event t c)
378499mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
379−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)
500+
501+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
502+mapAccumMaybeMIncremental
503+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
504+ => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c))
505+ -> PatchTarget p
506+ -> Event t b
507+ -> m (Incremental t p, Event t c)
380508mapAccumMaybeMIncremental f z e = do
381509 rec let e' = flip push e $ \o -> do
382510 v <- sample $ currentIncremental d'
@@ -384,9 +512,10 @@ mapAccumMaybeMIncremental f z e = do
384512 return $ case result of
385513 (Nothing, Nothing) -> Nothing
386514 _ -> Just result
387− d' <- holdIncremental z $ fmapMaybe fst e'
388− return (d', fmapMaybe snd e')
515+ d' <- holdIncremental z $ mapMaybe fst e'
516+ return (d', mapMaybe snd e')
389517
518+-- | A somewhat slow implementation of 'headE'
390519slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
391520slowHeadE e = do
392521 rec be <- hold e $ fmapCheap (const never) e'
@@ -395,18 +524,31 @@ slowHeadE e = do
395524
396525-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
397526-- key. This is much more efficient than filtering for each key with
398−-- 'fmapMaybe'.
527+-- 'mapMaybe'.
399528newtype EventSelector t k = EventSelector
400529 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
401530 -- determined by the type of the key, so this can be used to fan-out
402531 -- 'Event's whose sub-'Event's have different types.
403532 --
404533 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
405− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
534+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
406535 -- occurrences of an 'Event'.
407536 select :: forall a. k a -> Event t a
408537 }
409538
539+newtype EventSelectorG t k v = EventSelectorG
540+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
541+ -- determined by the type of the key, so this can be used to fan-out
542+ -- 'Event's whose sub-'Event's have different types.
543+ --
544+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
545+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
546+ -- occurrences of an 'Event'.
547+ selectG :: forall a. k a -> Event t (v a)
548+ }
549+
550+-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
551+-- filtering by key.
410552newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
411553
412554--------------------------------------------------------------------------------
@@ -537,40 +679,51 @@ instance (Reflex t, Num a) => Num (Behavior t a) where
537679 negate = fmap negate
538680 signum = fmap signum
539681
682+instance (Num a, Reflex t) => Num (Dynamic t a) where
683+ (+) = liftA2 (+)
684+ (*) = liftA2 (*)
685+ abs = fmap abs
686+ signum = fmap signum
687+ fromInteger = pure . fromInteger
688+ negate = fmap negate
689+ (-) = liftA2 (-)
690+
540691instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
541692 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
542693 sconcat = pull . fmap sconcat . mapM sample
543−#if MIN_VERSION_semigroups(0,17,0)
544694 stimes n = fmap $ stimes n
545−#else
546− times1p n = fmap $ times1p n
547−#endif
548695
549−-- | Flipped version of 'fmapMaybe'.
550−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
551−fforMaybe = flip fmapMaybe
696+-- | Alias for 'mapMaybe'
697+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
698+fmapMaybe = mapMaybe
699+
700+-- | Flipped version of 'mapMaybe'.
701+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b
702+fforMaybe = flip mapMaybe
552703
553704-- | Filter 'f a' using the provided predicate.
554−-- Relies on 'fforMaybe'.
555−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
556−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
705+ffilter :: Filterable f => (a -> Bool) -> f a -> f a
706+ffilter = W.filter
557707
558708-- | Filter 'Left's from 'f (Either a b)' into 'a'.
559−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
560−filterLeft = fmapMaybe (either Just (const Nothing))
709+filterLeft :: Filterable f => f (Either a b) -> f a
710+filterLeft = mapMaybe (either Just (const Nothing))
561711
562712-- | Filter 'Right's from 'f (Either a b)' into 'b'.
563−filterRight :: FunctorMaybe f => f (Either a b) -> f b
564−filterRight = fmapMaybe (either (const Nothing) Just)
713+filterRight :: Filterable f => f (Either a b) -> f b
714+filterRight = mapMaybe (either (const Nothing) Just)
565715
566716-- | Left-biased event union (prefers left event on simultaneous
567717-- occurrence).
568718instance Reflex t => Alt (Event t) where
569719 ev1 <!> ev2 = leftmost [ev1, ev2]
570720
571−-- | 'Event' intersection (convenient interface to 'coincidence').
721+-- | 'Event' intersection. Only occurs when both events are co-incident.
572722instance Reflex t => Apply (Event t) where
573− evf <.> evx = coincidence (fmap (<$> evx) evf)
723+ evf <.> evx = mapMaybe f (align evf evx) where
724+ f (These g a) = Just (g a)
725+ f _ = Nothing
726+
574727
575728-- | 'Event' intersection (convenient interface to 'coincidence').
576729instance Reflex t => Bind (Event t) where
@@ -579,13 +732,18 @@ instance Reflex t => Bind (Event t) where
579732
580733instance Reflex t => Functor (Event t) where
581734 {-# INLINE fmap #-}
582− fmap f = fmapMaybe $ Just . f
735+ fmap f = mapMaybe $ Just . f
583736 {-# INLINE (<$) #-}
584737 x <$ e = fmapCheap (const x) e
585738
739+-- TODO Remove this instance
586740instance Reflex t => FunctorMaybe (Event t) where
587741 {-# INLINE fmapMaybe #-}
588− fmapMaybe f = push $ return . f
742+ fmapMaybe = mapMaybe
743+
744+instance Reflex t => Filterable (Event t) where
745+ {-# INLINE mapMaybe #-}
746+ mapMaybe f = push $ return . f
589747
590748-- | Never: @'zero' = 'never'@.
591749instance Reflex t => Plus (Event t) where
@@ -636,7 +794,7 @@ headTailE e = do
636794-- event returns 'True'.
637795--
638796-- Starting at the current time, fire all the occurrences of the 'Event' for
639−-- which the given predicate returns 'True'. When 'False' first is returned,
797+-- which the given predicate returns 'True'. When first 'False' is returned,
640798-- do not fire, and permanently stop firing, even if 'True' values would have
641799-- been encountered later.
642800takeWhileE
@@ -651,7 +809,7 @@ takeWhileE f = takeWhileJustE $ \v -> guard (f v) $> v
651809-- event returns 'Just b'.
652810--
653811-- Starting at the current time, fire all the occurrences of the 'Event' for
654−-- which the given predicate returns 'Just b'. When 'Nothing' first is returned,
812+-- which the given predicate returns 'Just b'. When first 'Nothing' is returned,
655813-- do not fire, and permanently stop firing, even if 'Just b' values would have
656814-- been encountered later.
657815takeWhileJustE
@@ -734,11 +892,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
734892instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
735893 (<>) = alignWith (mergeThese (<>))
736894 sconcat = fmap sconcat . mergeList . toList
737−#if MIN_VERSION_semigroups(0,17,0)
738895 stimes n = fmap $ stimes n
739−#else
740− times1p n = fmap $ times1p n
741−#endif
742896
743897instance (Semigroup a, Reflex t) => Monoid (Event t a) where
744898 mempty = never
@@ -773,7 +927,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
773927mergeList [] = never
774928mergeList es = mergeWithFoldCheap' id es
775929
776−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
930+unsafeMapIncremental
931+ :: (Reflex t, Patch p, Patch p')
932+ => (PatchTarget p -> PatchTarget p')
933+ -> (p -> p')
934+ -> Incremental t p
935+ -> Incremental t p'
777936unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
778937
779938-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
@@ -782,6 +941,10 @@ unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIn
782941mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
783942mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
784943
944+-- | Like 'mergeMap' but for 'IntMap'.
945+mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
946+mergeIntMap = fmap dmapToIntMap . merge . intMapWithFunctorToDMap
947+
785948-- | Create a merge whose parents can change over time
786949mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
787950mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
@@ -799,7 +962,7 @@ fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
799962fanEither e =
800963 let justLeft = either Just (const Nothing)
801964 justRight = either (const Nothing) Just
802− in (fmapMaybe justLeft e, fmapMaybe justRight e)
965+ in (mapMaybe justLeft e, mapMaybe justRight e)
803966
804967-- | Split the event into separate events for 'This' and 'That' values,
805968-- allowing them to fire simultaneously when the input value is 'These'.
@@ -811,7 +974,7 @@ fanThese e =
811974 that (That y) = Just y
812975 that (These _ y) = Just y
813976 that _ = Nothing
814− in (fmapMaybe this e, fmapMaybe that e)
977+ in (mapMaybe this e, mapMaybe that e)
815978
816979-- | Split the event into an 'EventSelector' that allows efficient selection of
817980-- the individual 'Event's.
@@ -824,7 +987,7 @@ eventJoin = switchHold never
824987
825988-- | Switches to the new event whenever it receives one. Only the old event is
826989-- considered the moment a new one is switched in; the output event will fire at
827−-- that moment if only if the old event does.
990+-- that moment only if the old event does.
828991--
829992-- Because the simultaneous firing case is irrelevant, this function imposes
830993-- laxer "timing requirements" on the overall circuit, avoiding many potential
@@ -864,17 +1027,79 @@ switchHoldPromptOnly e0 e' = do
8641027 eLag <- switch <$> hold e0 e'
8651028 return $ coincidence $ leftmost [e', eLag <$ eLag]
8661029
1030+-- | When the given outer event fires, condense the inner events into the contained patch. Non-firing inner events will be replaced with deletions.
1031+coincidencePatchMap :: (Reflex t, Ord k) => Event t (PatchMap k (Event t v)) -> Event t (PatchMap k v)
1032+coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ ffor e $ \(PatchMap m) -> mergeMap $ ffor m $ \case
1033+ Nothing -> fmapCheap (const Nothing) e
1034+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1035+
1036+-- | See 'coincidencePatchMap'
1037+coincidencePatchIntMap :: Reflex t => Event t (PatchIntMap (Event t v)) -> Event t (PatchIntMap v)
1038+coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ ffor e $ \(PatchIntMap m) -> mergeIntMap $ ffor m $ \case
1039+ Nothing -> fmapCheap (const Nothing) e
1040+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1041+
1042+-- | See 'coincidencePatchMap'
1043+coincidencePatchMapWithMove :: (Reflex t, Ord k) => Event t (PatchMapWithMove k (Event t v)) -> Event t (PatchMapWithMove k v)
1044+coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ ffor e $ \p -> mergeMap $ ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1045+ PatchMapWithMove.From_Delete -> fforCheap e $ \_ ->
1046+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1047+ PatchMapWithMove.From_Move k -> fforCheap e $ \_ ->
1048+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1049+ PatchMapWithMove.From_Insert ev -> leftmost
1050+ [ fforCheap ev $ \v ->
1051+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1052+ , fforCheap e $ \_ ->
1053+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1054+ ]
1055+
1056+-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
1057+-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
1058+-- fires with the patched value.
1059+switchHoldPromptOnlyIncremental
1060+ :: forall t m p pt w
1061+ . ( Reflex t
1062+ , MonadHold t m
1063+ , Patch (p (Event t w))
1064+ , PatchTarget (p (Event t w)) ~ pt (Event t w)
1065+ , Patch (p w)
1066+ , PatchTarget (p w) ~ pt w
1067+ , Monoid (pt w)
1068+ )
1069+ => (Incremental t (p (Event t w)) -> Event t (pt w))
1070+ -> (Event t (p (Event t w)) -> Event t (p w))
1071+ -> pt (Event t w)
1072+ -> Event t (p (Event t w))
1073+ -> m (Event t (pt w))
1074+switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = do
1075+ lag <- mergePatchIncremental <$> holdIncremental e0 e'
1076+ pure $ ffor (align lag (coincidencePatch e')) $ \case
1077+ This old -> old
1078+ That new -> new `applyAlways` mempty
1079+ These old new -> new `applyAlways` old
1080+
8671081instance Reflex t => Align (Event t) where
8681082 nil = never
1083+#if MIN_VERSION_these(0, 8, 0)
1084+instance Reflex t => Semialign (Event t) where
1085+#endif
8691086 align = alignEventWithMaybe Just
8701087
1088+#ifdef MIN_VERSION_semialign
1089+#if MIN_VERSION_semialign(1,1,0)
1090+instance Reflex t => Zip (Event t) where
1091+#endif
1092+ zip x y = mapMaybe justThese $ align x y
1093+#endif
1094+
1095+
8711096-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
8721097-- the 'Behavior' is true at the time of occurrence.
8731098gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
8741099gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
8751100
876−-- | Create a new behavior given a starting behavior and switch to a the behvior
877−-- carried by the event when it fires.
1101+-- | Create a new behavior given a starting behavior and switch to the behavior
1102+-- carried by the event when it fires.
8781103switcher :: (Reflex t, MonadHold t m)
8791104 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
8801105switcher b eb = pull . (sample <=< sample) <$> hold b eb
@@ -889,7 +1114,7 @@ zipDyn = zipDynWith (,)
8891114
8901115-- | Combine two 'Dynamic's with a combining function. The result will change
8911116-- whenever either (or both) input 'Dynamic' changes.
892−-- More efficient than liftA2.
1117+-- More efficient than 'liftA2'.
8931118zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
8941119zipDynWith f da db =
8951120 let eab = align (updated da) (updated db)
@@ -907,11 +1132,7 @@ zipDynWith f da db =
9071132
9081133instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
9091134 (<>) = zipDynWith (<>)
910−#if MIN_VERSION_semigroups(0,17,0)
9111135 stimes n = fmap $ stimes n
912−#else
913− times1p n = fmap $ times1p n
914−#endif
9151136
9161137instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
9171138 mconcat = distributeListOverDynWith mconcat
@@ -920,14 +1141,23 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
9201141
9211142-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
9221143-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
923−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
1144+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
9241145distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
925−distributeDMapOverDynPure dm = case DMap.toList dm of
1146+distributeDMapOverDynPure = distributeDMapOverDynPureG coerceDynamic
1147+
1148+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1149+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1150+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1151+distributeDMapOverDynPureG
1152+ :: forall t k q v. (Reflex t, GCompare k)
1153+ => (forall a. q a -> Dynamic t (v a))
1154+ -> DMap k q -> Dynamic t (DMap k v)
1155+distributeDMapOverDynPureG nt dm = case DMap.toList dm of
9261156 [] -> constDyn DMap.empty
927− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1157+ [k :=> v] -> DMap.singleton k <$> nt v
9281158 _ ->
929− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
930− edmPre = merge $ DMap.map updated dm
1159+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
1160+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
9311161 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
9321162 olds <- sample $ current result
9331163 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -939,21 +1169,32 @@ distributeListOverDyn = distributeListOverDynWith id
9391169
9401170-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
9411171distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
942−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
1172+distributeListOverDynWith f =
1173+ fmap (f . map fromDSum . DMap.toAscList) .
1174+ distributeDMapOverDynPure .
1175+ DMap.fromDistinctAscList .
1176+ zipWith toDSum [0..]
1177+ where
1178+ toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
1179+ toDSum k v = Const2 k :=> v
1180+ fromDSum :: DSum (Const2 Int a) Identity -> a
1181+ fromDSum (Const2 _ :=> Identity v) = v
9431182
9441183-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
9451184-- unless the second supplied 'Event' occurs simultaneously.
9461185difference :: Reflex t => Event t a -> Event t b -> Event t a
9471186difference = alignEventWithMaybe $ \case
9481187 This a -> Just a
949− _ -> Nothing
1188+ _ -> Nothing
9501189
1190+-- | Zips two values by taking the union of their shapes and combining with the provided function.
1191+-- 'Nothing' values are dropped.
9511192alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
952−alignEventWithMaybe f ea eb =
953− fmapMaybe (f <=< dmapToThese)
954− $ merge
955− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1193+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $
1194+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
9561195
1196+-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that
1197+-- matches the provided key.
9571198filterEventKey
9581199 :: forall t m k v a.
9591200 ( Reflex t
@@ -971,7 +1212,10 @@ filterEventKey k kv' = do
9711212 Nothing -> Nothing
9721213 takeWhileJustE f kv'
9731214
974−
1215+-- | "Factor" the input 'DSum' 'Event' to produce an 'Event' which
1216+-- fires when the 'DSum' key changes and contains both the value of the
1217+-- 'DSum' at switchover and an 'Event' of values produced by subsequent
1218+-- firings of the input 'Event' that do not change the 'DSum' key.
9751219factorEvent
9761220 :: forall t m k v a.
9771221 ( Reflex t
@@ -983,9 +1227,9 @@ factorEvent
9831227 -> Event t (DSum k v)
9841228 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
9851229factorEvent k0 kv' = do
986− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1230+ key :: Behavior t (Some k) <- hold (Some k0) $ fmapCheap (\(k :=> _) -> Some k) kv'
9871231 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
988− Some.This oldKey -> case newKey `geq` oldKey of
1232+ Some oldKey -> case newKey `geq` oldKey of
9891233 Just Refl -> return Nothing
9901234 Nothing -> do
9911235 newInner <- filterEventKey newKey kv'
@@ -1021,26 +1265,94 @@ class Reflex t => Accumulator t f | f -> t where
10211265 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
10221266 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
10231267
1024−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1268+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1269+-- with the provided function. See 'foldDyn'.
1270+accumDyn
1271+ :: (Reflex t, MonadHold t m, MonadFix m)
1272+ => (a -> b -> a)
1273+ -> a
1274+ -> Event t b
1275+ -> m (Dynamic t a)
10251276accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1026−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
1277+
1278+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1279+-- with the provided 'PushM' action.
1280+accumMDyn
1281+ :: (Reflex t, MonadHold t m, MonadFix m)
1282+ => (a -> b -> PushM t a)
1283+ -> a
1284+ -> Event t b
1285+ -> m (Dynamic t a)
10271286accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1028−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1287+
1288+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1289+-- with the provided function, discarding 'Nothing' results.
1290+accumMaybeDyn
1291+ :: (Reflex t, MonadHold t m, MonadFix m)
1292+ => (a -> b -> Maybe a)
1293+ -> a
1294+ -> Event t b
1295+ -> m (Dynamic t a)
10291296accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1030−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
1297+
1298+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1299+-- with the provided 'PushM' action, discarding 'Nothing' results.
1300+accumMaybeMDyn
1301+ :: (Reflex t, MonadHold t m, MonadFix m)
1302+ => (a -> b -> PushM t (Maybe a))
1303+ -> a
1304+ -> Event t b
1305+ -> m (Dynamic t a)
10311306accumMaybeMDyn f z e = do
10321307 rec let e' = flip push e $ \o -> do
10331308 v <- sample $ current d'
10341309 f v o
10351310 d' <- holdDyn z e'
10361311 return d'
1037−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1312+
1313+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1314+-- with a function that both accumulates and produces a value to fire
1315+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
1316+mapAccumDyn
1317+ :: (Reflex t, MonadHold t m, MonadFix m)
1318+ => (a -> b -> (a, c))
1319+ -> a
1320+ -> Event t b
1321+ -> m (Dynamic t a, Event t c)
10381322mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1039−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)
1323+
1324+-- | Similar to 'mapAccumDyn' except that the combining function is a
1325+-- 'PushM' action.
1326+mapAccumMDyn
1327+ :: (Reflex t, MonadHold t m, MonadFix m)
1328+ => (a -> b -> PushM t (a, c))
1329+ -> a
1330+ -> Event t b
1331+ -> m (Dynamic t a, Event t c)
10401332mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1041−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)
1333+
1334+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event' with
1335+-- a function that both optionally accumulates and optionally produces
1336+-- a value to fire as a separate output 'Event'.
1337+-- Note that because 'Nothing's are discarded in both cases, the output
1338+-- 'Event' may fire even though the output 'Dynamic' has not changed, and
1339+-- the output 'Dynamic' may update even when the output 'Event' is not firing.
1340+mapAccumMaybeDyn
1341+ :: (Reflex t, MonadHold t m, MonadFix m)
1342+ => (a -> b -> (Maybe a, Maybe c))
1343+ -> a
1344+ -> Event t b
1345+ -> m (Dynamic t a, Event t c)
10421346mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1043−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)
1347+
1348+-- | Like 'mapAccumMaybeDyn' except that the combining function is a
1349+-- 'PushM' action.
1350+mapAccumMaybeMDyn
1351+ :: (Reflex t, MonadHold t m, MonadFix m)
1352+ => (a -> b -> PushM t (Maybe a, Maybe c))
1353+ -> a
1354+ -> Event t b
1355+ -> m (Dynamic t a, Event t c)
10441356mapAccumMaybeMDyn f z e = do
10451357 rec let e' = flip push e $ \o -> do
10461358 v <- sample $ current d'
@@ -1048,18 +1360,42 @@ mapAccumMaybeMDyn f z e = do
10481360 return $ case result of
10491361 (Nothing, Nothing) -> Nothing
10501362 _ -> Just result
1051− d' <- holdDyn z $ fmapMaybe fst e'
1052− return (d', fmapMaybe snd e')
1363+ d' <- holdDyn z $ mapMaybe fst e'
1364+ return (d', mapMaybe snd e')
10531365
1366+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1367+-- with the provided function.
10541368{-# INLINE accumB #-}
1055−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1369+accumB
1370+ :: (Reflex t, MonadHold t m, MonadFix m)
1371+ => (a -> b -> a)
1372+ -> a
1373+ -> Event t b
1374+ -> m (Behavior t a)
10561375accumB f = accumMaybeB $ \v o -> Just $ f v o
1376+
1377+-- | Like 'accumB' except that the combining function is a 'PushM' action.
10571378{-# INLINE accumMB #-}
1058−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
1379+accumMB
1380+ :: (Reflex t, MonadHold t m, MonadFix m)
1381+ => (a -> b -> PushM t a)
1382+ -> a
1383+ -> Event t b
1384+ -> m (Behavior t a)
10591385accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1386+
1387+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1388+-- with the provided function, discarding 'Nothing' results.
10601389{-# INLINE accumMaybeB #-}
1061−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1390+accumMaybeB
1391+ :: (Reflex t, MonadHold t m, MonadFix m)
1392+ => (a -> b -> Maybe a)
1393+ -> a
1394+ -> Event t b
1395+ -> m (Behavior t a)
10621396accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1397+
1398+-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action.
10631399{-# INLINE accumMaybeMB #-}
10641400accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
10651401accumMaybeMB f z e = do
@@ -1068,16 +1404,42 @@ accumMaybeMB f z e = do
10681404 f v o
10691405 d' <- hold z e'
10701406 return d'
1407+
1408+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1409+-- with a function that both accumulates and produces a value to fire
1410+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
10711411{-# INLINE mapAccumB #-}
1072−mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1412+mapAccumB
1413+ :: (Reflex t, MonadHold t m, MonadFix m)
1414+ => (a -> b -> (a, c))
1415+ -> a
1416+ -> Event t b
1417+ -> m (Behavior t a, Event t c)
10731418mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1419+
1420+-- | Like 'mapAccumB' except that the combining function is a 'PushM' action.
10741421{-# INLINE mapAccumMB #-}
1075−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)
1422+mapAccumMB
1423+ :: (Reflex t, MonadHold t m, MonadFix m)
1424+ => (a -> b -> PushM t (a, c))
1425+ -> a
1426+ -> Event t b
1427+ -> m (Behavior t a, Event t c)
10761428mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1429+
1430+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event' with
1431+-- a function that both optionally accumulates and optionally produces
1432+-- a value to fire as a separate output 'Event'. 'Nothing's are discarded.
10771433{-# INLINE mapAccumMaybeB #-}
1078−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)
1434+mapAccumMaybeB
1435+ :: (Reflex t, MonadHold t m, MonadFix m)
1436+ => (a -> b -> (Maybe a, Maybe c))
1437+ -> a
1438+ -> Event t b
1439+ -> m (Behavior t a, Event t c)
10791440mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
10801441
1442+-- | Like 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
10811443{-# INLINE mapAccumMaybeMB #-}
10821444mapAccumMaybeMB :: (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)
10831445mapAccumMaybeMB f z e = do
@@ -1087,8 +1449,8 @@ mapAccumMaybeMB f z e = do
10871449 return $ case result of
10881450 (Nothing, Nothing) -> Nothing
10891451 _ -> Just result
1090− d' <- hold z $ fmapMaybe fst e'
1091− return (d', fmapMaybe snd e')
1452+ d' <- hold z $ mapMaybe fst e'
1453+ return (d', mapMaybe snd e')
10921454
10931455-- | Accumulate occurrences of an 'Event', producing an output occurrence each
10941456-- time. Discard the underlying 'Accumulator'.
@@ -1232,38 +1594,70 @@ infixl 4 <@
12321594-- Cheap Functions
12331595------------------
12341596
1597+-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'.
12351598{-# INLINE pushAlwaysCheap #-}
12361599pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
12371600pushAlwaysCheap f = pushCheap (fmap Just . f)
12381601
1602+-- | A "cheap" version of 'mapMaybe'. See the performance note on 'pushCheap'.
1603+{-# INLINE mapMaybeCheap #-}
1604+mapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1605+mapMaybeCheap f = pushCheap $ return . f
1606+
1607+-- | An alias for 'mapMaybeCheap'
12391608{-# INLINE fmapMaybeCheap #-}
12401609fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1241−fmapMaybeCheap f = pushCheap $ return . f
1610+fmapMaybeCheap = mapMaybeCheap
1611+
12421612
1613+-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'.
12431614{-# INLINE fforMaybeCheap #-}
12441615fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1245−fforMaybeCheap = flip fmapMaybeCheap
1616+fforMaybeCheap = flip mapMaybeCheap
12461617
1618+-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'.
12471619{-# INLINE fforCheap #-}
12481620fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
12491621fforCheap = flip fmapCheap
12501622
1623+-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'.
12511624{-# INLINE fmapCheap #-}
12521625fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
12531626fmapCheap f = pushCheap $ return . Just . f
12541627
1628+-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'.
12551629{-# INLINE tagCheap #-}
12561630tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
12571631tagCheap b = pushAlwaysCheap $ \_ -> sample b
12581632
1633+-- | Merge a collection of events; the resulting 'Event' will only occur if at
1634+-- least one input event is occurring, and will contain all of the input keys
1635+-- that are occurring simultaneously
1636+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
1637+merge = mergeG coerceEvent
1638+{-# INLINE merge #-}
1639+
1640+-- | Create a merge whose parents can change over time
1641+mergeIncremental :: (Reflex t, GCompare k)
1642+ => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
1643+mergeIncremental = mergeIncrementalG coerceEvent
1644+
1645+-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
1646+mergeIncrementalWithMove :: (Reflex t, GCompare k)
1647+ => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
1648+mergeIncrementalWithMove = mergeIncrementalWithMoveG coerceEvent
1649+
1650+-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
12591651{-# INLINE mergeWithCheap #-}
12601652mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
12611653mergeWithCheap = mergeWithCheap' id
12621654
1655+-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'.
12631656{-# INLINE mergeWithCheap' #-}
12641657mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
12651658mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
12661659
1660+-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
12671661{-# INLINE mergeWithFoldCheap' #-}
12681662mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
12691663mergeWithFoldCheap' f es =
@@ -1276,31 +1670,11 @@ mergeWithFoldCheap' f es =
12761670-- Deprecated functions
12771671--------------------------------------------------------------------------------
12781672
1279−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1280−-- occurs. If both occur at the same time they are combined using 'mappend'.
1281−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1282−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1283−appendEvents = alignWith $ mergeThese mappend
1284−
1285−-- | Alias for 'headE'
1286−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1287−onceE :: MonadHold t m => Event t a -> m (Event t a)
1288−onceE = headE
1289−
1290−-- | Run both sides of a 'These' monadically, combining the results.
1291−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1292−#ifdef USE_TEMPLATE_HASKELL
1293−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1294−#endif
1295−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1296−sequenceThese t = case t of
1297− This ma -> fmap This ma
1298− These ma mb -> liftM2 These ma mb
1299− That mb -> fmap That mb
1300−
13011673{-# 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'. " #-}
1674+-- | See 'switchHoldPromptly'
13021675switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
13031676switchPromptly = switchHoldPromptly
13041677{-# 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'. " #-}
1678+-- | See 'switchHoldPromptOnly'
13051679switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
13061680switchPromptOnly = switchHoldPromptOnly
src/Reflex/Collection.hs+157−53
@@ -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
@@ -25,6 +27,13 @@ module Reflex.Collection
2527 , simpleList
2628 ) where
2729
30+#ifdef MIN_VERSION_semialign
31+import Prelude hiding (zip, zipWith)
32+#if MIN_VERSION_semialign(1,1,0)
33+import Data.Zip (Zip (..))
34+#endif
35+#endif
36+
2837import Control.Monad.Identity
2938import Data.Align
3039import Data.Functor.Misc
@@ -38,84 +47,179 @@ import Reflex.Adjustable.Class
3847import Reflex.Dynamic
3948import Reflex.PostBuild.Class
4049
41−listHoldWithKey :: forall t m k v a. (Ord k, Adjustable t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
50+-- | Create a set of widgets based on the provided 'Map'. When the
51+-- input 'Event' fires, remove widgets for keys with the value 'Nothing'
52+-- and add/replace widgets for keys with 'Just' values.
53+listHoldWithKey
54+ :: forall t m k v a
55+ . (Ord k, Adjustable t m, MonadHold t m)
56+ => Map k v
57+ -> Event t (Map k (Maybe v))
58+ -> (k -> v -> m a)
59+ -> m (Dynamic t (Map k a))
4260listHoldWithKey m0 m' f = do
4361 let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
44− dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
62+ dm' = fmap
63+ (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey
64+ (\k v -> ComposeMaybe $ fmap (f k) v)
65+ )
66+ m'
4567 (a0, a') <- sequenceDMapWithAdjust dm0 dm'
46− fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
4768
48−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
49−listWithKey :: forall t k v m a. (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
69+ --TODO: Move the dmapToMap to the righthand side so it doesn't get
70+ --fully redone every time
71+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a'
72+
73+--TODO: Something better than Dynamic t (Map k v) - we want something
74+--where the Events carry diffs, not the whole value
75+listWithKey
76+ :: forall t k v m a
77+ . (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m)
78+ => Dynamic t (Map k v)
79+ -> (k -> Dynamic t v -> m a)
80+ -> m (Dynamic t (Map k a))
5081listWithKey vals mkChild = do
5182 postBuild <- getPostBuild
5283 let childValChangedSelector = fanMap $ updated vals
53− -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey
54− -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges
55− -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for.
56− diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case
57− This _ -> Just Nothing
58− These _ _ -> Nothing
59− That new -> Just $ Just new
84+
85+ -- We keep track of changes to children values in the mkChild
86+ -- function we pass to listHoldWithKey The other changes we need
87+ -- to keep track of are child insertions and
88+ -- deletions. diffOnlyKeyChanges keeps track of insertions and
89+ -- deletions but ignores value changes, since they're already
90+ -- accounted for.
91+ diffOnlyKeyChanges olds news =
92+ flip Map.mapMaybe (align olds news) $ \case
93+ This _ -> Just Nothing
94+ These _ _ -> Nothing
95+ That new -> Just $ Just new
6096 rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
6197 let changeVals :: Event t (Map k (Maybe v))
62− changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
63− [ updated vals
64− , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
65− ]
98+ changeVals =
99+ attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
100+ [ updated vals
101+
102+ -- TODO: This should probably be added to the
103+ -- attachWith, not to the updated; if we were using
104+ -- diffMap instead of diffMapNoEq, I think it might not
105+ -- work
106+ , tag (current vals) postBuild
107+ ]
66108 listHoldWithKey Map.empty changeVals $ \k v ->
67109 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
68110
69−{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
70−listWithKey' :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
71−listWithKey' = listWithKeyShallowDiff
72−
73−-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
74−listWithKeyShallowDiff :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
111+-- | Display the given map of items (in key order) using the builder
112+-- function provided, and update it with the given event. 'Nothing'
113+-- update entries will delete the corresponding children, and 'Just'
114+-- entries will create them if they do not exist or send an update
115+-- event to them if they do.
116+listWithKeyShallowDiff
117+ :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m)
118+ => Map k v
119+ -> Event t (Map k (Maybe v))
120+ -> (k -> v -> Event t v -> m a)
121+ -> m (Dynamic t (Map k a))
75122listWithKeyShallowDiff initialVals valsChanged mkChild = do
76123 let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
77124 sentVals <- foldDyn applyMap (void initialVals) $ fmap (fmap void) valsChanged
78125 let relevantPatch patch _ = case patch of
79− Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
80− Just _ -> Nothing -- We don't want to let spurious re-creations of items through
81− listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
82− mkChild k v $ select childValChangedSelector $ Const2 k
83−
84−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
85−-- | Create a dynamically-changing set of Event-valued widgets.
86−-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
87−listViewWithKey :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
88−listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
89−
90−listViewWithKey' :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
126+
127+ -- Even if we let a Nothing through when the element doesn't
128+ -- already exist, this doesn't cause a problem because it is
129+ -- ignored
130+ Nothing -> Just Nothing
131+
132+ -- We don't want to let spurious re-creations of items through
133+ Just _ -> Nothing
134+ listHoldWithKey
135+ initialVals
136+ (attachWith (flip (Map.differenceWith relevantPatch))
137+ (current sentVals)
138+ valsChanged
139+ )
140+ $ \k v -> mkChild k v $ select childValChangedSelector $ Const2 k
141+
142+--TODO: Something better than Dynamic t (Map k v) - we want something
143+--where the Events carry diffs, not the whole value
144+-- | Create a dynamically-changing set of Event-valued widgets. This
145+-- is like 'listWithKey', specialized for widgets returning @/Event t a/@.
146+-- 'listWithKey' would return @/Dynamic t (Map k (Event t a))/@ in
147+-- this scenario, but 'listViewWithKey' flattens this to
148+-- @/Event t (Map k a)/@ via 'switch'.
149+listViewWithKey
150+ :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m)
151+ => Dynamic t (Map k v)
152+ -> (k -> Dynamic t v -> m (Event t a))
153+ -> m (Event t (Map k a))
154+listViewWithKey vals mkChild =
155+ switch . fmap mergeMap <$> listViewWithKey' vals mkChild
156+
157+listViewWithKey'
158+ :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m)
159+ => Dynamic t (Map k v)
160+ -> (k -> Dynamic t v -> m a)
161+ -> m (Behavior t (Map k a))
91162listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
92163
93−-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
94−selectViewListWithKey :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
95− => Dynamic t k -- ^ Current selection key
96− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
97− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
98− -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
164+-- | Create a dynamically-changing set of widgets, one of which is
165+-- selected at any time.
166+selectViewListWithKey
167+ :: forall t m k v a
168+ . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
169+ => Dynamic t k
170+ -- ^ Current selection key
171+ -> Dynamic t (Map k v)
172+ -- ^ Dynamic key/value map
173+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a))
174+ -- ^ Function to create a widget for a given key from Dynamic value
175+ -- and Dynamic Bool indicating if this widget is currently selected
176+ -> m (Event t (k, a))
177+ -- ^ Event that fires when any child's return Event fires. Contains
178+ -- key of an arbitrary firing widget.
99179selectViewListWithKey selection vals mkChild = do
100− let selectionDemux = demux selection -- For good performance, this value must be shared across all children
180+ -- For good performance, this value must be shared across all children
181+ let selectionDemux = demux selection
101182 selectChild <- listWithKey vals $ \k v -> do
102183 let selected = demuxed selectionDemux k
103184 selectSelf <- mkChild k v selected
104185 return $ fmap ((,) k) selectSelf
105186 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
106187
107−selectViewListWithKey_ :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
108− => Dynamic t k -- ^ Current selection key
109− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
110− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
111− -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
112−selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
188+-- | Like 'selectViewListWithKey' but discards the value of the list
189+-- item widget's output 'Event'.
190+selectViewListWithKey_
191+ :: forall t m k v a
192+ . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
193+ => Dynamic t k
194+ -- ^ Current selection key
195+ -> Dynamic t (Map k v)
196+ -- ^ Dynamic key/value map
197+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a))
198+ -- ^ Function to create a widget for a given key from Dynamic value
199+ -- and Dynamic Bool indicating if this widget is currently selected
200+ -> m (Event t k)
201+ -- ^ Event that fires when any child's return Event fires. Contains
202+ -- key of an arbitrary firing widget.
203+selectViewListWithKey_ selection vals mkChild =
204+ fmap fst <$> selectViewListWithKey selection vals mkChild
113205
114−-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
115−-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
116−list :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
206+-- | Create a dynamically-changing set of widgets from a Dynamic
207+-- key/value map. Unlike the 'withKey' variants, the child widgets
208+-- are insensitive to which key they're associated with.
209+list
210+ :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m)
211+ => Dynamic t (Map k v)
212+ -> (Dynamic t v -> m a)
213+ -> m (Dynamic t (Map k a))
117214list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
118215
119216-- | Create a dynamically-changing set of widgets from a Dynamic list.
120−simpleList :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
121−simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
217+simpleList
218+ :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m)
219+ => Dynamic t [v]
220+ -> (Dynamic t v -> m a)
221+ -> m (Dynamic t [a])
222+simpleList xs mkChild =
223+ fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap
224+ (Map.fromList . zip [(1 :: Int) ..])
225+ xs
src/Reflex/Dynamic.hs+35−185
@@ -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
@@ -58,6 +62,7 @@ module Reflex.Dynamic
5862 , Demux
5963 , demux
6064 , demuxed
65+ -- * Miscellaneous
6166 -- Things that probably aren't very useful:
6267 , HList (..)
6368 , FHList (..)
@@ -67,34 +72,14 @@ module Reflex.Dynamic
6772 , AllAreFunctors (..)
6873 , HListPtr (..)
6974 , distributeFHListOverDynPure
70− -- Unsafe
75+ -- * Unsafe
7176 , unsafeDynamic
72− -- * Deprecated functions
73− , apDyn
74− , attachDyn
75− , attachDynWith
76− , attachDynWithMaybe
77− , collectDyn
78− , combineDyn
79− , distributeDMapOverDyn
80− , distributeFHListOverDyn
81− , forDyn
82− , getDemuxed
83− , joinDyn
84− , mapDyn
85− , mconcatDyn
86− , nubDyn
87− , splitDyn
88− , tagDyn
89− , uniqDyn
90− , uniqDynBy
9177 ) where
9278
9379import Data.Functor.Compose
9480import Data.Functor.Misc
9581import Reflex.Class
9682
97−import Control.Applicative ((<*>))
9883import Control.Monad
9984import Control.Monad.Fix
10085import Control.Monad.Identity
@@ -102,11 +87,10 @@ import Data.Align
10287import Data.Dependent.Map (DMap)
10388import qualified Data.Dependent.Map as DMap
10489import Data.Dependent.Sum (DSum (..))
105−import Data.Functor.Product
10690import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
10791import Data.Map (Map)
10892import Data.Maybe
109−import Data.Monoid hiding (Product)
93+import Data.Monoid ((<>))
11094import Data.These
11195
11296import Debug.Trace
@@ -130,7 +114,7 @@ holdUniqDyn = holdUniqDynBy (==)
130114holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
131115holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
132116
133−-- | Dynamic Maybe that can only update from Nothing to Just or Just to Just (i.e., cannot revert to Nothing)
117+-- | @/Dynamic Maybe/@ that can only update from @/Nothing/@ to @/Just/@ or @/Just/@ to @/Just/@ (i.e., cannot revert to @/Nothing/@)
134118improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
135119improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
136120
@@ -195,9 +179,9 @@ switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
195179switchDyn d = switch (current d)
196180
197181-- | Switches to the new 'Event' whenever it receives one. Switching occurs
198−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
182+-- __before__ the inner 'Event' fires - so if the 'Dynamic' changes and both the
199183-- old and new inner Events fire simultaneously, the output will fire with the
200−-- value of the *new* 'Event'.
184+-- value of the __new__ 'Event'.
201185--
202186-- Prefer 'switchDyn' to this where possible. The timing requirements that
203187-- switching before imposes are likely to bring down your app unless you are
@@ -219,26 +203,18 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
219203
220204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
221205-- non-'Dynamic' elements, preserving the order of the elements.
206+{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
222207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
223−distributeListOverDynPure =
224− fmap (map fromDSum . DMap.toAscList) .
225− distributeDMapOverDynPure .
226− DMap.fromDistinctAscList .
227− zipWith toDSum [0..]
228− where
229− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
230− toDSum k v = Const2 k :=> v
231− fromDSum :: DSum (Const2 Int a) Identity -> a
232− fromDSum (Const2 _ :=> Identity v) = v
208+distributeListOverDynPure = distributeListOverDyn
233209
234210--TODO: Generalize this to functors other than Maps
235211-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
236212-- with the current values of the 'Dynamic's in a map.
237213joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
238−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
214+joinDynThroughMap = (distributeMapOverDynPure =<<)
239215
240216-- | Print the value of the 'Dynamic' when it is first read and on each
241−-- subsequent change that is observed (as traceEvent), prefixed with the
217+-- subsequent change that is observed (as 'traceEvent'), prefixed with the
242218-- provided string. This should /only/ be used for debugging.
243219--
244220-- Note: Just like Debug.Trace.trace, the value will only be shown if something
@@ -248,7 +224,7 @@ traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
248224
249225-- | Print the result of applying the provided function to the value
250226-- of the 'Dynamic' when it is first read and on each subsequent change
251−-- that is observed (as traceEvent). This should /only/ be used for
227+-- that is observed (as 'traceEvent'). This should /only/ be used for
252228-- debugging.
253229--
254230-- Note: Just like Debug.Trace.trace, the value will only be shown if something
@@ -264,27 +240,27 @@ traceDynWith f d =
264240-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
265241-- each time the 'Event' occurs.
266242--
267−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
268−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
269−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
243+-- Note: @/tagPromptlyDyn d e/@ differs from @/tag (current d) e/@ in the case that @/e/@ is firing
244+-- at the same time that @/d/@ is changing. With @/tagPromptlyDyn d e/@, the __new__ value of @/d/@
245+-- will replace the value of @/e/@, whereas with @/tag (current d) e/@, the __old__ value
270246-- will be used, since the 'Behavior' won't be updated until the end of the frame.
271247-- Additionally, this means that the output 'Event' may not be used to directly change
272−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
273−-- cyclic data flows, generally `tag (current d) e` is preferred.
248+-- the input 'Dynamic', because that would mean its value depends on itself. __When creating__
249+-- __cyclic data flows, generally @/tag (current d) e/@ is preferred.__
274250tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
275251tagPromptlyDyn = attachPromptlyDynWith const
276252
277253-- | Attach the current value of the 'Dynamic' to the value of the
278254-- 'Event' each time it occurs.
279255--
280−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
256+-- Note: @/attachPromptlyDyn d/@ is not the same as @/attach (current d)/@. See 'tagPromptlyDyn' for details.
281257attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
282258attachPromptlyDyn = attachPromptlyDynWith (,)
283259
284260-- | Combine the current value of the 'Dynamic' with the value of the
285261-- 'Event' each time it occurs.
286262--
287−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
263+-- Note: @/attachPromptlyDynWith f d/@ is not the same as @/attachWith f (current d)/@. See 'tagPromptlyDyn' for details.
288264attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
289265attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
290266
@@ -292,8 +268,7 @@ attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
292268-- current value of the 'Dynamic' value and possibly filtering if the combining
293269-- function returns 'Nothing'.
294270--
295−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
296−-- (current d)`. See 'tagPromptlyDyn' for details.
271+-- Note: @/attachPromptlyDynWithMaybe f d/@ is not the same as @/attachWithMaybe f (current d)/@. See 'tagPromptlyDyn' for details.
297272attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
298273attachPromptlyDynWithMaybe f d e =
299274 let e' = attach (current d) e
@@ -302,8 +277,8 @@ attachPromptlyDynWithMaybe f d e =
302277 These (_, b) a -> f a b -- Both events are firing, so use the newer value
303278 That _ -> Nothing -- The tagging event isn't firing, so don't fire
304279
305−-- | Factor a @Dynamic t (Maybe a)@ into a @Dynamic t (Maybe (Dynamic t a))@,
306−-- such that the outer 'Dynamic' is updated only when the 'Maybe''s constructor
280+-- | Factor a @/Dynamic t (Maybe a)/@ into a @/Dynamic t (Maybe (Dynamic t a))/@,
281+-- such that the outer 'Dynamic' is updated only when the "Maybe"'s constructor
307282-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
308283-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
309284-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
@@ -318,6 +293,8 @@ maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
318293 Left _ -> Nothing
319294 Right a -> Just a
320295
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').
321298eitherDyn :: 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)))
322299eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
323300 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
@@ -325,6 +302,9 @@ eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
325302 LeftTag :=> Compose a -> Left $ coerceDynamic a
326303 RightTag :=> Compose b -> Right $ coerceDynamic b
327304
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.
328308factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
329309 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
330310factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
@@ -339,6 +319,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
339319 inner' <- filterEventKey k (updated d)
340320 inner <- holdDyn v inner'
341321 return $ k :=> Compose inner
322+
342323--------------------------------------------------------------------------------
343324-- Demux
344325--------------------------------------------------------------------------------
@@ -352,7 +333,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
352333--
353334-- > demuxed (demux d) k === fmap (== k) d
354335--
355−-- However, the when getDemuxed is used multiple times, the complexity is only
336+-- However, when getDemuxed is used multiple times, the complexity is only
356337-- /O(log(n))/, rather than /O(n)/ for fmap.
357338data Demux t k = Demux { demuxValue :: Behavior t k
358339 , demuxSelector :: EventSelector t (Const2 k Bool)
@@ -506,7 +487,7 @@ instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
506487
507488-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
508489-- single 'Dynamic' whose value represents all the current values of the input's
509−-- consitutent 'Dynamic's.
490+-- constituent 'Dynamic's.
510491collectDynPure :: ( RebuildSortedHList (HListElems b)
511492 , IsHList a, IsHList b
512493 , AllAreFunctors (Dynamic t) (HListElems b)
@@ -547,134 +528,3 @@ instance IsHList (a, b, c, d, e, f) where
547528#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
548529 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
549530#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+2−26
@@ -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
@@ -84,7 +81,7 @@ mkDynPure = QuasiQuoter
8481 }
8582
8683mkDynExp :: String -> Q Exp
87−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
84+mkDynExp s = case Hs.parseExpWithMode Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] } s of
8885 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
8986 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
9087 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
@@ -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+13−4
@@ -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,13 +35,14 @@ 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
41−import Reflex.Class
4242import Reflex.Adjustable.Class
43+import Reflex.Class
4344import Reflex.DynamicWriter.Class
45+import Reflex.EventWriter.Class (EventWriter, tellEvent)
4446import Reflex.Host.Class
4547import qualified Reflex.Patch.MapWithMove as MapWithMove
4648import Reflex.PerformEvent.Class
@@ -86,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
8688 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
8789 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
8890
89−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
9092newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
9193 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
9294
@@ -114,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
114116 (result, ws) <- runStateT a []
115117 return (result, mconcat $ reverse ws)
116118
117−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
118120 tellDyn w = DynamicWriterT $ modify (w :)
119121
120122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
@@ -139,6 +141,10 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where
139141 get = lift get
140142 put = lift . put
141143
144+instance PrimMonad m => PrimMonad (DynamicWriterT t w m) where
145+ type PrimState (DynamicWriterT t w m) = PrimState m
146+ primitive = lift . primitive
147+
142148newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
143149
144150-- | When the execution of a 'DynamicWriterT' action is adjusted using
@@ -226,3 +232,6 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (DynamicWriterT t w m) wh
226232 tellQueryIncremental = lift . tellQueryIncremental
227233 askQueryResult = lift askQueryResult
228234 queryIncremental = lift . queryIncremental
235+
236+instance EventWriter t w m => EventWriter t w (DynamicWriterT t v m) where
237+ tellEvent = lift . tellEvent
src/Reflex/DynamicWriter/Class.hs+10−5
@@ -1,24 +1,29 @@
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 #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE UndecidableInstances #-}
77#ifdef USE_REFLEX_OPTIMIZER
88{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
99#endif
1010module Reflex.DynamicWriter.Class
11− ( MonadDynamicWriter (..)
11+ ( MonadDynamicWriter
12+ , DynamicWriter(..)
1213 ) where
1314
1415import Control.Monad.Reader (ReaderT, lift)
1516import Reflex.Class (Dynamic)
1617
18+{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}
19+-- | Type synonym for 'DynamicWriter'
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+68−54
@@ -22,9 +22,10 @@ module Reflex.EventWriter.Base
2222 , withEventWriterT
2323 ) where
2424
25−import Reflex.Class
2625import Reflex.Adjustable.Class
26+import Reflex.Class
2727import Reflex.EventWriter.Class (EventWriter, tellEvent)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
2829import Reflex.Host.Class
2930import Reflex.PerformEvent.Class
3031import Reflex.PostBuild.Class
@@ -40,7 +41,6 @@ import Control.Monad.Ref
4041import Control.Monad.State.Strict
4142import Data.Dependent.Map (DMap, DSum (..))
4243import qualified Data.Dependent.Map as DMap
43−import Data.Foldable
4444import Data.Functor.Compose
4545import Data.Functor.Misc
4646import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
@@ -48,6 +48,7 @@ import Data.IntMap.Strict (IntMap)
4848import qualified Data.IntMap.Strict as IntMap
4949import Data.List.NonEmpty (NonEmpty (..))
5050import Data.Map (Map)
51+import qualified Data.Map as Map
5152import Data.Semigroup
5253import Data.Some (Some)
5354import Data.Tuple
@@ -57,7 +58,7 @@ import Unsafe.Coerce
5758
5859{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
5960newtype TellId w x
60− = TellId Int -- ^ WARNING: Do not construct this directly; use 'TellId' instead
61+ = TellId Int -- ^ WARNING: Do not construct this directly; use 'tellId' instead
6162 deriving (Show, Eq, Ord, Enum)
6263
6364tellId :: Int -> TellId w w
@@ -135,9 +136,9 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
135136
136137instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
137138 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
138− traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
139− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
140− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
139+ traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') mergeIntIncremental coincidencePatchIntMap
140+ traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith mergeMapIncremental coincidencePatchMap
141+ traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeMapIncrementalWithMove coincidencePatchMapWithMove
141142
142143instance Requester t m => Requester t (EventWriterT t w m) where
143144 type Request (EventWriterT t w m) = Request m
@@ -160,19 +161,29 @@ runWithReplaceEventWriterTWith f a0 a' = do
160161 return (fst result0, fmapCheap fst result')
161162
162163-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
163−sequenceIntMapWithAdjustEventWriterTWith :: forall t m p w v v'. (Reflex t, MonadHold t m, Semigroup w, Functor p, Patch (p (Event t w)), PatchTarget (p (Event t w)) ~ IntMap (Event t w))
164− => ( (IntMap.Key -> v -> m (Event t w, v'))
165− -> IntMap v
166− -> Event t (p v)
167− -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
168− )
169− -> (p (Event t w) -> [Event t w])
170− -> (Incremental t (p (Event t w)) -> Event t (IntMap w))
171− -> (IntMap.Key -> v -> EventWriterT t w m v')
172− -> IntMap v
173− -> Event t (p v)
174− -> EventWriterT t w m (IntMap v', Event t (p v'))
175−sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
164+sequenceIntMapWithAdjustEventWriterTWith
165+ :: forall t m p w v v'
166+ . ( Reflex t
167+ , MonadHold t m
168+ , Semigroup w
169+ , Functor p
170+ , Patch (p (Event t w))
171+ , PatchTarget (p (Event t w)) ~ IntMap (Event t w)
172+ , Patch (p w)
173+ , PatchTarget (p w) ~ IntMap w
174+ )
175+ => ( (IntMap.Key -> v -> m (Event t w, v'))
176+ -> IntMap v
177+ -> Event t (p v)
178+ -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
179+ )
180+ -> (Incremental t (p (Event t w)) -> Event t (PatchTarget (p w)))
181+ -> (Event t (p (Event t w)) -> Event t (p w))
182+ -> (IntMap.Key -> v -> EventWriterT t w m v')
183+ -> IntMap v
184+ -> Event t (p v)
185+ -> EventWriterT t w m (IntMap v', Event t (p v'))
186+sequenceIntMapWithAdjustEventWriterTWith base mergePatchIncremental coincidencePatch f dm0 dm' = do
176187 let f' :: IntMap.Key -> v -> m (Event t w, v')
177188 f' k v = swap <$> runEventWriterT (f k v)
178189 (children0, children') <- base f' dm0 dm'
@@ -182,26 +193,39 @@ sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncreme
182193 requests0 = fmap fst children0
183194 requests' :: Event t (p (Event t w))
184195 requests' = fmapCheap (fmap fst) children'
185− childRequestMap :: Incremental t (p (Event t w)) <- holdIncremental requests0 requests'
186− tellEventsPromptly requests' patchNewElements $ mergePatchIncremental childRequestMap
196+ e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
197+ tellEvent $ fforMaybeCheap e $ \m ->
198+ case IntMap.elems m of
199+ [] -> Nothing
200+ h : t -> Just $ sconcat $ h :| t
187201 return (result0, result')
188202
189203-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
190−sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
191− => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
192− -> DMap k v
193− -> Event t (p k v)
194− -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
195− )
196− -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
197− -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
198− -> (p' (Some k) (Event t w) -> [Event t w])
199− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
200− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
201− -> DMap k v
202− -> Event t (p k v)
203− -> EventWriterT t w m (DMap k v', Event t (p k v'))
204−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
204+sequenceDMapWithAdjustEventWriterTWith
205+ :: forall t m p p' w k v v'
206+ . ( Reflex t
207+ , MonadHold t m
208+ , Semigroup w
209+ , Patch (p' (Some k) (Event t w))
210+ , PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w)
211+ , GCompare k
212+ , Patch (p' (Some k) w)
213+ , PatchTarget (p' (Some k) w) ~ Map (Some k) w
214+ )
215+ => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
216+ -> DMap k v
217+ -> Event t (p k v)
218+ -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
219+ )
220+ -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
221+ -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
222+ -> (Incremental t (p' (Some k) (Event t w)) -> Event t (PatchTarget (p' (Some k) w)))
223+ -> (Event t (p' (Some k) (Event t w)) -> Event t (p' (Some k) w))
224+ -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
225+ -> DMap k v
226+ -> Event t (p k v)
227+ -> EventWriterT t w m (DMap k v', Event t (p k v'))
228+sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith mergePatchIncremental coincidencePatch f dm0 dm' = do
205229 let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
206230 f' k v = Compose . swap <$> runEventWriterT (f k v)
207231 (children0, children') <- base f' dm0 dm'
@@ -211,26 +235,13 @@ sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewEle
211235 requests0 = weakenDMapWith (fst . getCompose) children0
212236 requests' :: Event t (p' (Some k) (Event t w))
213237 requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
214− childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
215− tellEventsPromptly requests' patchNewElements $ mergePatchIncremental childRequestMap
238+ e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
239+ tellEvent $ fforMaybeCheap e $ \m ->
240+ case Map.elems m of
241+ [] -> Nothing
242+ h : t -> Just $ sconcat $ h :| t
216243 return (result0, result')
217244
218−tellEventsPromptly
219− :: ( Foldable f
220− , Reflex t
221− , MonadHold t m
222− , EventWriter t w m
223− )
224− => Event t a
225− -> (a -> [Event t w])
226− -> Event t (f w)
227− -> m ()
228−tellEventsPromptly requests' patchNewElements mergedChildRequestMap = do
229− let patch0 = fforMaybeCheap mergedChildRequestMap $ \m -> case toList m of
230− [] -> Nothing
231− h : t -> Just $ sconcat $ h :| t
232− tellEvent =<< switchHoldPromptOnly patch0 (fmapCheap (mconcat . patchNewElements) requests')
233−
234245instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
235246 type Performable (EventWriterT t w m) = Performable m
236247 performEvent_ = lift . performEvent_
@@ -267,6 +278,9 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
267278 askQueryResult = lift askQueryResult
268279 queryIncremental = lift . queryIncremental
269280
281+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where
282+ tellDyn = lift . tellDyn
283+
270284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
271285 type PrimState (EventWriterT t w m) = PrimState m
272286 primitive = lift . primitive
src/Reflex/EventWriter/Class.hs−1
@@ -2,7 +2,6 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76#ifdef USE_REFLEX_OPTIMIZER
87{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/FastWeak.hs+59−5
@@ -5,6 +5,19 @@
55{-# LANGUAGE ForeignFunctionInterface #-}
66{-# LANGUAGE JavaScriptFFI #-}
77#endif
8+
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).
15+--
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.
18+--
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.
821module Reflex.FastWeak
922 ( FastWeakTicket
1023 , FastWeak
@@ -35,29 +48,57 @@ import System.Mem.Weak
3548
3649
3750#ifdef GHCJS_FAST_WEAK
51+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
52+-- can be used to get the referred to value without fear of @Nothing,
53+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
54+--
55+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeakTicket@.
3856newtype FastWeakTicket a = FastWeakTicket JSVal
3957
58+-- | A reference to some value which can be garbage collected if there are only
59+-- weak references to the value left.
60+--
61+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
62+--
63+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using
64+-- 'getFastWeakTicket' if the value hasn't been collected yet.
65+--
66+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeak@.
4067newtype FastWeak a = FastWeak JSVal
4168
4269-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
4370data Val a = Val { unVal :: a }
4471
45−-- | Coerce a JSVal that represents the heap object of a value of type 'a' into a value of type 'a'
72+-- | Coerce a JSVal that represents the heap object of a value of type @a@ into a value of type @a@
4673unsafeFromRawJSVal :: JSVal -> a
4774unsafeFromRawJSVal v = unVal (unsafeCoerce v)
4875
76+-- | Coerce a heap object of type @a@ into a 'JSVal' which represents that object.
4977unsafeToRawJSVal :: a -> JSVal
5078unsafeToRawJSVal v = unsafeCoerce (Val v)
5179#else
80+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
81+-- can be used to get the referred to value without fear of @Nothing@,
82+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
5283data FastWeakTicket a = FastWeakTicket
5384 { _fastWeakTicket_val :: !a
5485 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
5586 }
5687
88+-- | A reference to some value which can be garbage collected if there are only weak references to the value left.
89+--
90+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
91+--
92+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket'
93+-- if the value hasn't been collected yet.
94+--
95+-- Synonymous with 'Weak'.
5796type FastWeak a = Weak a
5897#endif
5998
60−-- This needs to be in IO so we know that we've relinquished the ticket
99+-- | Return the @a@ kept alive by the given 'FastWeakTicket'.
100+--
101+-- This needs to be in IO so we know that we've relinquished the ticket.
61102getFastWeakTicketValue :: FastWeakTicket a -> IO a
62103#ifdef GHCJS_FAST_WEAK
63104getFastWeakTicketValue t = do
@@ -69,6 +110,8 @@ foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a
69110getFastWeakTicketValue = return . _fastWeakTicket_val
70111#endif
71112
113+-- | Get the value referred to by a 'FastWeak' if it hasn't yet been collected,
114+-- or @Nothing@ if it has been collected.
72115getFastWeakValue :: FastWeak a -> IO (Maybe a)
73116#ifdef GHCJS_FAST_WEAK
74117getFastWeakValue w = do
@@ -84,6 +127,9 @@ foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.v
84127getFastWeakValue = deRefWeak
85128#endif
86129
130+-- | Try to create a 'FastWeakTicket' for the given 'FastWeak' which will ensure the value referred
131+-- remains alive. Returns @Just@ if the value hasn't been collected
132+-- and a ticket can therefore be obtained, @Nothing@ if it's been collected.
87133getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88134#ifdef GHCJS_FAST_WEAK
89135getFastWeakTicket w = do
@@ -103,8 +149,11 @@ getFastWeakTicket w = do
103149 }
104150#endif
105151
106−-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the associated FastWeak to actually be weak
152+-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
153+-- which can be obtained with 'getFastWeakTicketValue'.
107154mkFastWeakTicket :: a -> IO (FastWeakTicket a)
155+-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
156+-- associated FastWeak to actually be weak
108157#ifdef GHCJS_FAST_WEAK
109158mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
110159
@@ -119,11 +168,16 @@ mkFastWeakTicket v = do
119168 }
120169#endif
121170
171+-- | Demote a 'FastWeakTicket'; which ensures the value is alive, to a 'FastWeak' which doesn't.
172+-- Note that unless the ticket for the same 'FastWeak' is held in some other way
173+-- the value might be collected immediately.
174+getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
122175-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
123176#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
177+foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak' :: FastWeakTicket a -> IO (FastWeak a)
178+{-# INLINE getFastWeakTicketWeak #-}
179+getFastWeakTicketWeak = getFastWeakTicketWeak'
125180#else
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127181getFastWeakTicketWeak = return . _fastWeakTicket_weak
128182#endif
129183
src/Reflex/FunctorMaybe.hs+31−17
@@ -1,33 +1,47 @@
1−-- | This module defines the FunctorMaybe class, which extends Functors with the
2−-- ability to delete values.
1+{-# LANGUAGE CPP #-}
2+#if MIN_VERSION_base(4,9,0)
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+#endif
6+
7+-- |
8+-- Module:
9+-- Reflex.FunctorMaybe
10+-- Description:
11+-- This module defines the FunctorMaybe class, which extends Functors with the
12+-- ability to delete values.
313module Reflex.FunctorMaybe
414 ( FunctorMaybe (..)
515 ) where
616
7−import Data.Maybe
17+import Data.IntMap (IntMap)
18+import Data.Map (Map)
19+#if MIN_VERSION_base(4,9,0)
20+import Data.Semigroup (Option(..))
21+#endif
22+import Data.Witherable
823
924--TODO: See if there's a better class in the standard libraries already
1025
1126-- | A class for values that combines filtering and mapping using 'Maybe'.
12−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
13−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
14−-- and e.g. 'Event' is instance of neither.
15−--
16−-- A definition of 'fmapMaybe' must satisfy the following laws:
17−--
18−-- [/identity/]
19−-- @'fmapMaybe' 'Just' ≡ 'id'@
20−--
21−-- [/composition/]
22−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
27+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.
28+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-}
2329class FunctorMaybe f where
2430 -- | Combined mapping and filtering function.
2531 fmapMaybe :: (a -> Maybe b) -> f a -> f b
2632
27−-- | @fmapMaybe = (=<<)
2833instance FunctorMaybe Maybe where
29− fmapMaybe = (=<<)
34+ fmapMaybe = mapMaybe
35+
36+#if MIN_VERSION_base(4,9,0)
37+deriving instance FunctorMaybe Option
38+#endif
3039
31−-- | @fmapMaybe = mapMaybe@
3240instance FunctorMaybe [] where
3341 fmapMaybe = mapMaybe
42+
43+instance FunctorMaybe (Map k) where
44+ fmapMaybe = mapMaybe
45+
46+instance FunctorMaybe IntMap where
47+ fmapMaybe = mapMaybe
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE RankNTypes #-}
98{-# LANGUAGE RoleAnnotations #-}
109{-# LANGUAGE ScopedTypeVariables #-}
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−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleContexts #-}
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
98#ifdef USE_REFLEX_OPTIMIZER
@@ -15,7 +14,9 @@ module Reflex.NotReady.Class
1514
1615import Control.Monad.Reader (ReaderT)
1716import Control.Monad.Trans
17+import Control.Monad.Trans.Writer (WriterT)
1818
19+import Reflex.BehaviorWriter.Base (BehaviorWriterT)
1920import Reflex.Class
2021import Reflex.DynamicWriter.Base (DynamicWriterT)
2122import Reflex.EventWriter.Base (EventWriterT)
@@ -39,6 +40,10 @@ instance NotReady t m => NotReady t (ReaderT r m) where
3940 notReadyUntil = lift . notReadyUntil
4041 notReady = lift notReady
4142
43+instance (NotReady t m, Monoid w) => NotReady t (WriterT w m) where
44+ notReadyUntil = lift . notReadyUntil
45+ notReady = lift notReady
46+
4247instance NotReady t m => NotReady t (PostBuildT t m) where
4348 notReadyUntil = lift . notReadyUntil
4449 notReady = lift notReady
@@ -51,6 +56,10 @@ instance NotReady t m => NotReady t (DynamicWriterT t w m) where
5156 notReadyUntil = lift . notReadyUntil
5257 notReady = lift notReady
5358
59+instance NotReady t m => NotReady t (BehaviorWriterT t w m) where
60+ notReadyUntil = lift . notReadyUntil
61+ notReady = lift notReady
62+
5463instance NotReady t m => NotReady t (QueryT t q m) where
5564 notReadyUntil = lift . notReadyUntil
5665 notReady = lift notReady
src/Reflex/Optimizer.hs+13−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
@@ -16,6 +20,11 @@ import Control.Arrow
1620import CoreMonad
1721import Data.String
1822import GhcPlugins
23+
24+#if MIN_VERSION_base(4,9,0)
25+import Prelude hiding ((<>))
26+#endif
27+
1928#endif
2029
2130#ifdef ghcjs_HOST_OS
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/DMap.hs+22−8
@@ -5,6 +5,7 @@
55{-# LANGUAGE ScopedTypeVariables #-}
66{-# LANGUAGE StandaloneDeriving #-}
77{-# LANGUAGE TypeFamilies #-}
8+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
89module Reflex.Patch.DMap where
910
1011import Reflex.Patch.Class
@@ -20,14 +21,16 @@ import qualified Data.Map as Map
2021import Data.Semigroup (Semigroup (..))
2122import Data.Some (Some)
2223
23−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
24−-- deleted.
24+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
25+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
26+-- while deletions are represented as @'ComposeMaybe' Nothing@.
2527newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
2628
2729deriving instance GCompare k => Semigroup (PatchDMap k v)
2830
2931deriving instance GCompare k => Monoid (PatchDMap k v)
3032
33+-- | Apply the insertions or deletions to a given 'DMap'.
3134instance GCompare k => Patch (PatchDMap k v) where
3235 type PatchTarget (PatchDMap k v) = DMap k v
3336 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
@@ -37,32 +40,43 @@ instance GCompare k => Patch (PatchDMap k v) where
3740 Nothing -> Just $ Constant ()
3841 Just _ -> Nothing
3942
43+-- | Map a function @v a -> v' a@ over any inserts/updates in the given
44+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
4045mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
4146mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
4247
43−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
48+-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
49+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
50+traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
4451traversePatchDMap f = traversePatchDMapWithKey $ const f
4552
53+-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
54+-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
4655traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
4756traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
4857
58+-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
59+-- @v a -> v'@ to weaken each value contained in the patch.
4960weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
5061weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
5162
52−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
63+-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
64+-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
65+patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
5366patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
5467
55−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
68+-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
69+const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
5670const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
57− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
71+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
5872 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
5973
74+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
6075const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
6176const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
6277 where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
6378 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
6479
65−-- | Get the values that will be deleted if the given patch is applied to the
66−-- given 'DMap'. Includes values that will be replaced.
80+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
6781getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
6882getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hs+9−9
@@ -19,9 +19,9 @@ import Reflex.Patch.Class
1919import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
2020import qualified Reflex.Patch.MapWithMove as MapWithMove
2121
22+import Data.Constraint.Extras
2223import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
2324import qualified Data.Dependent.Map as DMap
24−import Data.Dependent.Sum (EqTag (..))
2525import Data.Functor.Constant
2626import Data.Functor.Misc
2727import Data.Functor.Product
@@ -30,8 +30,7 @@ import Data.GADT.Show (GShow, gshow)
3030import qualified Data.Map as Map
3131import Data.Maybe
3232import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some)
34−import qualified Data.Some as Some
33+import Data.Some (Some(Some))
3534import Data.These
3635
3736-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
@@ -104,8 +103,9 @@ validationErrorsForPatchDMapWithMove m =
104103 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
105104 unbalancedMove _ = Nothing
106105
107−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
108− PatchDMapWithMove a == PatchDMapWithMove b = a == b
106+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
107+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
109109
110110-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
111111data Pair1 f g a = Pair1 (f a) (g a)
@@ -310,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
310310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
311311 From_Insert v -> MapWithMove.From_Insert $ f v
312312 From_Delete -> MapWithMove.From_Delete
313− From_Move k -> MapWithMove.From_Move $ Some.This k
314− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
313+ From_Move k -> MapWithMove.From_Move $ Some k
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
315315 }
316316
317317-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
@@ -341,6 +341,7 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
341341 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
342342 }
343343
344+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
344345instance GCompare k => Patch (PatchDMapWithMove k v) where
345346 type PatchTarget (PatchDMapWithMove k v) = DMap k v
346347 apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
@@ -356,8 +357,7 @@ instance GCompare k => Patch (PatchDMapWithMove k v) where
356357 From_Delete -> Just $ Constant ()
357358 _ -> Nothing
358359
359−-- | Get the values that will be deleted or moved if the given patch is applied
360−-- to the given 'DMap'.
360+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
361361getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
362362getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
363363 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Reflex/Patch/IntMap.hs+20−10
@@ -1,9 +1,10 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DeriveFoldable #-}
32{-# LANGUAGE DeriveFunctor #-}
43{-# LANGUAGE DeriveTraversable #-}
54{-# LANGUAGE GeneralizedNewtypeDeriving #-}
65{-# LANGUAGE TypeFamilies #-}
6+-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
7+-- insert/update or delete of associations.
78module Reflex.Patch.IntMap where
89
910import Data.IntMap.Strict (IntMap)
@@ -12,8 +13,12 @@ import Data.Maybe
1213import Data.Semigroup
1314import Reflex.Patch.Class
1415
16+-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
17+-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
18+-- and @Nothing@ means delete.
1519newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
1620
21+-- | Apply the insertions or deletions to a given 'IntMap'.
1722instance Patch (PatchIntMap a) where
1823 type PatchTarget (PatchIntMap a) = IntMap a
1924 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
@@ -27,23 +32,28 @@ instance Patch (PatchIntMap a) where
2732instance Semigroup (PatchIntMap v) where
2833 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
2934 -- PatchMap is idempotent, so stimes n is id for every n
30−#if MIN_VERSION_semigroups(0,17,0)
3135 stimes = stimesIdempotentMonoid
32−#else
33− times1p n x = case compare n 0 of
34− LT -> error "stimesIdempotentMonoid: negative multiplier"
35− EQ -> mempty
36− GT -> x
37−#endif
38−
39−traverseIntMapPatchWithKey :: Applicative t => (Int -> a -> t b) -> PatchIntMap a -> t (PatchIntMap b)
36+
37+-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
38+-- (that is, all inserts/updates), producing a @PatchIntMap b@.
39+mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
40+mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
41+
42+-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
43+-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
44+traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
4045traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
4146
47+-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
4248patchIntMapNewElements :: PatchIntMap a -> [a]
4349patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
4450
51+-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
52+-- the inserts/updates in the given patch.
4553patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
4654patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
4755
56+-- | Subset the given @'IntMap' a@ to contain only the keys that would be
57+-- deleted by the given @'PatchIntMap' a@.
4858getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
4959getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Patch/Map.hs+1−10
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE LambdaCase #-}
32{-# LANGUAGE TypeFamilies #-}
43-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
@@ -18,8 +17,7 @@ import Data.Semigroup
1817newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
1918 deriving (Show, Read, Eq, Ord)
2019
21−-- | Applying a 'PatchMap' will update the 'Map' by performing the insertions
22−-- and deletions specified
20+-- | Apply the insertions or deletions to a given 'Map'.
2321instance Ord k => Patch (PatchMap k v) where
2422 type PatchTarget (PatchMap k v) = Map k v
2523 {-# INLINABLE apply #-}
@@ -36,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
3634instance Ord k => Semigroup (PatchMap k v) where
3735 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3836 -- PatchMap is idempotent, so stimes n is id for every n
39−#if MIN_VERSION_semigroups(0,17,0)
4037 stimes = stimesIdempotentMonoid
41−#else
42− times1p n x = case compare n 0 of
43− LT -> error "stimesIdempotentMonoid: negative multiplier"
44− EQ -> mempty
45− GT -> x
46−#endif
4738
4839-- | The empty 'PatchMap' contains no insertions or deletions
4940instance Ord k => Monoid (PatchMap k v) where
src/Reflex/Patch/MapWithMove.hs+59−17
@@ -20,9 +20,9 @@ import Data.List
2020import Data.Map (Map)
2121import qualified Data.Map as Map
2222import Data.Maybe
23−import Data.Semigroup
23+import 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@
@@ -41,6 +41,18 @@ data NodeInfo k v = NodeInfo
4141 }
4242 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
4343
44+-- | Describe how a key's new value should be produced
45+data From k v
46+ = From_Insert v -- ^ Insert the given value here
47+ | From_Delete -- ^ Delete the existing value, if any, from here
48+ | From_Move !k -- ^ Move the value here from the given key
49+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
50+
51+-- | Describe where a key's old value will go. If this is 'Just', that means
52+-- the key's old value will be moved to the given other key; if it is 'Nothing',
53+-- that means it will be deleted.
54+type To = Maybe
55+
4456-- | Create a 'PatchMapWithMove', validating it
4557patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
4658patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
@@ -62,22 +74,51 @@ patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
6274unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
6375unPatchMapWithMove (PatchMapWithMove p) = p
6476
65−-- | Warning: when using this function, you must ensure that the invariants of
66−-- 'PatchMapWithMove' are preserved; they will not be checked.
67−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
68−unsafePatchMapWithMove = PatchMapWithMove
77+-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
78+insertMapKey :: k -> v -> PatchMapWithMove k v
79+insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
6980
70−-- | Describe how a key's new value should be produced
71−data From k v
72− = From_Insert v -- ^ Insert the given value here
73− | From_Delete -- ^ Delete the existing value, if any, from here
74− | From_Move !k -- ^ Move the value here from the given key
75− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
81+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
82+--
83+-- @
84+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
85+-- @
86+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
87+moveMapKey src dst
88+ | src == dst = mempty
89+ | otherwise =
90+ PatchMapWithMove $ Map.fromList
91+ [ (dst, NodeInfo (From_Move src) Nothing)
92+ , (src, NodeInfo From_Delete (Just dst))
93+ ]
7694
77−-- | Describe where a key's old value will go. If this is 'Just', that means
78−-- the key's old value will be moved to the given other key; if it is 'Nothing',
79−-- that means it will be deleted.
80−type To = Maybe
95+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
96+--
97+-- @
98+-- let aMay = Map.lookup a map
99+-- bMay = Map.lookup b map
100+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
101+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
102+-- . Map.delete a . Map.delete b $ map
103+-- @
104+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
105+swapMapKey src dst
106+ | src == dst = mempty
107+ | otherwise =
108+ PatchMapWithMove $ Map.fromList
109+ [ (dst, NodeInfo (From_Move src) (Just src))
110+ , (src, NodeInfo (From_Move dst) (Just dst))
111+ ]
112+
113+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
114+deleteMapKey :: k -> PatchMapWithMove k v
115+deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
116+
117+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
118+--
119+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
120+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
121+unsafePatchMapWithMove = PatchMapWithMove
81122
82123-- | Apply the insertions, deletions, and moves to a given 'Map'
83124instance Ord k => Patch (PatchMapWithMove k v) where
@@ -91,10 +132,11 @@ instance Ord k => Patch (PatchMapWithMove k v) where
91132 From_Delete -> Just ()
92133 _ -> Nothing
93134
94−-- | Returns all the new elements that will be added to the 'Map'
135+-- | Returns all the new elements that will be added to the 'Map'.
95136patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
96137patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
97138
139+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
98140patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
99141patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
100142 where f ni = case _nodeInfo_from ni of
src/Reflex/PerformEvent/Base.hs+1−1
@@ -141,7 +141,7 @@ hostPerformEventT a = do
141141 case mToPerform of
142142 Nothing -> return [result']
143143 Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
144+ responses <- runHostFrame $ traverseRequesterData (Identity <$>) toPerform
145145 mrt <- readRef responseTrigger
146146 let followupEventTriggers = case mrt of
147147 Just rt -> [rt :=> Identity responses]
src/Reflex/PerformEvent/Class.hs−1
@@ -5,7 +5,6 @@
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE RankNTypes #-}
109{-# LANGUAGE ScopedTypeVariables #-}
1110{-# LANGUAGE TypeFamilies #-}
src/Reflex/PostBuild/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE DataKinds #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
109{-# LANGUAGE TypeOperators #-}
src/Reflex/Profiled.hs+30−16
@@ -8,9 +8,17 @@
88{-# LANGUAGE StandaloneDeriving #-}
99{-# LANGUAGE TypeFamilies #-}
1010{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE RankNTypes #-}
14+-- |
15+-- Module:
16+-- Reflex.Profiled
17+-- Description:
18+-- This module contains an instance of the 'Reflex' class that provides
19+-- profiling/cost-center information.
1120module Reflex.Profiled where
1221
13−import Control.Lens hiding (children)
1422import Control.Monad
1523import Control.Monad.Exception
1624import Control.Monad.Fix
@@ -27,6 +35,7 @@ import Data.Map (Map)
2735import qualified Data.Map.Strict as Map
2836import Data.Monoid ((<>))
2937import Data.Ord
38+import Data.Profunctor.Unsafe ((#.))
3039import qualified Data.Semigroup as S
3140import Data.Type.Coercion
3241import Foreign.Ptr
@@ -71,9 +80,9 @@ getCostCentreStack = go []
7180 go (cc : l) parent
7281
7382toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
74−toCostCentreTree ccs n = do
75− ccList <- getCostCentreStack ccs
76− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
83+toCostCentreTree ccs n =
84+ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
85+ <$> getCostCentreStack ccs
7786
7887getCostCentreTree :: IO CostCentreTree
7988getCostCentreTree = do
@@ -127,27 +136,32 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
127136 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
128137 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
129138 pull = Behavior_Profiled . pull . coerce
130− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
131− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
132− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
139+ fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
140+ mergeG :: forall (k :: z -> *) q v. GCompare k
141+ => (forall a. q a -> Event (ProfiledTimeline t) (v a))
142+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
143+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
133144 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
134145 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
135146 current (Dynamic_Profiled d) = coerce $ current d
136147 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
137148 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
138149 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
139− mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
140− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
150+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
151+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
141152 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
142153 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
143154 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
144− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
145− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
146− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
147− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
148− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
149− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
150− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
155+ behaviorCoercion c =
156+ Coercion `trans` behaviorCoercion @t c `trans` Coercion
157+ eventCoercion c =
158+ Coercion `trans` eventCoercion @t c `trans` Coercion
159+ dynamicCoercion c =
160+ Coercion `trans` dynamicCoercion @t c `trans` Coercion
161+ incrementalCoercion c d =
162+ Coercion `trans` incrementalCoercion @t c d `trans` Coercion
163+ mergeIntIncremental = Event_Profiled . mergeIntIncremental .
164+ coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
151165 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
152166
153167deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
src/Reflex/Pure.hs+32−26
@@ -5,6 +5,9 @@
55{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
811#ifdef USE_REFLEX_OPTIMIZER
912{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1013#endif
@@ -13,11 +16,13 @@
1316-- * MonadSample (Pure t) ((->) t)
1417-- * MonadHold (Pure t) ((->) t)
1518{-# 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.
19+-- |
20+-- Module: Reflex.Pure
21+-- Description:
22+-- This module provides a pure implementation of Reflex, which is intended to
23+-- serve as a reference for the semantics of the Reflex class. All
24+-- implementations of Reflex should produce the same results as this
25+-- implementation, although performance and laziness/strictness may differ.
2126module Reflex.Pure
2227 ( Pure
2328 , Behavior (..)
@@ -26,14 +31,9 @@ module Reflex.Pure
2631 , Incremental (..)
2732 ) where
2833
29−#if !MIN_VERSION_base(4,8,0)
30−import Control.Applicative
31−#endif
32−
3334import Control.Monad
3435import Data.Dependent.Map (DMap, GCompare)
3536import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Identity
3737import Data.IntMap (IntMap)
3838import qualified Data.IntMap as IntMap
3939import Data.Maybe
@@ -41,14 +41,15 @@ import Data.MemoTrie
4141import Data.Monoid
4242import Data.Type.Coercion
4343import Reflex.Class
44+import Data.Kind (Type)
4445
4546-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
46−-- with the type @t@.
47−data Pure t
47+-- with the type @/t/@.
48+data Pure (t :: Type)
4849
49−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
50−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
51−-- exclusively to memoize functions of t, not for any of its other capabilities.
50+-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
51+-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
52+-- exclusively to memoize functions of @/t/@, not for any of its other capabilities.
5253instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
5354
5455 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
@@ -77,17 +78,19 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
7778 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
7879 -- warning because the GCompare instance is not used; however, removing the
7980 -- GCompare instance produces a different warning, due to that constraint
80− -- being present in the original class definition
81+ -- being present in the original class definition.
8182
82− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
83− merge events = Event $ memo $ \t ->
84− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
83+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
84+ -- -> DMap k q -> Event (Pure t) (DMap k v)
85+ mergeG nt events = Event $ memo $ \t ->
86+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
8587 in if DMap.null currentOccurrences
8688 then Nothing
8789 else Just currentOccurrences
8890
89− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
90− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
91+-- The instance signature doeesn't compile, leave commented for documentation
92+-- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
93+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
9194
9295 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
9396 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
@@ -110,8 +113,8 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
110113 --a) -> Incremental (Pure t) p a
111114 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
112115
113− mergeIncremental = mergeIncrementalImpl
114− mergeIncrementalWithMove = mergeIncrementalImpl
116+ mergeIncrementalG = mergeIncrementalImpl
117+ mergeIncrementalWithMoveG = mergeIncrementalImpl
115118
116119 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
117120
@@ -126,14 +129,17 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
126129 behaviorCoercion Coercion = Coercion
127130 eventCoercion Coercion = Coercion
128131 dynamicCoercion Coercion = Coercion
132+ incrementalCoercion Coercion Coercion = Coercion
129133
130134 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
131135
132136 mergeIntIncremental = mergeIntIncrementalImpl
133137
134−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
135−mergeIncrementalImpl i = Event $ \t ->
136− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
138+mergeIncrementalImpl :: (PatchTarget p ~ DMap k q, GCompare k)
139+ => (forall a. q a -> Event (Pure t) (v a))
140+ -> Incremental (Pure t) p -> Event (Pure t) (DMap k v)
141+mergeIncrementalImpl nt i = Event $ \t ->
142+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
137143 in if DMap.null results
138144 then Nothing
139145 else Just results
src/Reflex/Query/Base.hs+33−37
@@ -1,7 +1,6 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE GeneralizedNewtypeDeriving #-}
33{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
54{-# LANGUAGE LambdaCase #-}
65{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
@@ -22,6 +21,7 @@ module Reflex.Query.Base
2221import Control.Applicative (liftA2)
2322import Control.Monad.Exception
2423import Control.Monad.Fix
24+import Control.Monad.Primitive
2525import Control.Monad.Reader
2626import Control.Monad.Ref
2727import Control.Monad.State.Strict
@@ -37,8 +37,7 @@ import Data.Map (Map)
3737import qualified Data.Map as Map
3838import Data.Monoid ((<>))
3939import qualified Data.Semigroup as S
40−import Data.Some (Some)
41−import qualified Data.Some as Some
40+import Data.Some (Some(Some))
4241import Data.These
4342
4443import Reflex.Class
@@ -73,26 +72,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
7372getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
7473getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
7574
76−{-
77−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
78− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
79− bs' = fmapCheap snd $ r'
80− patches = unsafeBuildIncremental (sampleBs bs0) $
81− flip pushCheap bs' $ \bs -> do
82− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
83− return (Just (AdditivePatch p))
84−-}
75+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
76+maskMempty x = if x == mempty then Nothing else Just x
8577
86−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
78+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
8779 runWithReplace (QueryT a0) a' = do
8880 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
8981 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
9082 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
9183 bs' = fmapCheap snd $ r'
9284 bbs <- hold bs0 bs'
93− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
85+ let patches = flip pushCheap bs' $ \newBs -> do
9486 oldBs <- sample bbs
95− (~~) <$> sampleBs newBs <*> sampleBs oldBs
87+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
9688 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
9789 QueryT $ lift $ tellEvent patches
9890 return (r0, fmapCheap fst r')
@@ -151,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
151143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
152144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
153145 liftedBs0 :: Map (Some k) [Behavior t q]
154− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
146+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
155147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
156148 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
157− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
149+ Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
158150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
159151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
160152 accumBehaviors :: forall m'. MonadHold t m'
@@ -170,19 +162,19 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
170162 let p k bs = case Map.lookup k bs0 of
171163 Nothing -> case bs of
172164 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
173− Nothing -> return mempty
165+ Nothing -> return Nothing
174166 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
175− Just newBs -> sampleBs newBs
167+ Just newBs -> maskMempty <$> sampleBs newBs
176168 Just oldBs -> case bs of
177169 -- 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
178− Nothing -> negateG <$> sampleBs oldBs
170+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
179171 -- 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
180172 -- composed with the sampling the child's new state.
181− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
173+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
182174 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
183175 -- child patches and wrap them in AdditivePatch.
184− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
185− return (apply pbs bs0, Just patch)
176+ patch <- fold <$> Map.traverseWithKey p bs'
177+ return (apply pbs bs0, AdditivePatch <$> patch)
186178 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
187179 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
188180 return (liftedResult0, liftedResult')
@@ -195,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
195187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
196188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
197189 liftedBs0 :: Map (Some k) [Behavior t q]
198− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
190+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
199191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
200192 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
201− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
193+ Map.fromDistinctAscList $ (\(k :=> mr) -> (Some k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
202194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
203195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
204196 accumBehaviors' :: forall m'. MonadHold t m'
@@ -215,28 +207,28 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
215207 p k bs = case Map.lookup k bs0 of
216208 Nothing -> case MapWithMove._nodeInfo_from bs of
217209 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
218− MapWithMove.From_Delete -> return mempty
210+ MapWithMove.From_Delete -> return Nothing
219211 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
220− MapWithMove.From_Insert newBs -> sampleBs newBs
212+ MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs
221213 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
222− Nothing -> return mempty
223− Just newBs -> sampleBs newBs
214+ Nothing -> return Nothing
215+ Just newBs -> maskMempty <$> sampleBs newBs
224216 Just oldBs -> case MapWithMove._nodeInfo_from bs of
225217 -- 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
226− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
218+ MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs
227219 -- 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
228220 -- composed with the sampling the child's new state.
229− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
221+ MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
230222 MapWithMove.From_Move k'
231− | k' == k -> return mempty
223+ | k' == k -> return Nothing
232224 | otherwise -> case Map.lookup k' bs0 of
233225 -- If we are moving from a non-existent key, that is a delete
234− Nothing -> negateG <$> sampleBs oldBs
235− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
226+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
227+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
236228 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
237229 -- child patches and wrap them in AdditivePatch.
238− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
239− return (apply pbs bs0, Just patch)
230+ patch <- fold <$> Map.traverseWithKey p bs'
231+ return (apply pbs bs0, AdditivePatch <$> patch)
240232 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
241233 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
242234 return (liftedResult0, liftedResult')
@@ -244,6 +236,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
244236instance MonadTrans (QueryT t q) where
245237 lift = QueryT . lift . lift . lift
246238
239+instance PrimMonad m => PrimMonad (QueryT t q m) where
240+ type PrimState (QueryT t q m) = PrimState m
241+ primitive = lift . primitive
242+
247243instance PostBuild t m => PostBuild t (QueryT t q m) where
248244 getPostBuild = lift getPostBuild
249245
@@ -335,5 +331,5 @@ instance Requester t m => Requester t (QueryT t q m) where
335331instance EventWriter t w m => EventWriter t w (QueryT t q m) where
336332 tellEvent = lift . tellEvent
337333
338−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (QueryT t q m) where
334+instance DynamicWriter t w m => DynamicWriter t w (QueryT t q m) where
339335 tellDyn = lift . tellDyn
src/Reflex/Query/Class.hs+25−6
@@ -3,9 +3,15 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
8+-- |
9+-- Module:
10+-- Reflex.Query.Class
11+-- Description:
12+-- A class that ties together queries to some data source and their results,
13+-- providing methods for requesting data from the source and accumulating
14+-- streamed results.
915module Reflex.Query.Class
1016 ( Query (..)
1117 , QueryMorphism (..)
@@ -27,11 +33,15 @@ import Data.Data
2733import Data.Ix
2834import Data.Map.Monoidal (MonoidalMap)
2935import qualified Data.Map.Monoidal as MonoidalMap
30−import Data.Semigroup
36+import Data.Semigroup (Semigroup(..))
3137import Foreign.Storable
3238
3339import Reflex.Class
3440
41+-- | A 'Query' can be thought of as a declaration of interest in some set of data.
42+-- A 'QueryResult' is the set of data associated with that interest set.
43+-- The @crop@ function provides a way to determine what part of a given 'QueryResult'
44+-- is relevant to a given 'Query'.
3545class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
3646 type QueryResult a :: *
3747 crop :: a -> QueryResult a -> QueryResult a
@@ -40,8 +50,8 @@ instance (Ord k, Query v) => Query (MonoidalMap k v) where
4050 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
4151 crop q r = MonoidalMap.intersectionWith (flip crop) r q
4252
43−-- | NB: 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
53+-- | QueryMorphism's must be group homomorphisms when acting on the query type
54+-- and compatible with the query relationship when acting on the query result.
4555data QueryMorphism q q' = QueryMorphism
4656 { _queryMorphism_mapQuery :: q -> q'
4757 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
@@ -54,13 +64,16 @@ instance Category QueryMorphism where
5464 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
5565 }
5666
67+-- | Apply a 'QueryMorphism' to a 'Query'
5768mapQuery :: QueryMorphism q q' -> q -> q'
5869mapQuery = _queryMorphism_mapQuery
5970
71+-- | Map a 'QueryMorphism' to a 'QueryResult'
6072mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
6173mapQueryResult = _queryMorphism_mapQueryResult
6274
63−-- | This type keeps track of the multiplicity of elements of the view selector that are being used by the app
75+-- | This type can be used to track of the frequency of interest in a given 'Query'. See note on
76+-- 'combineSelectedCounts'
6477newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
6578 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
6679
@@ -76,10 +89,14 @@ instance Group SelectedCount where
7689
7790instance Additive SelectedCount
7891
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.
92+-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
93+-- this function which returns Nothing if the result is 0. This allows the pruning of leaves
94+-- of the 'Query' that are no longer wanted.
8095combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
8196combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
8297
98+-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
99+-- used interface.
83100class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
84101 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
85102 askQueryResult :: m (Dynamic t (QueryResult q))
@@ -90,9 +107,11 @@ instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
90107 askQueryResult = lift askQueryResult
91108 queryIncremental = lift . queryIncremental
92109
110+-- | Produce and send an 'Incremental' 'Query' from a 'Dynamic' 'Query'.
93111tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
94112tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
95113
114+-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
96115queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
97116queryDyn q = do
98117 tellQueryDyn q
src/Reflex/Requester/Base.hs+113−5
@@ -21,14 +21,20 @@
2121module Reflex.Requester.Base
2222 ( RequesterT (..)
2323 , runRequesterT
24+ , withRequesterT
2425 , runWithReplaceRequesterTWith
2526 , traverseIntMapWithKeyWithAdjustRequesterTWith
2627 , traverseDMapWithKeyWithAdjustRequesterTWith
2728 , RequesterData
2829 , RequesterDataKey
2930 , traverseRequesterData
31+ , forRequesterData
3032 , requesterDataToList
3133 , singletonRequesterData
34+ , matchResponsesWithRequests
35+ , multiEntry
36+ , unMultiEntry
37+ , requesting'
3238 ) where
3339
3440import Reflex.Class
@@ -43,6 +49,7 @@ import Reflex.TriggerEvent.Class
4349import Control.Applicative (liftA2)
4450import Control.Monad.Exception
4551import Control.Monad.Identity
52+import Control.Monad.Primitive
4653import Control.Monad.Reader
4754import Control.Monad.Ref
4855import Control.Monad.State.Strict
@@ -59,8 +66,7 @@ import qualified Data.Map as Map
5966import Data.Monoid ((<>))
6067import Data.Proxy
6168import qualified Data.Semigroup as S
62−import Data.Some (Some)
63−import qualified Data.Some as Some
69+import Data.Some (Some(Some))
6470import Data.Type.Equality
6571import Data.Unique.Tag
6672
@@ -130,6 +136,10 @@ traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWith
130136 MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
131137 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
132138
139+-- | 'traverseRequesterData' with its arguments flipped
140+forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response)
141+forRequesterData r f = traverseRequesterData f r
142+
133143data MyTagType :: * -> * where
134144 MyTagType_Single :: MyTagType (Single a)
135145 MyTagType_Multi :: MyTagType Multi
@@ -239,6 +249,10 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
239249deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
240250deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
241251
252+instance PrimMonad m => PrimMonad (RequesterT t request response m) where
253+ type PrimState (RequesterT t request response m) = PrimState m
254+ primitive = lift . primitive
255+
242256-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
243257instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
244258 mempty = pure mempty
@@ -252,7 +266,6 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response
252266-- requests are made, and responses should be provided in the input 'Event'.
253267-- The 'Tag' keys will be used to return the responses to the same place the
254268-- requests were issued.
255−
256269runRequesterT :: (Reflex t, Monad m)
257270 => RequesterT t request response m a
258271 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
@@ -262,6 +275,20 @@ runRequesterT (RequesterT a) responses = do
262275 coerceEvent responses
263276 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
264277
278+-- | Map a function over the request and response of a 'RequesterT'
279+withRequesterT
280+ :: (Reflex t, MonadFix m)
281+ => (forall x. req x -> req' x) -- ^ The function to map over the request
282+ -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response
283+ -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed
284+ -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT'
285+withRequesterT freq frsp child = do
286+ rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp'
287+ (a, req) <- lift $ runRequesterT child rsp
288+ rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $
289+ fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req
290+ return a
291+
265292instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
266293 type Request (RequesterT t request response m) = request
267294 type Response (RequesterT t request response m) = response
@@ -365,7 +392,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
365392 pack = Entry
366393 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
367394 f' k (n, v) = do
368− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
395+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
369396 return (fmapCheap (IntMap.singleton n) myRequests, result)
370397 ndm' <- numberOccurrencesFrom 1 dm'
371398 (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
@@ -413,7 +440,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
413440 pack = Entry
414441 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
415442 f' k (Compose (n, v)) = do
416− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
443+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
417444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
418445 ndm' <- numberOccurrencesFrom 1 dm'
419446 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
@@ -427,3 +454,84 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
427454 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
428455 requests <- holdIncremental requests0 requests'
429456 return (result0, result')
457+
458+data Decoder rawResponse response =
459+ forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
460+
461+-- | Matches incoming responses with previously-sent requests
462+-- and uses the provided request "decoder" function to process
463+-- incoming responses.
464+matchResponsesWithRequests
465+ :: forall t rawRequest rawResponse request response m.
466+ ( MonadFix m
467+ , MonadHold t m
468+ , Reflex t
469+ )
470+ => (forall a. request a -> (rawRequest, rawResponse -> response a))
471+ -- ^ Given a request (from 'Requester'), produces the wire format of the
472+ -- request and a function used to process the associated response
473+ -> Event t (RequesterData request)
474+ -- ^ The outgoing requests
475+ -> Event t (Int, rawResponse)
476+ -- ^ The incoming responses, tagged by an identifying key
477+ -> m ( Event t (Map Int rawRequest)
478+ , Event t (RequesterData response)
479+ )
480+ -- ^ A map of outgoing wire-format requests and an event of responses keyed
481+ -- by the 'RequesterData' key of the associated outgoing request
482+matchResponsesWithRequests f send recv = do
483+ rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
484+ waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
485+ holdIncremental mempty $ leftmost
486+ [ fmap (\(_, outstanding, _) -> outstanding) outgoing
487+ , snd <$> incoming
488+ ]
489+ let outgoing = processOutgoing nextId send
490+ incoming = processIncoming waitingFor recv
491+ return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming)
492+ where
493+ -- Tags each outgoing request with an identifying integer key
494+ -- and returns the next available key, a map of response decoders
495+ -- for requests for which there are outstanding responses, and the
496+ -- raw requests to be sent out.
497+ processOutgoing
498+ :: Behavior t Int
499+ -- The next available key
500+ -> Event t (RequesterData request)
501+ -- The outgoing request
502+ -> Event t ( Int
503+ , PatchMap Int (Decoder rawResponse response)
504+ , Map Int rawRequest )
505+ -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests
506+ processOutgoing nextId out = flip pushAlways out $ \dm -> do
507+ oldNextId <- sample nextId
508+ let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do
509+ n <- get
510+ put $ succ n
511+ let (rawReq, rspF) = f v
512+ return (n, rawReq, Decoder k rspF)
513+ patchWaitingFor = PatchMap $ Map.fromList $
514+ (\(n, _, dec) -> (n, Just dec)) <$> result
515+ toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result
516+ return (newNextId, patchWaitingFor, toSend)
517+ -- Looks up the each incoming raw response in a map of response
518+ -- decoders and returns the decoded response and a patch that can
519+ -- be used to clear the ID of the consumed response out of the queue
520+ -- of expected responses.
521+ processIncoming
522+ :: Incremental t (PatchMap Int (Decoder rawResponse response))
523+ -- A map of outstanding expected responses
524+ -> Event t (Int, rawResponse)
525+ -- A incoming response paired with its identifying key
526+ -> Event t (RequesterData response, PatchMap Int v)
527+ -- The decoded response and a patch that clears the outstanding responses queue
528+ processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do
529+ wf <- sample $ currentIncremental waitingFor
530+ case Map.lookup n wf of
531+ Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
532+ Just (Decoder k rspF) -> do
533+ let rsp = rspF rawRsp
534+ return $ Just
535+ ( singletonRequesterData k rsp
536+ , PatchMap $ Map.singleton n Nothing
537+ )
src/Reflex/Requester/Class.hs−1
@@ -6,7 +6,6 @@
66{-# LANGUAGE CPP #-}
77{-# LANGUAGE FlexibleInstances #-}
88{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE RecursiveDo #-}
1110{-# LANGUAGE TypeFamilies #-}
1211{-# LANGUAGE UndecidableInstances #-}
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+291−218
@@ -9,7 +9,6 @@
99{-# LANGUAGE GADTs #-}
1010{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1111{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
1312{-# LANGUAGE MultiWayIf #-}
1413{-# LANGUAGE RankNTypes #-}
1514{-# LANGUAGE RoleAnnotations #-}
@@ -17,9 +16,15 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
2024#ifdef USE_REFLEX_OPTIMIZER
2125{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2226#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
2328-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2429-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2530module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -33,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
3338import Control.Monad.Exception
3439import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
3540import Control.Monad.Primitive
36−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
41+--import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
42+import Control.Monad.Reader.Class
43+import Control.Monad.IO.Class
44+import Control.Monad.ReaderIO
3745import Control.Monad.Ref
3846import Data.Align
3947import Data.Coerce
@@ -49,20 +57,30 @@ import Data.GADT.Compare
4957import Data.IntMap.Strict (IntMap)
5058import qualified Data.IntMap.Strict as IntMap
5159import Data.IORef
52−import Data.Maybe
60+import Data.Kind (Type)
61+import Data.Maybe hiding (mapMaybe)
5362import Data.Monoid ((<>))
5463import Data.Proxy
5564import Data.These
5665import Data.Traversable
66+import Data.Witherable (Filterable, mapMaybe)
5767import GHC.Exts
5868import GHC.IORef (IORef (..))
5969import GHC.Stack
6070import Reflex.FastWeak
61−import Reflex.FunctorMaybe
6271import System.IO.Unsafe
6372import System.Mem.Weak
6473import Unsafe.Coerce
6574
75+#ifdef MIN_VERSION_semialign
76+#if MIN_VERSION_these(0,8,0)
77+import Data.These.Combinators (justThese)
78+#endif
79+#if MIN_VERSION_semialign(1,1,0)
80+import Data.Zip (Zip (..))
81+#endif
82+#endif
83+
6684#ifdef DEBUG_CYCLES
6785import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
6886import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -74,9 +92,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
7492import Data.FastWeakBag (FastWeakBag)
7593import qualified Data.FastWeakBag as FastWeakBag
7694import Data.Reflection
77−import Data.Some (Some)
78−import qualified Data.Some as Some
95+import Data.Some (Some(Some))
7996import Data.Type.Coercion
97+import Data.Profunctor.Unsafe ((#.), (.#))
8098import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
8199import qualified Data.WeakBag as WeakBag
82100import qualified Reflex.Class
@@ -193,12 +211,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193211
194212newNodeId :: IO Int
195213newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202214#endif
203215
204216--------------------------------------------------------------------------------
@@ -302,9 +314,10 @@ cacheEvent e =
302314#else
303315 Event $
304316#endif
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
317+ unsafePerformIO $ do
318+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
319+ <- newIORef emptyFastWeak
320+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308321#ifdef DEBUG_TRACE_EVENTS
309322 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310323#endif
@@ -319,9 +332,9 @@ cacheEvent e =
319332 scheduleClear occRef
320333 propagateFast a subscribers
321334 , subscriberInvalidateHeight = \old -> do
322− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
335+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
323336 , subscriberRecalculateHeight = \new -> do
324− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
337+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
325338 }
326339 when (isJust occ) $ do
327340 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
@@ -370,7 +383,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370383eventNever :: Event x a
371384eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372385
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
386+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
374387eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375388
376389eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +435,14 @@ newSubscriberHold h = return $ Subscriber
422435 , subscriberRecalculateHeight = \_ -> return ()
423436 }
424437
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
438+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
426439newSubscriberFan subscribed = return $ Subscriber
427440 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428441 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429442 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430443 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431444 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
445+ let f _ (Pair v subsubs) = do
433446 propagate v $ _fanSubscribedChildren_list subsubs
434447 return $ Constant ()
435448 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -437,12 +450,12 @@ newSubscriberFan subscribed = return $ Subscriber
437450 , subscriberInvalidateHeight = \old -> do
438451 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
439452 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
440− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
453+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
441454 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
442455 , subscriberRecalculateHeight = \new -> do
443456 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
444457 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
445− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
458+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
446459 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
447460 }
448461
@@ -458,7 +471,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
458471 oldHeight <- readIORef $ switchSubscribedHeight subscribed
459472 when (oldHeight /= invalidHeight) $ do
460473 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
461− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
474+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
462475 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
463476 , subscriberRecalculateHeight = \new -> do
464477 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -481,8 +494,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
481494 Nothing -> do
482495 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
483496 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
485− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
497+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
486499 Just o -> do -- Since it's already firing, no need to adjust height
487500 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
488501 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -529,14 +542,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
529542propagate a subscribers = withIncreasedDepth $ do
530543 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
531544 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
532− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
545+ WeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
533546
534547-- | Propagate everything at the current height
535548propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
536549propagateFast a subscribers = withIncreasedDepth $ do
537550 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
538551 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
539− FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
552+ FastWeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
540553
541554--------------------------------------------------------------------------------
542555-- EventSubscribed
@@ -545,6 +558,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
545558toAny :: a -> Any
546559toAny = unsafeCoerce
547560
561+-- Why do we use Any here, instead of just giving eventSubscribedRetained an
562+-- existential type? Sadly, GHC does not currently know how to unbox types
563+-- with existentially quantified fields. So instead we just coerce values
564+-- to type Any on the way in. Since we never coerce them back, this is
565+-- perfectly safe.
548566data EventSubscribed x = EventSubscribed
549567 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550568 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -577,12 +595,12 @@ eventSubscribedNever = EventSubscribed
577595#endif
578596 }
579597
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
598+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581599eventSubscribedFan !subscribed = EventSubscribed
582600 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583601 , eventSubscribedRetained = toAny subscribed
584602#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
603+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586604 , eventSubscribedHasOwnHeightRef = False
587605 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588606#endif
@@ -595,7 +613,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595613#ifdef DEBUG_CYCLES
596614 , eventSubscribedGetParents = do
597615 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
616+ return [Some $ _eventSubscription_subscribed s]
599617 , eventSubscribedHasOwnHeightRef = True
600618 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601619#endif
@@ -608,8 +626,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608626#ifdef DEBUG_CYCLES
609627 , eventSubscribedGetParents = do
610628 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
629+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
630+ innerParents = maybeToList $ fmap Some innerSubscription
613631 return $ outerParent : innerParents
614632 , eventSubscribedHasOwnHeightRef = True
615633 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +643,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625643
626644walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627645walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
646+ subscribers <- flip execStateT mempty $ ($ Some s0) $ fix $ \loop (Some s) -> do
629647 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630648 when (h == invalidHeight) $ do
631649 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
650+ modify (Some s :)
633651 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
652+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635653 return subscribers
636654#endif
637655
@@ -659,7 +677,7 @@ behaviorPull !p = Behavior $ do
659677 val <- liftIO $ readIORef $ pullValue p
660678 case val of
661679 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
680+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663681 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664682 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665683 return $ pullSubscribedValue subscribed
@@ -668,7 +686,7 @@ behaviorPull !p = Behavior $ do
668686 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669687 parentsRef <- liftIO $ newIORef []
670688 holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
689+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672690 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673691 parents <- liftIO $ readIORef parentsRef
674692 let subscribed = PullSubscribed
@@ -678,7 +696,7 @@ behaviorPull !p = Behavior $ do
678696 , pullSubscribedParents = parents
679697 }
680698 liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
699+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
682700 return a
683701
684702behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +707,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689707readHoldTracked h = do
690708 result <- liftIO $ readIORef $ holdValue h
691709 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
710+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
693711 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694712 return result
695713
@@ -703,42 +721,46 @@ readBehaviorUntracked b = do
703721-- Dynamic
704722--------------------------------------------------------------------------------
705723
706−data Dynamic x p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
708− , dynamicUpdated :: !(Event x p)
724+type DynamicS x p = Dynamic x (PatchTarget p) p
725+
726+data Dynamic x target p = Dynamic
727+ { dynamicCurrent :: !(Behavior x target)
728+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709729 }
710730
711−dynamicHold :: Hold x p -> Dynamic x p
731+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
732+
733+
734+
735+
736+dynamicHold :: Hold x p -> DynamicS x p
712737dynamicHold !h = Dynamic
713738 { dynamicCurrent = behaviorHold h
714739 , dynamicUpdated = eventHold h
715740 }
716741
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
742+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
718743dynamicHoldIdentity = dynamicHold
719744
720−dynamicConst :: PatchTarget p -> Dynamic x p
745+dynamicConst :: PatchTarget p -> DynamicS x p
721746dynamicConst !a = Dynamic
722747 { dynamicCurrent = behaviorConst a
723748 , dynamicUpdated = eventNever
724749 }
725750
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
751+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
727752dynamicDyn !d = Dynamic
728753 { dynamicCurrent = behaviorDyn d
729754 , dynamicUpdated = eventDyn d
730755 }
731756
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
757+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
733758dynamicDynIdentity = dynamicDyn
734759
735760--------------------------------------------------------------------------------
736761-- Combinators
737762--------------------------------------------------------------------------------
738763
739−--TODO: Figure out why certain things are not 'representational', then make them
740−--representational so we can use coerce
741−
742764--type role Hold representational
743765data Hold x p
744766 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -759,20 +781,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759781
760782-- | Stores all global data relevant to a particular Spider timeline; only one
761783-- value should exist for each type @x@
762−data SpiderTimelineEnv x = SpiderTimelineEnv
784+newtype SpiderTimelineEnv x = STE {unSTE :: SpiderTimelineEnv' x}
785+-- We implement SpiderTimelineEnv with a newtype wrapper so
786+-- we can get the coercions we want safely.
787+type role SpiderTimelineEnv nominal
788+
789+data SpiderTimelineEnv' x = SpiderTimelineEnv
763790 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764791 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765792#ifdef DEBUG
766793 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767794#endif
768795 }
769−type role SpiderTimelineEnv nominal
796+type role SpiderTimelineEnv' phantom
770797
771798instance Eq (SpiderTimelineEnv x) where
772799 _ == _ = True -- Since only one exists of each type
773800
774801instance GEq SpiderTimelineEnv where
775− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
802+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
776803 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777804 else Nothing
778805
@@ -782,9 +809,9 @@ data EventEnv x
782809 , eventEnvDynInits :: !(IORef [SomeDynInit x])
783810 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784811 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
812+ , eventEnvClears :: !(IORef [Some Clear]) -- Needed for Subscribe
813+ , eventEnvIntClears :: !(IORef [Some IntClear])
814+ , eventEnvRootClears :: !(IORef [Some RootClear])
788815 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789816 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790817 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
@@ -795,7 +822,7 @@ runEventM :: EventM x a -> IO a
795822runEventM = unEventM
796823
797824asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
798−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
825+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
799826
800827class MonadIO m => Defer a m where
801828 getDeferralQueue :: m (IORef [a])
@@ -856,29 +883,29 @@ putCurrentHeight h = do
856883 heightRef <- asksEventEnv eventEnvCurrentHeight
857884 liftIO $ writeIORef heightRef $! h
858885
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
886+instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
860887 {-# INLINE getDeferralQueue #-}
861888 getDeferralQueue = asksEventEnv eventEnvClears
862889
863890{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
891+scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
892+scheduleClear r = defer $ Some $ Clear r
866893
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
894+instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868895 {-# INLINE getDeferralQueue #-}
869896 getDeferralQueue = asksEventEnv eventEnvIntClears
870897
871898{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
899+scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
900+scheduleIntClear r = defer $ Some $ IntClear r
874901
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
902+instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876903 {-# INLINE getDeferralQueue #-}
877904 getDeferralQueue = asksEventEnv eventEnvRootClears
878905
879906{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
907+scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
908+scheduleRootClear r = defer $ Some $ RootClear r
882909
883910instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884911 {-# INLINE getDeferralQueue #-}
@@ -933,9 +960,9 @@ getHoldEventSubscription h = do
933960
934961type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935962
936−--type role BehaviorM representational
937963-- BehaviorM can sample behaviors
938−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
964+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderIO (BehaviorEnv x) a }
965+ deriving (Functor, Applicative, MonadIO, MonadFix, MonadReader (BehaviorEnv x))
939966
940967instance Monad (BehaviorM x) where
941968 {-# INLINE (>>=) #-}
@@ -951,7 +978,7 @@ data BehaviorSubscribed x a
951978 = forall p. BehaviorSubscribedHold (Hold x p)
952979 | BehaviorSubscribedPull (PullSubscribed x a)
953980
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
981+newtype SomeBehaviorSubscribed x = SomeBehaviorSubscribed (Some (BehaviorSubscribed x))
955982
956983--type role PullSubscribed representational
957984data PullSubscribed x a
@@ -986,7 +1013,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
9861013#endif
9871014 }
9881015
989−data Root x (k :: * -> *)
1016+data Root x k
9901017 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
9911018 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
9921019 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1083,25 @@ heightBagVerify b@(HeightBag s c) = if
10561083heightBagVerify = id
10571084#endif
10581085
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1086+data FanSubscribedChildren x k v a = FanSubscribedChildren
1087+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1088+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1089+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
10631090 }
10641091
1065−data FanSubscribed (x :: *) k
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1092+data FanSubscribed x k v
1093+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1094+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1095+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
10691096 , fanSubscribedParent :: !(EventSubscription x)
10701097#ifdef DEBUG_NODEIDS
10711098 , fanSubscribedNodeId :: Int
10721099#endif
10731100 }
10741101
1075−data Fan x k
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1102+data Fan x k v
1103+ = Fan { fanParent :: !(Event x (DMap k v))
1104+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
10781105 }
10791106
10801107data SwitchSubscribed x a
@@ -1128,24 +1155,35 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
11281155newInvalidatorPull :: Pull x a -> IO (Invalidator x)
11291156newInvalidatorPull p = return $! InvalidatorPull p
11301157
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1132− fmapMaybe f = push $ return . f
1158+instance HasSpiderTimeline x => Filterable (Event x) where
1159+ mapMaybe f = push $ return . f
11331160
11341161instance HasSpiderTimeline x => Align (Event x) where
11351162 nil = eventNever
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1163+#if MIN_VERSION_these(0, 8, 0)
1164+instance HasSpiderTimeline x => Semialign (Event x) where
1165+#endif
1166+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1167+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1168+
1169+#ifdef MIN_VERSION_semialign
1170+#if MIN_VERSION_semialign(1,1,0)
1171+instance HasSpiderTimeline x => Zip (Event x) where
1172+#endif
1173+ zip x y = mapMaybe justThese $ align x y
1174+#endif
11371175
11381176data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11391177 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11401178 | HoldDyn !(Hold x p)
11411179
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1180+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11431181
1144−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1182+newMapDyn :: HasSpiderTimeline x => (a -> b) -> DynamicS x (Identity a) -> DynamicS x (Identity b)
11451183newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
11461184
11471185--TODO: Avoid the duplication between this and R.zipDynWith
1148−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1186+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> DynamicS x (Identity a) -> DynamicS x (Identity b) -> DynamicS x (Identity c)
11491187zipDynWith f da db =
11501188 let eab = align (dynamicUpdated da) (dynamicUpdated db)
11511189 ec = flip push eab $ \o -> do
@@ -1168,18 +1206,12 @@ buildDynamic readV0 v' = do
11681206 return d
11691207
11701208unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1172− where x = (readV0, v')
1209+unsafeBuildDynamic readV0 v' =
1210+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11731211
11741212-- ResultM can read behaviors and events
11751213type ResultM = EventM
11761214
1177−{-# NOINLINE unsafeNewIORef #-}
1178−unsafeNewIORef :: a -> b -> IORef b
1179−unsafeNewIORef a b = unsafePerformIO $ do
1180− touch a
1181− newIORef b
1182−
11831215instance HasSpiderTimeline x => Functor (Event x) where
11841216 fmap f = push $ return . Just . f
11851217
@@ -1192,33 +1224,42 @@ push f e = cacheEvent (pushCheap f e)
11921224
11931225{-# INLINABLE pull #-}
11941226pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1196− { pullCompute = a
1197− , pullValue = unsafeNewIORef a Nothing
1227+pull a = unsafePerformIO $ do
1228+ ref <- newIORef Nothing
11981229#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1230+ nid <- newNodeId
12001231#endif
1201− }
1232+ pure $ behaviorPull $ Pull
1233+ { pullCompute = a
1234+ , pullValue = ref
1235+#ifdef DEBUG_NODEIDS
1236+ , pullNodeId = nid
1237+#endif
1238+ }
12021239
12031240{-# INLINABLE switch #-}
12041241switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1205−switch a = eventSwitch $ Switch
1206− { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1208− }
1242+switch a = unsafePerformIO $ do
1243+ ref <- newIORef Nothing
1244+ pure $ eventSwitch $ Switch
1245+ { switchParent = a
1246+ , switchSubscribed = ref
1247+ }
12091248
12101249coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1211−coincidence a = eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1214− }
1250+coincidence a = unsafePerformIO $ do
1251+ ref <- newIORef Nothing
1252+ pure $ eventCoincidence $ Coincidence
1253+ { coincidenceParent = a
1254+ , coincidenceSubscribed = ref
1255+ }
12151256
12161257-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12171258run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
12181259run roots after = do
12191260 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
12201261 let t = spiderTimeline :: SpiderTimelineEnv x
1221− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1262+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
12221263 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
12231264 occBefore <- liftIO $ do
12241265 occBefore <- readIORef occRef
@@ -1256,11 +1297,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
12561297 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
12571298 EQ -> a
12581299
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1300+newtype Clear a = Clear (IORef (Maybe a))
12601301
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1302+newtype IntClear a = IntClear (IORef (IntMap a))
12621303
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1304+newtype RootClear k = RootClear (IORef (DMap k Identity))
12641305
12651306data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
12661307
@@ -1282,9 +1323,9 @@ type WeakList a = [Weak a]
12821323withIncreasedDepth :: CanTrace x m => m a -> m a
12831324withIncreasedDepth a = do
12841325 spiderTimeline <- askSpiderTimelineEnv
1285− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1326+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
12861327 result <- a
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1328+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
12881329 return result
12891330#else
12901331withIncreasedDepth :: m a -> m a
@@ -1311,7 +1352,7 @@ traceMWhen _ b getMessage = when b $ do
13111352 message <- getMessage
13121353#ifdef DEBUG
13131354 spiderTimeline <- askSpiderTimelineEnv
1314− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1355+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
13151356#else
13161357 let d = 0
13171358#endif
@@ -1352,25 +1393,25 @@ propagateSubscriberHold h a = do
13521393data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
13531394
13541395runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1355−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1396+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
13561397
13571398askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
13581399askInvalidator = do
1359− (!m, _) <- BehaviorM ask
1400+ (!m, _) <- ask
13601401 case m of
13611402 Nothing -> return Nothing
13621403 Just (!wi, _) -> return $ Just wi
13631404
13641405askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
13651406askParentsRef = do
1366− (!m, _) <- BehaviorM ask
1407+ (!m, _) <- ask
13671408 case m of
13681409 Nothing -> return Nothing
13691410 Just (_, !p) -> return $ Just p
13701411
13711412askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
13721413askBehaviorHoldInits = do
1373− (_, !his) <- BehaviorM ask
1414+ (_, !his) <- ask
13741415 return his
13751416
13761417{-# INLINE getDynHold #-}
@@ -1415,6 +1456,9 @@ getRootSubscribed k r sub = do
14151456 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14161457 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14171458 writeIORef uninitRef $! uninit
1459+#ifdef DEBUG_NODEIDS
1460+ nid <- newNodeId
1461+#endif
14181462 let !subscribed = RootSubscribed
14191463 { rootSubscribedKey = k
14201464 , rootSubscribedCachedSubscribed = cached
@@ -1423,7 +1467,7 @@ getRootSubscribed k r sub = do
14231467 , rootSubscribedUninit = uninit
14241468 , rootSubscribedWeakSelf = weakSelf
14251469#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1470+ , rootSubscribedNodeId = nid
14271471#endif
14281472 }
14291473 -- If we die at the same moment that all our children die, they will
@@ -1472,16 +1516,10 @@ newFanInt = do
14721516 , _fanInt_occRef = occRef
14731517 }
14741518
1475−{-# NOINLINE unsafeNewFanInt #-}
1476−unsafeNewFanInt :: b -> FanInt x a
1477−unsafeNewFanInt b = unsafePerformIO $ do
1478− touch b
1479− newFanInt
1480−
14811519fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1483− let self = unsafeNewFanInt p
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1520+fanInt p = unsafePerformIO $ do
1521+ self <- newFanInt
1522+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14851523 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14861524 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14871525 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1489,15 +1527,15 @@ fanInt p =
14891527 liftIO $ writeIORef (_fanInt_occRef self) m
14901528 scheduleIntClear $ _fanInt_occRef self
14911529 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1492− FastWeakBag.traverse b $ \s -> do
1530+ FastWeakBag.traverse_ b $ \s -> do
14931531 subscriberPropagate s v
14941532 , subscriberInvalidateHeight = \old -> do
14951533 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1496− FastWeakBag.traverse b $ \s -> do
1534+ FastWeakBag.traverse_ b $ \s -> do
14971535 subscriberInvalidateHeight s old
14981536 , subscriberRecalculateHeight = \new -> do
14991537 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1500− FastWeakBag.traverse b $ \s -> do
1538+ FastWeakBag.traverse_ b $ \s -> do
15011539 subscriberRecalculateHeight s new
15021540 }
15031541 liftIO $ do
@@ -1517,7 +1555,7 @@ fanInt p =
15171555 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15181556
15191557{-# INLINABLE getFanSubscribed #-}
1520−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1558+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k v -> Subscriber x (v a) -> EventM x (WeakBagTicket, FanSubscribed x k v, Maybe (v a))
15211559getFanSubscribed k f sub = do
15221560 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15231561 case mSubscribed of
@@ -1535,13 +1573,16 @@ getFanSubscribed k f sub = do
15351573 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15361574 occRef <- liftIO $ newIORef parentOcc
15371575 when (isJust parentOcc) $ scheduleClear occRef
1576+#ifdef DEBUG_NODEIDS
1577+ nid <- liftIO newNodeId
1578+#endif
15381579 let subscribed = FanSubscribed
15391580 { fanSubscribedCachedSubscribed = fanSubscribed f
15401581 , fanSubscribedOccurrence = occRef
15411582 , fanSubscribedParent = subscription
15421583 , fanSubscribedSubscribers = subscribersRef
15431584#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1585+ , fanSubscribedNodeId = nid
15451586#endif
15461587 }
15471588 let !self = (k, subscribed)
@@ -1551,7 +1592,7 @@ getFanSubscribed k f sub = do
15511592 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15521593 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15531594
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1595+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15551596cleanupFanSubscribed (k, subscribed) = do
15561597 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15571598 let reducedSubscribers = DMap.delete k subscribers
@@ -1563,7 +1604,7 @@ cleanupFanSubscribed (k, subscribed) = do
15631604 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15641605
15651606{-# INLINE subscribeFanSubscribed #-}
1566−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1607+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
15671608subscribeFanSubscribed k subscribed sub = do
15681609 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15691610 case DMap.lookup k subscribers of
@@ -1601,6 +1642,9 @@ getSwitchSubscribed s sub = do
16011642 when (isJust parentOcc) $ scheduleClear occRef
16021643 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16031644 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1645+#ifdef DEBUG_NODEIDS
1646+ nid <- liftIO newNodeId
1647+#endif
16041648 let !subscribed = SwitchSubscribed
16051649 { switchSubscribedCachedSubscribed = switchSubscribed s
16061650 , switchSubscribedOccurrence = occRef
@@ -1613,7 +1657,7 @@ getSwitchSubscribed s sub = do
16131657 , switchSubscribedCurrentParent = subscriptionRef
16141658 , switchSubscribedWeakSelf = weakSelf
16151659#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1660+ , switchSubscribedNodeId = nid
16171661#endif
16181662 }
16191663 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1658,6 +1702,9 @@ getCoincidenceSubscribed c sub = do
16581702 scheduleClear innerSubdRef
16591703 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16601704 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1705+#ifdef DEBUG_NODEIDS
1706+ nid <- liftIO newNodeId
1707+#endif
16611708 let subscribed = CoincidenceSubscribed
16621709 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16631710 , coincidenceSubscribedOccurrence = occRef
@@ -1668,7 +1715,7 @@ getCoincidenceSubscribed c sub = do
16681715 , coincidenceSubscribedInnerParent = innerSubdRef
16691716 , coincidenceSubscribedWeakSelf = weakSelf
16701717#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1718+ , coincidenceSubscribedNodeId = nid
16721719#endif
16731720 }
16741721 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1685,26 +1732,34 @@ cleanupCoincidenceSubscribed subscribed = do
16851732subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16861733subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16871734
1688−{-# INLINE merge #-}
1689−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1690−merge d = cacheEvent (mergeCheap d)
1735+{-# INLINE mergeG #-}
1736+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1737+ => (forall a. q a -> Event x (v a))
1738+ -> DynamicS x (PatchDMap k q) -> Event x (DMap k v)
1739+mergeG nt d = cacheEvent (mergeCheap nt d)
16911740
16921741{-# INLINE mergeWithMove #-}
1693−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1694−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1742+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1743+ => (forall a. q a -> Event x (v a))
1744+ -> DynamicS x (PatchDMapWithMove k q) -> Event x (DMap k v)
1745+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
16951746
16961747{-# INLINE [1] mergeCheap #-}
1697−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1698−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1748+mergeCheap
1749+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1750+ => (forall a. q a -> Event x (v a))
1751+ -> DynamicS x (PatchDMap k q)
1752+ -> Event x (DMap k v)
1753+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
16991754 where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1755+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17011756 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17021757 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17031758 (mOldSubd, newPs) <- case me of
17041759 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17051760 Just e -> do
17061761 let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1762+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17081763 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17091764 let newParent = MergeSubscribedParent subscription
17101765 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1714,28 +1769,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17141769 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17151770 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17161771 foldM f ([], oldParents) $ DMap.toList p
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1772+
1773+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17181774 getInitialSubscribers initialParents subscriber = do
17191775 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17201776 let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1777+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17221778 height <- liftIO $ getEventSubscribedHeight parentSubd
1723− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1779+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
17241780 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17251781 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17261782 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17271783 )
1784+
17281785 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17291786 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17301787
17311788{-# INLINE [1] mergeCheapWithMove #-}
1732−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1733−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1789+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1790+ => (forall a. q a -> Event x (v a))
1791+ -> DynamicS x (PatchDMapWithMove k q)
1792+ -> Event x (DMap k v)
1793+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
17341794 where
1735− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1795+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17361796 updateMe subscriber heightBagRef oldParents p = do
17371797 -- Prepare new parents for insertion
1738− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1798+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
17391799 subscribeParent k e = do
17401800 keyRef <- liftIO $ newIORef k
17411801 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1744,9 +1804,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17441804 newParentHeight <- getEventSubscribedHeight subd
17451805 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17461806 return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1807+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17481808 -- Collect old parents for deletion and update the keys of moved parents
1749− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1809+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
17501810 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17511811 Nothing -> do
17521812 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1757,46 +1817,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17571817 return Nothing
17581818 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17591819 return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1820+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17611821 getInitialSubscribers initialParents subscriber = do
17621822 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17631823 keyRef <- liftIO $ newIORef k
17641824 let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1825+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17661826 height <- liftIO $ getEventSubscribedHeight parentSubd
1767− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1827+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17681828 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17691829 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17701830 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17711831 )
1832+
17721833 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17731834 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17741835
1775−type MergeUpdateFunc k x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1836+type MergeUpdateFunc k v x p s
1837+ = (forall a. EventM x (k a) -> Subscriber x (v a))
17771838 -> IORef HeightBag
17781839 -> DMap k s
17791840 -> p
17801841 -> EventM x ([EventSubscription x], DMap k s)
17811842
1782−type MergeInitFunc k x s
1783− = DMap k (Event x)
1784− -> (forall a. EventM x (k a) -> Subscriber x a)
1785− -> EventM x (DMap k Identity, [Height], DMap k s)
1843+type MergeInitFunc k v q x s
1844+ = DMap k q
1845+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1846+ -> EventM x (DMap k v, [Height], DMap k s)
17861847
17871848type MergeDestroyFunc k s
17881849 = DMap k s
17891850 -> IO ()
17901851
1791−data Merge x k s = Merge
1852+data Merge x k v s = Merge
17921853 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17931854 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17941855 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1856+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1857+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
17971858 }
17981859
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1860+invalidateMergeHeight :: Merge x k v s -> IO ()
18001861invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18011862
18021863invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1806,8 +1867,7 @@ invalidateMergeHeight' heightRef sub = do
18061867 writeIORef heightRef $! invalidHeight
18071868 subscriberInvalidateHeight sub oldHeight
18081869
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1870+revalidateMergeHeight :: Merge x k v s -> IO ()
18111871revalidateMergeHeight m = do
18121872 currentHeight <- readIORef $ _merge_heightRef m
18131873 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1823,19 +1883,19 @@ revalidateMergeHeight m = do
18231883 subscriberRecalculateHeight (_merge_sub m) height
18241884 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18251885
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1886+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18271887scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18281888 vals <- liftIO $ readIORef $ _merge_accumRef m
18291889 liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
18301890 --TODO: Assert that m is not empty
18311891 subscriberPropagate (_merge_sub m) vals
18321892
1833−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1893+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
18341894mergeSubscriber m getKey = Subscriber
18351895 { subscriberPropagate = \a -> do
18361896 oldM <- liftIO $ readIORef $ _merge_accumRef m
18371897 k <- getKey
1838− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1898+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
18391899 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18401900 liftIO $ writeIORef (_merge_accumRef m) $! newM
18411901 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1850,7 +1910,7 @@ mergeSubscriber m getKey = Subscriber
18501910 else liftIO $ do
18511911#ifdef DEBUG_CYCLES
18521912 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1913+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
18541914 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
18551915#else
18561916 let cycleInfo = ""
@@ -1866,7 +1926,7 @@ mergeSubscriber m getKey = Subscriber
18661926 }
18671927
18681928--TODO: Be able to run as much of this as possible promptly
1869−updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1929+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
18701930updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18711931 where updateMe = do
18721932 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1874,9 +1934,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18741934 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18751935 return subscriptionsToKill
18761936
1877−{-# INLINE mergeCheap' #-}
1878−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k Identity)
1879−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1937+{-# INLINE mergeGCheap' #-}
1938+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
1939+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1940+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
18801941 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18811942 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18821943 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1924,11 +1985,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
19241985 , occ
19251986 )
19261987
1927−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1988+mergeInt :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19281989mergeInt = cacheEvent . mergeIntCheap
19291990
19301991{-# INLINABLE mergeIntCheap #-}
1931−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1992+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19321993mergeIntCheap d = Event $ \sub -> do
19331994 initialParents <- readBehaviorUntracked $ dynamicCurrent d
19341995 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2025,14 +2086,16 @@ mergeIntCheap d = Event $ \sub -> do
20252086 )
20262087
20272088newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2089+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
20282090
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2030−fan e =
2091+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2092+fanG e = unsafePerformIO $ do
2093+ ref <- newIORef Nothing
20312094 let f = Fan
20322095 { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2096+ , fanSubscribed = ref
20342097 }
2035− in EventSelector $ \k -> eventFan k f
2098+ pure $ EventSelectorG $ \k -> eventFan k f
20362099
20372100runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20382101runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2086,18 +2149,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
20862149-- | Run an event action outside of a frame
20872150runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
20882151runFrame a = SpiderHost $ do
2089− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2152+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
20902153 let go = do
20912154 result <- a
20922155 runHoldInits (eventEnvHoldInits env) (eventEnvDynInits env) (eventEnvMergeInits env) -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
20932156 return result
20942157 result <- runEventM go
20952158 toClear <- readIORef $ eventEnvClears env
2096− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2159+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
20972160 toClearInt <- readIORef $ eventEnvIntClears env
2098− forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2161+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
20992162 toClearRoot <- readIORef $ eventEnvRootClears env
2100− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2163+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
21012164 toAssign <- readIORef $ eventEnvAssignments env
21022165 toReconnectRef <- newIORef []
21032166 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2144,7 +2207,7 @@ runFrame a = SpiderHost $ do
21442207 myHeight <- readIORef $ switchSubscribedHeight subscribed
21452208 when (parentHeight /= myHeight) $ do
21462209 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2147− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2210+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
21482211 mapM_ _someMergeUpdate_invalidateHeight mergeUpdates --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
21492212 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
21502213 unsubscribe subscription
@@ -2185,7 +2248,7 @@ invalidateCoincidenceHeight subscribed = do
21852248 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
21862249 when (oldHeight /= invalidHeight) $ do
21872250 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2188− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2251+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
21892252
21902253updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
21912254updateSwitchHeight new subscribed = do
@@ -2193,7 +2256,7 @@ updateSwitchHeight new subscribed = do
21932256 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21942257 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21952258 writeIORef (switchSubscribedHeight subscribed) $! new
2196− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2259+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
21972260
21982261recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
21992262recalculateCoincidenceHeight subscribed = do
@@ -2202,7 +2265,7 @@ recalculateCoincidenceHeight subscribed = do
22022265 height <- calculateCoincidenceHeight subscribed
22032266 when (height /= invalidHeight) $ do
22042267 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2205− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2268+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
22062269
22072270calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
22082271calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2297,7 +2360,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
22972360 fail _ = error "Dynamic does not support 'fail'"
22982361
22992362{-# INLINABLE newJoinDyn #-}
2300−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
2363+newJoinDyn :: HasSpiderTimeline x => DynamicS x (Identity (DynamicS x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
23012364newJoinDyn d =
23022365 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
23032366 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2307,9 +2370,13 @@ newJoinDyn d =
23072370 in Reflex.Spider.Internal.unsafeBuildDynamic readV0 v'
23082371
23092372instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
2310− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
2373+ fmap = mapDynamicSpider
23112374 x <$ d = R.unsafeBuildDynamic (return x) $ x <$ R.updated d
23122375
2376+mapDynamicSpider :: HasSpiderTimeline x => (a -> b) -> Reflex.Class.Dynamic (SpiderTimeline x) a -> Reflex.Class.Dynamic (SpiderTimeline x) b
2377+mapDynamicSpider f = SpiderDynamic . newMapDyn f . unSpiderDynamic
2378+{-# INLINE [1] mapDynamicSpider #-}
2379+
23132380instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
23142381 pure = SpiderDynamic . dynamicConst
23152382#if MIN_VERSION_base(4,10,0)
@@ -2443,7 +2510,7 @@ unsafeNewSpiderTimelineEnv = do
24432510#ifdef DEBUG
24442511 depthRef <- newIORef 0
24452512#endif
2446− return $ SpiderTimelineEnv
2513+ return $ STE $ SpiderTimelineEnv
24472514 { _spiderTimeline_lock = lock
24482515 , _spiderTimeline_eventEnv = env
24492516#ifdef DEBUG
@@ -2453,19 +2520,19 @@ unsafeNewSpiderTimelineEnv = do
24532520
24542521-- | Create a new SpiderTimelineEnv
24552522newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2456−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2523+newSpiderTimeline = withSpiderTimeline (pure . Some)
24572524
2458−data LocalSpiderTimeline x s
2525+data LocalSpiderTimeline (x :: Type) s
24592526
24602527instance Reifies s (SpiderTimelineEnv x) =>
24612528 HasSpiderTimeline (LocalSpiderTimeline x s) where
2462− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2529+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
24632530
24642531localSpiderTimeline
2465− :: Proxy s
2532+ :: proxy s
24662533 -> SpiderTimelineEnv x
24672534 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2468−localSpiderTimeline _ = unsafeCoerce
2535+localSpiderTimeline _ = coerce
24692536
24702537-- | Pass a new timeline to the given function.
24712538withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2473,18 +2540,18 @@ withSpiderTimeline k = do
24732540 env <- unsafeNewSpiderTimelineEnv
24742541 reify env $ \s -> k $ localSpiderTimeline s env
24752542
2476−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2543+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24772544
24782545type ComputeM = EventM
24792546
2480−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2547+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24812548
24822549instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24832550 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
24842551 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
24852552 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2486− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2487− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2553+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2554+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
24882555 type PullM (SpiderTimeline x) = SpiderPullM x
24892556 type PushM (SpiderTimeline x) = SpiderPushM x
24902557 {-# INLINABLE never #-}
@@ -2497,10 +2564,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24972564 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
24982565 {-# INLINABLE pull #-}
24992566 pull = SpiderBehavior . pull . coerce
2500− {-# INLINABLE merge #-}
2501− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2502− {-# INLINABLE fan #-}
2503− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2567+ {-# INLINABLE fanG #-}
2568+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2569+ {-# INLINABLE mergeG #-}
2570+ mergeG
2571+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2572+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2573+ -> DMap k q
2574+ -> R.Event (SpiderTimeline x) (DMap k v)
2575+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
25042576 {-# INLINABLE switch #-}
25052577 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25062578 {-# INLINABLE coincidence #-}
@@ -2508,15 +2580,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25082580 {-# INLINABLE current #-}
25092581 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
25102582 {-# INLINABLE updated #-}
2511− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2583+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
25122584 {-# INLINABLE unsafeBuildDynamic #-}
25132585 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25142586 {-# INLINABLE unsafeBuildIncremental #-}
25152587 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2516− {-# INLINABLE mergeIncremental #-}
2517− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2518− {-# INLINABLE mergeIncrementalWithMove #-}
2519− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2588+ {-# INLINABLE mergeIncrementalG #-}
2589+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2590+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2591+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
25202592 {-# INLINABLE currentIncremental #-}
25212593 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25222594 {-# INLINABLE updatedIncremental #-}
@@ -2527,9 +2599,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25272599 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
25282600 eventCoercion Coercion = Coercion
25292601 behaviorCoercion Coercion = Coercion
2530− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2602+ dynamicCoercion Coercion = Coercion
2603+ incrementalCoercion Coercion Coercion = Coercion
25312604 {-# INLINABLE mergeIntIncremental #-}
2532− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2605+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
25332606 {-# INLINABLE fanInt #-}
25342607 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
25352608
@@ -2554,7 +2627,7 @@ instance MonadAtomicRef (EventM x) where
25542627 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25552628
25562629-- | The monad for actions that manipulate a Spider timeline identified by @x@
2557−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2630+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25582631
25592632instance Monad (SpiderHost x) where
25602633 {-# INLINABLE (>>=) #-}
@@ -2576,7 +2649,7 @@ runSpiderHost (SpiderHost a) = a
25762649runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25772650runSpiderHostForTimeline (SpiderHost a) _ = a
25782651
2579−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2652+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25802653 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25812654
25822655instance Monad (SpiderHostFrame x) where
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/TriggerEvent/Class.hs−1
@@ -2,7 +2,6 @@
22-- new 'Event's that can be triggered from 'IO'.
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76module Reflex.TriggerEvent.Class
87 ( TriggerEvent (..)
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 return 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/EventWriterT.hs+76−7
@@ -1,4 +1,7 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
13{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE GADTs #-}
25{-# LANGUAGE RecursiveDo #-}
36{-# LANGUAGE ScopedTypeVariables #-}
47module Main where
@@ -6,8 +9,15 @@ module Main where
69import Control.Lens
710import Control.Monad
811import Control.Monad.Fix
12+import qualified Data.Dependent.Map as DMap
13+import Data.Functor.Misc
14+import qualified Data.Map as M
915import Data.These
1016
17+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
18+import Data.These.Lens
19+#endif
20+
1121import Reflex
1222import Reflex.EventWriter.Base
1323import Test.Run
@@ -28,6 +38,13 @@ main = do
2838 print os2
2939 os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
3040 print os3
41+ os4@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
42+ print os4
43+ os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
44+ print os5
45+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
46+ print os6
47+ let ![[Nothing, Nothing]] = os6
3148 return ()
3249
3350unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
@@ -36,16 +53,13 @@ unwrapApp x appIn = do
3653 return e
3754
3855testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
39−testOrdering pulse = do
40− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
41− return ()
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
4257
4358testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
4459testSimultaneous pulse = do
4560 let e0 = fmapMaybe (^? here) pulse
4661 e1 = fmapMaybe (^? there) pulse
4762 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
48− return ()
4963
5064-- | Test that a widget telling and event which fires at the same time it has been replaced
5165-- doesn't count along with the new widget.
@@ -59,7 +73,62 @@ testMoribundTellEvent
5973 => Event t ()
6074 -> EventWriterT t [Int] m ()
6175testMoribundTellEvent pulse = do
62− rec let tellIntOnReplce :: Int -> EventWriterT t [Int] m ()
63− tellIntOnReplce x = tellEvent $ [x] <$ rwrFinished
64− (_, rwrFinished) <- runWithReplace (tellIntOnReplce 1) $ tellIntOnReplce 2 <$ pulse
76+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
77+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
78+ (_, rwrFinished) <- runWithReplace (tellIntOnReplace 1) $ tellIntOnReplace 2 <$ pulse
79+ return ()
80+
81+-- | The equivalent of 'testMoribundTellEvent' for 'traverseDMapWithKeyWithAdjust'.
82+testMoribundTellEventDMap
83+ :: forall t m
84+ . ( Reflex t
85+ , Adjustable t m
86+ , MonadHold t m
87+ , MonadFix m
88+ )
89+ => Event t ()
90+ -> EventWriterT t [Int] m ()
91+testMoribundTellEventDMap pulse = do
92+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
93+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
94+ (_, rwrFinished :: Event t (PatchDMap (Const2 () Int) Identity)) <-
95+ traverseDMapWithKeyWithAdjust
96+ (\(Const2 ()) (Identity v) -> Identity . const v <$> tellIntOnReplace v)
97+ (mapToDMap $ M.singleton () 1)
98+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton () 2) <$ pulse)
99+ return ()
100+
101+-- | Ensures that elements which are _not_ removed can still fire 'tellEvent's
102+-- during the same frame as other elements are updated.
103+testLiveTellEventDMap
104+ :: forall t m
105+ . ( Reflex t
106+ , Adjustable t m
107+ , MonadHold t m
108+ , MonadFix m
109+ )
110+ => Event t ()
111+ -> EventWriterT t [Int] m ()
112+testLiveTellEventDMap pulse = do
113+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
114+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
115+ (_, rwrFinished :: Event t (PatchDMap (Const2 Int ()) Identity)) <-
116+ traverseDMapWithKeyWithAdjust
117+ (\(Const2 k) (Identity ()) -> Identity <$> tellIntOnReplace k)
118+ (mapToDMap $ M.singleton 1 ())
119+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
65120 return ()
121+
122+delayedPulse
123+ :: forall t m
124+ . ( Reflex t
125+ , Adjustable t m
126+ , MonadHold t m
127+ , MonadFix m
128+ )
129+ => Event t ()
130+ -> EventWriterT t [Int] m ()
131+delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do
132+ -- This has the effect of delaying pulse' from pulse
133+ (_, pulse') <- runWithReplace (pure ()) $ pure [1] <$ pulse
134+ tellEvent pulse'
test/GC.hs+4−3
@@ -27,6 +27,7 @@ import qualified Reflex.Spider.Internal as S
2727
2828import System.Exit
2929import System.Mem
30+import Data.Coerce
3031
3132main :: IO ()
3233main = do
@@ -46,7 +47,7 @@ hostPerf ref = S.runSpiderHost $ do
4647 eventToPerform <- Host.runHostFrame $ do
4748 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
4849 <- S.SpiderHostFrame
49− $ fmap ( S.merge
50+ $ fmap ( S.mergeG coerce
5051 . S.dynamicHold)
5152 $ S.hold DMap.empty
5253 -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
@@ -55,8 +56,8 @@ hostPerf ref = S.runSpiderHost $ do
5556 { S.subscriberPropagate = S.subscriberPropagate sub
5657 }
5758 return (s, o))
58− $ runIdentity <$> S.select
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
59+ $ runIdentity . runIdentity <$> S.selectG
60+ (S.fanG $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
6061 (WrapArg Request)
6162 return $ alignWith (mergeThese (<>))
6263 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
test/QueryT.hs+10−1
@@ -1,15 +1,16 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveFunctor #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE GeneralizedNewtypeDeriving #-}
45{-# LANGUAGE LambdaCase #-}
56{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE StandaloneDeriving #-}
68{-# LANGUAGE TypeFamilies #-}
79{-# LANGUAGE UndecidableInstances #-}
810
911import Control.Lens
1012import Control.Monad.Fix
1113import Data.Align
12−import Data.AppendMap () -- for the Align instance
1314import qualified Data.AppendMap as AMap
1415import Data.Functor.Misc
1516import Data.Map (Map)
@@ -18,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
1819import Data.Semigroup
1920import Data.These
2021
22+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
23+import Data.These.Lens
24+#endif
25+
2126import Reflex
2227import Reflex.Patch.MapWithMove
2328import Test.Run
@@ -36,6 +41,10 @@ instance (Ord k, Query a, Eq (QueryResult a), Align (MonoidalMap k)) => Query (S
3641newtype Selector k a = Selector { unSelector :: MonoidalMap k a }
3742 deriving (Show, Read, Eq, Ord, Functor)
3843
44+#if !(MIN_VERSION_monoidal_containers(0,4,1))
45+deriving instance Ord k => Align (MonoidalMap k)
46+#endif
47+
3948instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Semigroup (Selector k a) where
4049 (Selector a) <> (Selector b) = Selector $ fmapMaybe id $ f a b
4150 where
test/Reflex/Bench/Focused.hs+12
@@ -130,6 +130,16 @@ mapDynChain = iterM (return . fmap (+1))
130130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132132
133+holdDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134+holdDynChain = iterM (\d -> sample (current d) >>= flip holdDyn (updated d))
135+
136+buildDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
137+buildDynChain = iterM (\d -> do
138+ let b = fmap (+1) (current d)
139+ e = fmap (*2) (updated d)
140+ buildDynamic (sample b) e)
141+
142+
133143combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134144combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135145
@@ -308,6 +318,8 @@ dynamics :: Word -> [(String, TestCase)]
308318dynamics n =
309319 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310320 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
321+ , testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d
322+ , testE "buildDynChain" $ fmap updated $ buildDynChain n =<< d
311323 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312324 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313325 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
test/Reflex/Plan/Pure.hs+4−4
@@ -55,15 +55,15 @@ instance TestPlan (Pure Int) PurePlan where
5555runPure :: PurePlan a -> (a, IntSet)
5656runPure (PurePlan p) = runStateT p mempty $ 0
5757
58−relavantTimes :: IntSet -> IntSet
59−relavantTimes occs = IntSet.fromList [0..l + 1]
58+relevantTimes :: IntSet -> IntSet
59+relevantTimes occs = IntSet.fromList [0..l + 1]
6060 where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
6161
6262testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
63−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
63+testBehavior (b, occs) = IntMap.fromSet (sample b) (relevantTimes occs)
6464
6565testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
66−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
66+testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relevantTimes occs)
6767
6868
6969
test/Reflex/Test/CrossImpl.hs+5
@@ -218,6 +218,11 @@ testCases =
218218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219219 let b' = pull $ sample =<< sample bb
220220 return (b', e)
221+ , (,) "foldDynWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
222+ d <- foldDyn (:) [] $
223+ pushAlways (\a -> foldDyn (:) [a] e) e
224+ let b = current (join (fmap distributeListOverDynPure d))
225+ return (b, e)
221226 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222227 bb <- hold "b" e
223228 bd <- hold never . fmap (const e) =<< headE e
test/Reflex/TestPlan.hs+2−2
@@ -26,8 +26,8 @@ import Reflex.Class
2626import Prelude
2727
2828class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
29− -- | Speicify a plan of an input Event firing
30− -- Occurances must be in the future (i.e. Time > 0)
29+ -- | Specify a plan of an input Event firing
30+ -- Occurrences must be in the future (i.e. Time > 0)
3131 -- Initial specification is
3232
3333 plan :: [(Word, a)] -> m (Event t a)
test/RequesterT.hs+116−9
@@ -1,15 +1,25 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
13{-# LANGUAGE FlexibleContexts #-}
24{-# LANGUAGE GADTs #-}
35{-# LANGUAGE RankNTypes #-}
6+{-# LANGUAGE RecursiveDo #-}
47{-# LANGUAGE ScopedTypeVariables #-}
58module Main where
69
710import Control.Lens
811import Control.Monad
12+import Control.Monad.Fix
913import qualified Data.Dependent.Map as DMap
1014import Data.Dependent.Sum
15+import Data.Functor.Misc
16+import qualified Data.Map as M
1117import Data.These
1218
19+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
20+import Data.These.Lens
21+#endif
22+
1323import Reflex
1424import Reflex.Requester.Base
1525import Reflex.Requester.Class
@@ -20,18 +30,31 @@ data RequestInt a where
2030
2131main :: IO ()
2232main = do
23− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
33+ os1 <- runApp' (unwrapApp testOrdering) $
2434 [ Just ()
2535 ]
2636 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− ]
37+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
38+ [ This ()
39+ , That ()
40+ , This ()
41+ , These () ()
42+ ]
3443 print os2
44+ os3 <- runApp' (unwrapApp testMoribundRequest) [Just ()]
45+ print os3
46+ os4 <- runApp' (unwrapApp testMoribundRequestDMap) [Just ()]
47+ print os4
48+ os5 <- runApp' (unwrapApp testLiveRequestDMap) [Just ()]
49+ print os5
50+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
51+ print os6
52+ let ![[Just [1,2,3,4,5,6,7,8,9,10]]] = os1 -- The order is reversed here: see the documentation for 'runRequesterT'
53+ let ![[Just [9,7,5,3,1]],[Nothing,Nothing],[Just [10,8,6,4,2]],[Just [10,8,6,4,2],Nothing]] = os2
54+ let ![[Nothing, Just [2]]] = os3
55+ let ![[Nothing, Just [2]]] = os4
56+ let ![[Nothing, Just [1, 2]]] = os5
57+ -- let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
3558 return ()
3659
3760unwrapRequest :: DSum tag RequestInt -> Int
@@ -43,7 +66,7 @@ unwrapApp :: ( Reflex t, Monad m )
4366 -> m (Event t [Int])
4467unwrapApp x appIn = do
4568 ((), e) <- runRequesterT (x appIn) never
46− return $ fmap (map unwrapRequest . DMap.toList) e
69+ return $ fmap (map unwrapRequest . requesterDataToList) e
4770
4871testOrdering :: ( Response m ~ Identity
4972 , Request m ~ RequestInt
@@ -65,3 +88,87 @@ testSimultaneous pulse = do
6588 switchE = fmapMaybe (^? there) pulse
6689 forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
6790 requestingIdentity (RequestInt (i+1) <$ tellE)
91+
92+-- | Test that a widget requesting and event which fires at the same time it has been replaced
93+-- doesn't count along with the new widget.
94+testMoribundRequest
95+ :: forall t m
96+ . ( Reflex t
97+ , Adjustable t m
98+ , MonadHold t m
99+ , MonadFix m
100+ , Response m ~ Identity
101+ , Request m ~ RequestInt
102+ , Requester t m
103+ )
104+ => Event t ()
105+ -> m ()
106+testMoribundRequest pulse = do
107+ rec let requestIntOnReplace x = requestingIdentity $ RequestInt x <$ rwrFinished
108+ (_, rwrFinished) <- runWithReplace (requestIntOnReplace 1) $ requestIntOnReplace 2 <$ pulse
109+ return ()
110+
111+-- | The equivalent of 'testMoribundRequest' for 'traverseDMapWithKeyWithAdjust'.
112+testMoribundRequestDMap
113+ :: forall t m
114+ . ( Reflex t
115+ , Adjustable t m
116+ , MonadHold t m
117+ , MonadFix m
118+ , Response m ~ Identity
119+ , Request m ~ RequestInt
120+ , Requester t m
121+ )
122+ => Event t ()
123+ -> m ()
124+testMoribundRequestDMap pulse = do
125+ rec let requestIntOnReplace :: Int -> m ()
126+ requestIntOnReplace x = void $ requestingIdentity $ RequestInt x <$ rwrFinished
127+ (_, rwrFinished :: Event t (PatchDMap (Const2 () Int) Identity)) <-
128+ traverseDMapWithKeyWithAdjust
129+ (\(Const2 ()) (Identity v) -> Identity . const v <$> requestIntOnReplace v)
130+ (mapToDMap $ M.singleton () 1)
131+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton () 2) <$ pulse)
132+ return ()
133+
134+-- | Ensures that elements which are _not_ removed can still fire requests
135+-- during the same frame as other elements are updated.
136+testLiveRequestDMap
137+ :: forall t m
138+ . ( Reflex t
139+ , Adjustable t m
140+ , MonadHold t m
141+ , MonadFix m
142+ , Response m ~ Identity
143+ , Request m ~ RequestInt
144+ , Requester t m
145+ )
146+ => Event t ()
147+ -> m ()
148+testLiveRequestDMap pulse = do
149+ rec let requestIntOnReplace :: Int -> m ()
150+ requestIntOnReplace x = void $ requestingIdentity $ RequestInt x <$ rwrFinished
151+ (_, rwrFinished :: Event t (PatchDMap (Const2 Int ()) Identity)) <-
152+ traverseDMapWithKeyWithAdjust
153+ (\(Const2 k) (Identity ()) -> Identity <$> requestIntOnReplace k)
154+ (mapToDMap $ M.singleton 1 ())
155+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
156+ return ()
157+
158+delayedPulse
159+ :: forall t m
160+ . ( Reflex t
161+ , Adjustable t m
162+ , MonadHold t m
163+ , MonadFix m
164+ , Response m ~ Identity
165+ , Request m ~ RequestInt
166+ , PerformEvent t m
167+ , Requester t m
168+ )
169+ => Event t ()
170+ -> m ()
171+delayedPulse pulse = void $ flip runWithReplace (pure () <$ pulse) $ do
172+ -- This has the effect of delaying pulse' from pulse
173+ (_, pulse') <- runWithReplace (pure ()) $ pure (RequestInt 1) <$ pulse
174+ requestingIdentity pulse'
test/hlint.hs+1
@@ -22,6 +22,7 @@ main = do
2222 , "--ignore=Use unless"
2323 , "--ignore=Reduce duplication"
2424 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
2526 ]
2627 recurseInto = and <$> sequence
2728 [ fileType ==? Directory