on develop help sign in
Merge branch 'develop' into dn-add-test-folddyn
commit7ae1e2337b022a2134b5bbcd25f257f01ab6c2e6 authorOliver Batchelor <saulzar@gmail.com> dateFri 16 Aug 2019, 12:04 (7 years ago) parent63e88f15fca658
60 files changed+1879−864
.travis.yml+171 CONTRIBUTING.md+66 ChangeLog.md+49 Quickref.md+7−7 README.md+13−7 cabal.haskell-ci+16 cabal.project+1 cabal.project.freeze+1 default.nix+11−7 reflex.cabal+69−33 release.nix+4−4 src/Control/Monad/ReaderIO.hs+60 src/Data/AppendMap.hs+20−19 src/Data/FastMutableIntMap.hs+17 src/Data/FastWeakBag.hs+1−1 src/Data/Functor/Misc.hs+3−56 src/Data/Map/Misc.hs+2−1 src/Reflex.hs+1 src/Reflex/Adjustable/Class.hs+30−4 src/Reflex/BehaviorWriter/Class.hs−1 src/Reflex/Class.hs+425−117 src/Reflex/Collection.hs+12−10 src/Reflex/Dynamic.hs+18−168 src/Reflex/Dynamic/TH.hs+2−26 src/Reflex/DynamicWriter/Base.hs+8−3 src/Reflex/DynamicWriter/Class.hs+10−5 src/Reflex/EventWriter/Base.hs+3−3 src/Reflex/EventWriter/Class.hs−1 src/Reflex/FastWeak.hs+10−6 src/Reflex/FunctorMaybe.hs+25−21 src/Reflex/Host/Class.hs−1 src/Reflex/Network.hs+10−4 src/Reflex/NotReady/Class.hs+10−1 src/Reflex/Optimizer.hs+8−4 src/Reflex/Patch.hs+12−5 src/Reflex/Patch/Class.hs+1−1 src/Reflex/Patch/DMapWithMove.hs+6−7 src/Reflex/Patch/IntMap.hs−8 src/Reflex/Patch/Map.hs−8 src/Reflex/Patch/MapWithMove.hs+1−1 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+28−22 src/Reflex/Query/Base.hs+28−37 src/Reflex/Query/Class.hs+25−6 src/Reflex/Requester/Base.hs+108−5 src/Reflex/Requester/Class.hs−1 src/Reflex/Spider.hs+5−2 src/Reflex/Spider/Internal.hs+264−201 src/Reflex/Time.hs+113−13 src/Reflex/TriggerEvent/Class.hs−1 src/Reflex/Workflow.hs+13−1 test/EventWriterT.hs+24−4 test/GC.hs+4−3 test/QueryT.hs+4 test/Reflex/Bench/Focused.hs+12 test/RequesterT.hs+116−9 test/hlint.hs+1
.travis.ymlnew file+171
@@ -0,0 +1,171 @@
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
15+cache:
16+ directories:
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
28+matrix:
29+ include:
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
131+script:
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
169+
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+49
@@ -0,0 +1,49 @@
1+# Revision history for reflex
2+
3+## 0.6.2.2
4+
5+* Support these >= 1. Add `split-these` flag to control whether to use new these/semialign combination or not.
6+* Update version bounds to fix some CI failures
7+* Add travis CI configuration
8+
9+## 0.6.2.1
10+
11+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
12+ functor:
13+ * `fan` to `fanG`
14+ * `EventSelectorG` for `fanG` result selector.
15+
16+* Reduce the amount of unsafeCoerce in coercing newtypes under Event/Dynamic/Behavior.
17+ * Add fused ReaderIO for the purpose of coercion (ReaderT's third argument has nominal role preventing automated coerce)
18+ * Add incrementalCoercion/coerceIncremental to go with dynamicCoercion/coerceDynamic
19+
20+* Generalize merging functions:
21+ `merge` to `mergeG`,
22+ `mergeIncremental` to `mergeIncrementalG`,
23+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
24+
25+* Generalize distribute function:
26+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
27+
28+## 0.6.2.0
29+
30+* Fix `holdDyn` so that it is lazy in its event argument
31+ These produce `DMap`s whose values needn't be `Identity`.
32+* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
33+* Fix `holdDyn` so that it is lazy in its event argument.
34+
35+## 0.6.1.0
36+
37+* Re-export all of `Data.Map.Monoidal`
38+* Fix `QueryT` and `RequesterT` tests
39+
40+## 0.6.0.0 -- 2019-03-20
41+
42+* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
43+* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
44+* Remove many deprecated functions.
45+* Add a `Num` instance for `Dynamic`.
46+* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
47+* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
48+* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
49+* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
Quickref.md+7−7
@@ -57,15 +57,15 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
5757[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
5858[ ] leftmost :: [Event a] -> Event a
5959[ ] mergeList :: [Event a] -> Event (NonEmpty a)
60−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
60+[ ] merge :: GCompare k => DMap k Event -> Event (DMap k Identity)
6161[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
6262
6363-- Efficient one-to-many fanout
64−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
66−[ ] select :: EventSelector k -> k a -> Event a
67−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
64+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65+[ ] fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
66+[ ] select :: EventSelector k -> k a -> Event a
67+[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68+[ ] fanThese :: Event (These a b) -> (Event a, Event b)
6969
7070-- Event to Event via function that can sample current values
7171[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
@@ -166,7 +166,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
166166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
167167[ ] switch :: Behavior (Event a) -> Event a
168168
169−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
169+-- Flatten Dynamic-of-Event to Event. New Event is used immediately.
170170[ ] switchDyn :: Dynamic (Event a) -> Event a
171171
172172-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
README.md+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+11−7
@@ -6,12 +6,13 @@
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+, semialign ? null, splitThese ? (semialign != null)
1011, useTemplateHaskell ? true
1112}:
1213mkDerivation {
1314 pname = "reflex";
14− version = "0.5";
15+ version = "0.6.2.2";
1516 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
1617 libraryHaskellDepends = [
1718 base bifunctors containers dependent-map dependent-sum
@@ -21,18 +22,21 @@ mkDerivation {
2122 transformers-compat prim-uniq
2223 base bifunctors containers deepseq dependent-map dependent-sum
2324 mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
25+ random time unbounded-delays monoidal-containers witherable
26+ profunctors
2527 ] ++ (if ghc.isGhcjs or false then [
2628 ghcjs-base
2729 ] else []) ++ (if !useTemplateHaskell then [] else [
2830 haskell-src-exts haskell-src-meta
29− ]);
31+ ]) ++ (if splitThese then [
32+ semialign
33+ ] else []);
3034 testHaskellDepends = if ghc.isGhcjs or false then [] else [
3135 hlint filepath directory filemanip
3236 ];
33− configureFlags = if useTemplateHaskell then [] else [
34− "-f-use-template-haskell"
35− ];
37+ configureFlags =
38+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
39+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
3640 homepage = "https://github.com/reflex-frp/reflex";
3741 description = "Higher-order Functional Reactive Programming";
3842 license = stdenv.lib.licenses.bsd3;
reflex.cabal+69−33
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.5
2+Version: 0.6.2.2
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -15,6 +15,11 @@ bug-reports: https://github.com/reflex-frp/reflex/issues
1515extra-source-files:
1616 README.md
1717 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
1823
1924flag use-reflex-optimizer
2025 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -36,37 +41,51 @@ flag fast-weak
3641 default: False
3742 manual: True
3843
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
3949library
4050 hs-source-dirs: src
4151 build-depends:
4252 MemoTrie == 0.6.*,
4353 base >= 4.9 && < 4.13,
4454 bifunctors >= 5.2 && < 5.6,
45− comonad,
46− 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,
4758 data-default >= 0.5 && < 0.8,
48− dependent-map >= 0.2.4 && < 0.3,
59+ dependent-map >= 0.3 && < 0.4,
4960 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
5062 lens >= 4.7 && < 5,
5163 monad-control >= 1.0.1 && < 1.1,
52− monoidal-containers == 0.4.*,
5364 mtl >= 2.1 && < 2.3,
5465 prim-uniq >= 0.1.0.1 && < 0.2,
55− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
5668 random == 1.1.*,
5769 ref-tf == 0.4.*,
5870 reflection == 2.1.*,
5971 semigroupoids >= 4.0 && < 6,
60− semigroups >= 0.16 && < 0.19,
6172 stm >= 2.4 && < 2.6,
6273 syb >= 0.5 && < 0.8,
63− these >= 0.4 && < 0.8,
6474 time >= 1.4 && < 1.9,
65− transformers >= 0.2,
66− transformers-compat >= 0.3,
67− 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.2 && < 0.4
78+
79+ if flag(split-these)
80+ build-depends: these >= 1 && <1.1,
81+ semialign >=1 && <1.1,
82+ monoidal-containers >= 0.5.0.1 && < 0.6
83+ else
84+ build-depends: these >= 0.4 && <0.9,
85+ monoidal-containers == 0.5.0.0
6886
6987 exposed-modules:
88+ Control.Monad.ReaderIO
7089 Data.AppendMap,
7190 Data.FastMutableIntMap,
7291 Data.FastWeakBag,
@@ -131,8 +150,8 @@ library
131150 if flag(use-template-haskell)
132151 cpp-options: -DUSE_TEMPLATE_HASKELL
133152 build-depends:
134− dependent-sum >= 0.3 && < 0.5,
135− haskell-src-exts >= 1.16 && < 1.21,
153+ dependent-sum >= 0.6 && < 0.7,
154+ haskell-src-exts >= 1.16 && < 1.22,
136155 haskell-src-meta >= 0.6 && < 0.9,
137156 template-haskell >= 2.9 && < 2.15
138157 exposed-modules:
@@ -140,7 +159,7 @@ library
140159 other-extensions: TemplateHaskell
141160 else
142161 build-depends:
143− dependent-sum == 0.4.*
162+ dependent-sum >= 0.6 && < 0.7
144163
145164 if flag(fast-weak) && impl(ghcjs)
146165 cpp-options: -DGHCJS_FAST_WEAK
@@ -157,14 +176,14 @@ test-suite semantics
157176 base,
158177 bifunctors,
159178 containers,
160− deepseq >= 1.3 && < 1.5,
179+ deepseq,
161180 dependent-map,
162181 dependent-sum,
163182 mtl,
164183 ref-tf,
165184 reflex,
166185 split,
167− transformers >= 0.3
186+ transformers
168187 other-modules:
169188 Reflex.Bench.Focused
170189 Reflex.Plan.Pure
@@ -183,7 +202,7 @@ test-suite CrossImpl
183202 containers,
184203 dependent-map,
185204 dependent-sum,
186− deepseq >= 1.3 && < 1.5,
205+ deepseq,
187206 mtl,
188207 transformers,
189208 ref-tf,
@@ -202,7 +221,7 @@ test-suite hlint
202221 , directory
203222 , filepath
204223 , filemanip
205− , hlint
224+ , hlint < 2.1 || >= 2.2.2
206225 if impl(ghcjs)
207226 buildable: False
208227
@@ -212,7 +231,7 @@ test-suite EventWriterT
212231 hs-source-dirs: test
213232 build-depends: base
214233 , containers
215− , deepseq >= 1.3 && < 1.5
234+ , deepseq
216235 , dependent-map
217236 , dependent-sum
218237 , lens
@@ -221,6 +240,10 @@ test-suite EventWriterT
221240 , transformers
222241 , reflex
223242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
224247 other-modules:
225248 Reflex.Test
226249 Reflex.TestPlan
@@ -228,22 +251,30 @@ test-suite EventWriterT
228251 Reflex.Plan.Pure
229252 Test.Run
230253
254+
231255test-suite RequesterT
232256 type: exitcode-stdio-1.0
233257 main-is: RequesterT.hs
234258 hs-source-dirs: test
235259 build-depends: base
260+ , containers
261+ , deepseq
236262 , dependent-sum
237263 , dependent-map
238264 , lens
265+ , mtl
239266 , these
240267 , transformers
241268 , reflex
242269 , ref-tf
243− buildable: False
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
244274 other-modules:
245275 Reflex.TestPlan
246276 Reflex.Plan.Pure
277+ Test.Run
247278
248279test-suite QueryT
249280 type: exitcode-stdio-1.0
@@ -253,15 +284,18 @@ test-suite QueryT
253284 , containers
254285 , dependent-map
255286 , dependent-sum
256− , deepseq >= 1.3 && < 1.5
287+ , deepseq
257288 , lens
258289 , monoidal-containers
259290 , mtl
260291 , ref-tf
261292 , reflex
262− , semigroups
263293 , these
264294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
265299 other-modules:
266300 Test.Run
267301 Reflex.TestPlan
@@ -276,14 +310,16 @@ test-suite GC-Semantics
276310 , containers
277311 , dependent-sum
278312 , dependent-map
279− , deepseq >= 1.3 && < 1.5
313+ , deepseq
280314 , mtl
281315 , these
282316 , transformers
283317 , reflex
284318 , ref-tf
285− if impl(ghc < 8)
286− build-depends: semigroups
319+
320+ if flag(split-these)
321+ build-depends: semialign
322+
287323 other-modules:
288324 Reflex.Plan.Pure
289325 Reflex.Plan.Reflex
@@ -296,7 +332,7 @@ test-suite rootCleanup
296332 hs-source-dirs: test
297333 build-depends: base
298334 , containers
299− , deepseq >= 1.3 && < 1.5
335+ , deepseq
300336 , dependent-sum
301337 , mtl
302338 , reflex
@@ -315,8 +351,8 @@ benchmark spider-bench
315351 build-depends:
316352 base,
317353 containers,
318− criterion >= 1.1 && < 1.6,
319− deepseq >= 1.3 && < 1.5,
354+ criterion,
355+ deepseq,
320356 dependent-map,
321357 dependent-sum,
322358 ref-tf,
@@ -325,7 +361,7 @@ benchmark spider-bench
325361 reflex,
326362 split,
327363 stm,
328− transformers >= 0.3
364+ transformers
329365 other-modules:
330366 Reflex.TestPlan
331367 Reflex.Plan.Reflex
@@ -339,9 +375,9 @@ benchmark saulzar-bench
339375 ghc-options: -Wall -O2 -rtsopts -threaded
340376 build-depends:
341377 base,
342− containers >= 0.5 && < 0.7,
343− criterion >= 1.1 && < 1.6,
344− deepseq >= 1.3 && < 1.5,
378+ containers,
379+ criterion,
380+ deepseq,
345381 dependent-map,
346382 dependent-sum,
347383 loch-th,
@@ -353,7 +389,7 @@ benchmark saulzar-bench
353389 split,
354390 stm,
355391 time,
356− transformers >= 0.3
392+ transformers
357393 other-modules:
358394 Reflex.TestPlan
359395 Reflex.Plan.Reflex
release.nix+4−4
@@ -3,7 +3,7 @@
33let
44 inherit (rp.nixpkgs) lib;
55 compilers = ["ghc8_4" "ghc8_0" "ghcjs8_4" "ghcjs8_0"];
6−in lib.genAttrs compilers (ghc: {
7− reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; };
8− reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; };
9−})
6+in lib.attrValues (lib.genAttrs compilers (ghc: {
7+ reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; splitThese = false; };
8+ reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; splitThese = false; };
9+}))
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+20−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,26 @@ 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 (..))
3735
3836{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
37+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
3938type AppendMap = MonoidalMap
4039
4140{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
41+-- | A synonym for 'getMonoidalMap'
4242_unAppendMap :: MonoidalMap k v -> Map k v
4343_unAppendMap = getMonoidalMap
4444
45+-- | Pattern synonym for 'MonoidalMap'
4546pattern AppendMap :: Map k v -> MonoidalMap k v
4647pattern AppendMap m = MonoidalMap m
4748
48−instance FunctorMaybe (MonoidalMap k) where
49− fmapMaybe = mapMaybe
49+#if !MIN_VERSION_witherable(0,3,2)
50+instance W.Filterable (MonoidalMap k) where
51+ mapMaybe = mapMaybe
52+#endif
5053
5154-- | Deletes a key, returning 'Nothing' if the result is empty.
5255nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -56,24 +59,22 @@ nonEmptyDelete k vs =
5659 then Nothing
5760 else Just deleted
5861
62+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
5963mapMaybeNoNull :: (a -> Maybe b)
6064 -> MonoidalMap token a
6165 -> Maybe (MonoidalMap token b)
6266mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
67+ let bs = mapMaybe f as
6468 in if null bs
6569 then Nothing
6670 else Just bs
6771
6872-- 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−
73+-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
7474showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
7575showTree = coerce (Map.showTree :: Map k a -> String)
7676
77+-- | Displays a 'MonoidalMap' as a tree, using the supplied function to convert nodes to string.
7778showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
7879showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
7980
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+1−1
@@ -50,7 +50,7 @@ newtype FastWeakBag a = FastWeakBag JSVal
5050#else
5151data FastWeakBag a = FastWeakBag
5252 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
53+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
5454 }
5555#endif
5656
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+2−1
@@ -46,12 +46,13 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
4646-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
4747applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
4848applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
49− where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
49+ where (deletions, insertions) = Map.mapEither maybeToEither patch
5050 maybeToEither = \case
5151 Nothing -> Left ()
5252 Just r -> Right r
5353
5454-- |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" #-}
5556mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
5657mapPartitionEithers = Map.mapEither id
5758
src/Reflex.hs+1
@@ -19,6 +19,7 @@ import Reflex.Dynamic.TH as X
1919import Reflex.Dynamic.Uniq as X
2020import Reflex.DynamicWriter.Base as X
2121import Reflex.DynamicWriter.Class as X
22+import Reflex.NotReady.Class as X
2223import Reflex.PerformEvent.Base as X
2324import Reflex.PerformEvent.Class as X
2425import Reflex.PostBuild.Base as X
src/Reflex/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/Class.hs−1
@@ -5,7 +5,6 @@ Description: This module defines the 'MonadBehaviorWriter' class
55{-# LANGUAGE CPP #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE UndecidableInstances #-}
109#ifdef USE_REFLEX_OPTIMIZER
1110{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Class.hs+425−117
@@ -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,16 +36,21 @@ module Reflex.Class
3236 , coerceBehavior
3337 , coerceEvent
3438 , coerceDynamic
39+ , coerceIncremental
3540 , MonadSample (..)
3641 , MonadHold (..)
3742 -- ** 'fan' related types
3843 , EventSelector (..)
44+ , EventSelectorG (..)
3945 , EventSelectorInt (..)
4046 -- * Convenience functions
4147 , constDyn
4248 , pushAlways
4349 -- ** Combining 'Event's
4450 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4554 , mergeMap
4655 , mergeIntMap
4756 , mergeMapIncremental
@@ -56,6 +65,7 @@ module Reflex.Class
5665 , alignEventWithMaybe
5766 -- ** Breaking up 'Event's
5867 , splitE
68+ , fan
5969 , fanEither
6070 , fanThese
6171 , fanMap
@@ -80,6 +90,7 @@ module Reflex.Class
8090 , gate
8191 -- ** Combining 'Dynamic's
8292 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
8394 , distributeListOverDyn
8495 , distributeListOverDynWith
8596 , zipDyn
@@ -133,8 +144,10 @@ module Reflex.Class
133144 -- * Unsafe functions
134145 , unsafeDynamic
135146 , unsafeMapIncremental
136− -- * 'FunctorMaybe'
137− , FunctorMaybe (..)
147+ -- * 'Filterable' convenience functions
148+ , FunctorMaybe -- fmapMaybe is purposely not exported from deprecated 'FunctorMaybe' and the new alias is exported instead
149+ , mapMaybe -- Re-exported for convenience
150+ , fmapMaybe
138151 , fforMaybe
139152 , ffilter
140153 , filterLeft
@@ -144,10 +157,11 @@ module Reflex.Class
144157 , ffor2
145158 , ffor3
146159 -- * Deprecated functions
147− , appendEvents
148− , onceE
149− , sequenceThese
160+ , switchPromptly
161+ , switchPromptOnly
162+ -- * "Cheap" functions
150163 , fmapMaybeCheap
164+ , mapMaybeCheap
151165 , fmapCheap
152166 , fforCheap
153167 , fforMaybeCheap
@@ -155,12 +169,18 @@ module Reflex.Class
155169 , tagCheap
156170 , mergeWithCheap
157171 , mergeWithCheap'
158− , switchPromptly
159− , switchPromptOnly
160172 -- * Slow, but general, implementations
161173 , slowHeadE
162174 ) where
163175
176+#if defined(MIN_VERSION_semialign)
177+import Prelude hiding (zip, zipWith)
178+
179+#if MIN_VERSION_these(0,8,0)
180+import Data.These.Combinators (justThese)
181+#endif
182+#endif
183+
164184import Control.Applicative
165185import Control.Monad.Identity
166186import Control.Monad.Reader
@@ -188,12 +208,14 @@ import qualified Data.IntMap.Strict as IntMap
188208import Data.List.NonEmpty (NonEmpty (..))
189209import Data.Map (Map)
190210import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
191−import Data.Some (Some)
192−import qualified Data.Some as Some
211+import Data.Some (Some(Some))
193212import Data.String
194213import Data.These
195214import Data.Type.Coercion
196−import Reflex.FunctorMaybe
215+import Data.Witherable (Filterable(..))
216+import qualified Data.Witherable as W
217+import Reflex.FunctorMaybe (FunctorMaybe)
218+import qualified Reflex.FunctorMaybe
197219import Reflex.Patch
198220import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
199221
@@ -248,16 +270,20 @@ class ( MonadHold t (PushM t)
248270 -- | Merge a collection of events; the resulting 'Event' will only occur if at
249271 -- least one input event is occurring, and will contain all of the input keys
250272 -- that are occurring simultaneously
251− 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
252− -- | Efficiently fan-out an event to many destinations. This function should
253− -- be partially applied, and then the result applied repeatedly to create
254− -- child events
255− 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
273+
274+ --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
275+ mergeG :: GCompare k => (forall a. q a -> Event t (v a))
276+ -> DMap k q -> Event t (DMap k v)
277+
278+ -- | Efficiently fan-out an event to many destinations. You should save the
279+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
280+ -- create child events
281+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
282+
256283 -- | Create an 'Event' that will occur whenever the currently-selected input
257284 -- 'Event' occurs
258285 switch :: Behavior t (Event t a) -> Event t a
259− -- | Create an 'Event' that will occur whenever the input event is occurring
260− -- and its occurrence value, another 'Event', is also occurring
286+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring
261287 coincidence :: Event t (Event t a) -> Event t a
262288 -- | Extract the 'Behavior' of a 'Dynamic'.
263289 current :: Dynamic t a -> Behavior t a
@@ -271,9 +297,14 @@ class ( MonadHold t (PushM t)
271297 -- that value.
272298 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
273299 -- | Create a merge whose parents can change over time
274− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
300+ mergeIncrementalG :: GCompare k
301+ => (forall a. q a -> Event t (v a))
302+ -> Incremental t (PatchDMap k q)
303+ -> Event t (DMap k v)
275304 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
276− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
305+ mergeIncrementalWithMoveG :: GCompare k
306+ => (forall a. q a -> Event t (v a))
307+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
277308 -- | Extract the 'Behavior' component of an 'Incremental'
278309 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
279310 -- | Extract the 'Event' component of an 'Incremental'
@@ -289,10 +320,28 @@ class ( MonadHold t (PushM t)
289320 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
290321 -- occurrence type
291322 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
323+ -- | Construct a 'Coercion' for an 'Incremental' given 'Coercion's for its
324+ -- patch target and patch types.
325+ incrementalCoercion
326+ :: Coercion (PatchTarget a) (PatchTarget b) -> Coercion a b -> Coercion (Incremental t a) (Incremental t b)
292327 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
293328 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
294329
330+-- | Efficiently fan-out an event to many destinations. You should save the
331+-- result in a @let@-binding, and then repeatedly 'select' on the result to
332+-- create child events
333+fan :: forall t k. (Reflex t, GCompare k)
334+ => Event t (DMap k Identity) -> EventSelector t k
335+ --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
336+fan e = EventSelector (fixup (selectG (fanG e) :: k a -> Event t (Identity a)) :: forall a. k a -> Event t a)
337+ where
338+ fixup :: forall a. (k a -> Event t (Identity a)) -> k a -> Event t a
339+ fixup = case eventCoercion Coercion :: Coercion (Event t (Identity a)) (Event t a) of
340+ Coercion -> coerce
341+
295342--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
343+-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
344+-- keys simultaneously.
296345mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
297346mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
298347
@@ -308,6 +357,12 @@ coerceEvent = coerceWith $ eventCoercion Coercion
308357coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
309358coerceDynamic = coerceWith $ dynamicCoercion Coercion
310359
360+-- | Coerce an 'Incremental' between representationally-equivalent value types
361+coerceIncremental
362+ :: (Reflex t, Coercible a b, Coercible (PatchTarget a) (PatchTarget b))
363+ => Incremental t a -> Incremental t b
364+coerceIncremental = coerceWith $ incrementalCoercion Coercion Coercion
365+
311366-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
312367-- __must__ change when and only when the 'Event' fires, such that the
313368-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
@@ -361,26 +416,91 @@ class MonadSample t m => MonadHold t m where
361416 -- the supplied 'Event'.
362417 headE :: Event t a -> m (Event t a)
363418
364−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
419+-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
420+-- using the combining function to produce a patch.
421+accumIncremental
422+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
423+ => (PatchTarget p -> b -> p)
424+ -> PatchTarget p
425+ -> Event t b
426+ -> m (Incremental t p)
365427accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
366−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)
428+
429+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
430+accumMIncremental
431+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
432+ => (PatchTarget p -> b -> PushM t p)
433+ -> PatchTarget p
434+ -> Event t b
435+ -> m (Incremental t p)
367436accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
368−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
437+
438+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
439+-- combining function produces @Nothing@)
440+accumMaybeIncremental
441+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
442+ => (PatchTarget p -> b -> Maybe p)
443+ -> PatchTarget p
444+ -> Event t b
445+ -> m (Incremental t p)
369446accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
370−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)
447+
448+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
449+accumMaybeMIncremental
450+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
451+ => (PatchTarget p -> b -> PushM t (Maybe p))
452+ -> PatchTarget p
453+ -> Event t b
454+ -> m (Incremental t p)
371455accumMaybeMIncremental f z e = do
372456 rec let e' = flip push e $ \o -> do
373457 v <- sample $ currentIncremental d'
374458 f v o
375459 d' <- holdIncremental z e'
376460 return d'
377−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)
461+
462+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event'
463+-- with a function that both accumulates and produces a value to fire
464+-- as an 'Event'. Returns both the accumulated value and the constructed
465+-- 'Event'.
466+mapAccumIncremental
467+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
468+ => (PatchTarget p -> b -> (p, c))
469+ -> PatchTarget p
470+ -> Event t b
471+ -> m (Incremental t p, Event t c)
378472mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
379−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)
473+
474+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
475+mapAccumMIncremental
476+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
477+ => (PatchTarget p -> b -> PushM t (p, c))
478+ -> PatchTarget p
479+ -> Event t b
480+ -> m (Incremental t p, Event t c)
380481mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
381−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)
482+
483+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event' with
484+-- a function that both optionally accumulates and optionally produces
485+-- a value to fire as a separate output 'Event'.
486+-- Note that because 'Nothing's are discarded in both cases, the output
487+-- 'Event' may fire even though the output 'Incremental' has not changed, and
488+-- the output 'Incremental' may update even when the output 'Event' is not firing.
489+mapAccumMaybeIncremental
490+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
491+ => (PatchTarget p -> b -> (Maybe p, Maybe c))
492+ -> PatchTarget p
493+ -> Event t b
494+ -> m (Incremental t p, Event t c)
382495mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
383−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)
496+
497+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
498+mapAccumMaybeMIncremental
499+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
500+ => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c))
501+ -> PatchTarget p
502+ -> Event t b
503+ -> m (Incremental t p, Event t c)
384504mapAccumMaybeMIncremental f z e = do
385505 rec let e' = flip push e $ \o -> do
386506 v <- sample $ currentIncremental d'
@@ -388,9 +508,10 @@ mapAccumMaybeMIncremental f z e = do
388508 return $ case result of
389509 (Nothing, Nothing) -> Nothing
390510 _ -> Just result
391− d' <- holdIncremental z $ fmapMaybe fst e'
392− return (d', fmapMaybe snd e')
511+ d' <- holdIncremental z $ mapMaybe fst e'
512+ return (d', mapMaybe snd e')
393513
514+-- | A somewhat slow implementation of 'headE'
394515slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
395516slowHeadE e = do
396517 rec be <- hold e $ fmapCheap (const never) e'
@@ -399,18 +520,31 @@ slowHeadE e = do
399520
400521-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
401522-- key. This is much more efficient than filtering for each key with
402−-- 'fmapMaybe'.
523+-- 'mapMaybe'.
403524newtype EventSelector t k = EventSelector
404525 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
405526 -- determined by the type of the key, so this can be used to fan-out
406527 -- 'Event's whose sub-'Event's have different types.
407528 --
408529 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
409− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
530+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
410531 -- occurrences of an 'Event'.
411532 select :: forall a. k a -> Event t a
412533 }
413534
535+newtype EventSelectorG t k v = EventSelectorG
536+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
537+ -- determined by the type of the key, so this can be used to fan-out
538+ -- 'Event's whose sub-'Event's have different types.
539+ --
540+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
541+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
542+ -- occurrences of an 'Event'.
543+ selectG :: forall a. k a -> Event t (v a)
544+ }
545+
546+-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
547+-- filtering by key.
414548newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
415549
416550--------------------------------------------------------------------------------
@@ -541,31 +675,39 @@ instance (Reflex t, Num a) => Num (Behavior t a) where
541675 negate = fmap negate
542676 signum = fmap signum
543677
678+instance (Num a, Reflex t) => Num (Dynamic t a) where
679+ (+) = liftA2 (+)
680+ (*) = liftA2 (*)
681+ abs = fmap abs
682+ signum = fmap signum
683+ fromInteger = pure . fromInteger
684+ negate = fmap negate
685+ (-) = liftA2 (-)
686+
544687instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
545688 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
546689 sconcat = pull . fmap sconcat . mapM sample
547−#if MIN_VERSION_semigroups(0,17,0)
548690 stimes n = fmap $ stimes n
549−#else
550− times1p n = fmap $ times1p n
551−#endif
552691
553−-- | Flipped version of 'fmapMaybe'.
554−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
555−fforMaybe = flip fmapMaybe
692+-- | Alias for 'mapMaybe'
693+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
694+fmapMaybe = mapMaybe
695+
696+-- | Flipped version of 'mapMaybe'.
697+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b
698+fforMaybe = flip mapMaybe
556699
557700-- | Filter 'f a' using the provided predicate.
558−-- Relies on 'fforMaybe'.
559−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
560−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
701+ffilter :: Filterable f => (a -> Bool) -> f a -> f a
702+ffilter = W.filter
561703
562704-- | Filter 'Left's from 'f (Either a b)' into 'a'.
563−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
564−filterLeft = fmapMaybe (either Just (const Nothing))
705+filterLeft :: Filterable f => f (Either a b) -> f a
706+filterLeft = mapMaybe (either Just (const Nothing))
565707
566708-- | Filter 'Right's from 'f (Either a b)' into 'b'.
567−filterRight :: FunctorMaybe f => f (Either a b) -> f b
568−filterRight = fmapMaybe (either (const Nothing) Just)
709+filterRight :: Filterable f => f (Either a b) -> f b
710+filterRight = mapMaybe (either (const Nothing) Just)
569711
570712-- | Left-biased event union (prefers left event on simultaneous
571713-- occurrence).
@@ -583,13 +725,18 @@ instance Reflex t => Bind (Event t) where
583725
584726instance Reflex t => Functor (Event t) where
585727 {-# INLINE fmap #-}
586− fmap f = fmapMaybe $ Just . f
728+ fmap f = mapMaybe $ Just . f
587729 {-# INLINE (<$) #-}
588730 x <$ e = fmapCheap (const x) e
589731
732+-- TODO Remove this instance
590733instance Reflex t => FunctorMaybe (Event t) where
591734 {-# INLINE fmapMaybe #-}
592− fmapMaybe f = push $ return . f
735+ fmapMaybe = mapMaybe
736+
737+instance Reflex t => Filterable (Event t) where
738+ {-# INLINE mapMaybe #-}
739+ mapMaybe f = push $ return . f
593740
594741-- | Never: @'zero' = 'never'@.
595742instance Reflex t => Plus (Event t) where
@@ -738,11 +885,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
738885instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
739886 (<>) = alignWith (mergeThese (<>))
740887 sconcat = fmap sconcat . mergeList . toList
741−#if MIN_VERSION_semigroups(0,17,0)
742888 stimes n = fmap $ stimes n
743−#else
744− times1p n = fmap $ times1p n
745−#endif
746889
747890instance (Semigroup a, Reflex t) => Monoid (Event t a) where
748891 mempty = never
@@ -777,7 +920,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
777920mergeList [] = never
778921mergeList es = mergeWithFoldCheap' id es
779922
780−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
923+unsafeMapIncremental
924+ :: (Reflex t, Patch p, Patch p')
925+ => (PatchTarget p -> PatchTarget p')
926+ -> (p -> p')
927+ -> Incremental t p
928+ -> Incremental t p'
781929unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
782930
783931-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
@@ -807,7 +955,7 @@ fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
807955fanEither e =
808956 let justLeft = either Just (const Nothing)
809957 justRight = either (const Nothing) Just
810− in (fmapMaybe justLeft e, fmapMaybe justRight e)
958+ in (mapMaybe justLeft e, mapMaybe justRight e)
811959
812960-- | Split the event into separate events for 'This' and 'That' values,
813961-- allowing them to fire simultaneously when the input value is 'These'.
@@ -819,7 +967,7 @@ fanThese e =
819967 that (That y) = Just y
820968 that (These _ y) = Just y
821969 that _ = Nothing
822− in (fmapMaybe this e, fmapMaybe that e)
970+ in (mapMaybe this e, mapMaybe that e)
823971
824972-- | Split the event into an 'EventSelector' that allows efficient selection of
825973-- the individual 'Event's.
@@ -894,6 +1042,9 @@ coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $
8941042 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
8951043 ]
8961044
1045+-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
1046+-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
1047+-- fires with the patched value.
8971048switchHoldPromptOnlyIncremental
8981049 :: forall t m p pt w
8991050 . ( Reflex t
@@ -918,7 +1069,15 @@ switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = d
9181069
9191070instance Reflex t => Align (Event t) where
9201071 nil = never
1072+#if MIN_VERSION_these(0, 8, 0)
1073+instance Reflex t => Semialign (Event t) where
1074+#endif
9211075 align = alignEventWithMaybe Just
1076+
1077+#if defined(MIN_VERSION_semialign)
1078+ zip x y = mapMaybe justThese $ align x y
1079+#endif
1080+
9221081
9231082-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
9241083-- the 'Behavior' is true at the time of occurrence.
@@ -959,11 +1118,7 @@ zipDynWith f da db =
9591118
9601119instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
9611120 (<>) = zipDynWith (<>)
962−#if MIN_VERSION_semigroups(0,17,0)
9631121 stimes n = fmap $ stimes n
964−#else
965− times1p n = fmap $ times1p n
966−#endif
9671122
9681123instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
9691124 mconcat = distributeListOverDynWith mconcat
@@ -974,12 +1129,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
9741129-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
9751130-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
9761131distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
977−distributeDMapOverDynPure dm = case DMap.toList dm of
1132+distributeDMapOverDynPure = distributeDMapOverDynPureG coerceDynamic
1133+
1134+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1135+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1136+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1137+distributeDMapOverDynPureG
1138+ :: forall t k q v. (Reflex t, GCompare k)
1139+ => (forall a. q a -> Dynamic t (v a))
1140+ -> DMap k q -> Dynamic t (DMap k v)
1141+distributeDMapOverDynPureG nt dm = case DMap.toList dm of
9781142 [] -> constDyn DMap.empty
979− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1143+ [k :=> v] -> DMap.singleton k <$> nt v
9801144 _ ->
981− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
982− edmPre = merge $ DMap.map updated dm
1145+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
1146+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
9831147 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
9841148 olds <- sample $ current result
9851149 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -991,21 +1155,32 @@ distributeListOverDyn = distributeListOverDynWith id
9911155
9921156-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
9931157distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
994−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
1158+distributeListOverDynWith f =
1159+ fmap (f . map fromDSum . DMap.toAscList) .
1160+ distributeDMapOverDynPure .
1161+ DMap.fromDistinctAscList .
1162+ zipWith toDSum [0..]
1163+ where
1164+ toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
1165+ toDSum k v = Const2 k :=> v
1166+ fromDSum :: DSum (Const2 Int a) Identity -> a
1167+ fromDSum (Const2 _ :=> Identity v) = v
9951168
9961169-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
9971170-- unless the second supplied 'Event' occurs simultaneously.
9981171difference :: Reflex t => Event t a -> Event t b -> Event t a
9991172difference = alignEventWithMaybe $ \case
10001173 This a -> Just a
1001− _ -> Nothing
1174+ _ -> Nothing
10021175
1176+-- | Zips two values by taking the union of their shapes and combining with the provided function.
1177+-- 'Nothing' values are dropped.
10031178alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1004−alignEventWithMaybe f ea eb =
1005− fmapMaybe (f <=< dmapToThese)
1006− $ merge
1007− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1179+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $
1180+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
10081181
1182+-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that
1183+-- matches the provided key.
10091184filterEventKey
10101185 :: forall t m k v a.
10111186 ( Reflex t
@@ -1023,7 +1198,10 @@ filterEventKey k kv' = do
10231198 Nothing -> Nothing
10241199 takeWhileJustE f kv'
10251200
1026−
1201+-- | "Factor" the input 'DSum' 'Event' to produce an 'Event' which
1202+-- fires when the 'DSum' key changes and contains both the value of the
1203+-- 'DSum' at switchover and an 'Event' of values produced by subsequent
1204+-- firings of the input 'Event' that do not change the 'DSum' key.
10271205factorEvent
10281206 :: forall t m k v a.
10291207 ( Reflex t
@@ -1035,9 +1213,9 @@ factorEvent
10351213 -> Event t (DSum k v)
10361214 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
10371215factorEvent k0 kv' = do
1038− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1216+ key :: Behavior t (Some k) <- hold (Some k0) $ fmapCheap (\(k :=> _) -> Some k) kv'
10391217 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1040− Some.This oldKey -> case newKey `geq` oldKey of
1218+ Some oldKey -> case newKey `geq` oldKey of
10411219 Just Refl -> return Nothing
10421220 Nothing -> do
10431221 newInner <- filterEventKey newKey kv'
@@ -1073,26 +1251,94 @@ class Reflex t => Accumulator t f | f -> t where
10731251 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
10741252 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
10751253
1076−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1254+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1255+-- with the provided function. See 'foldDyn'.
1256+accumDyn
1257+ :: (Reflex t, MonadHold t m, MonadFix m)
1258+ => (a -> b -> a)
1259+ -> a
1260+ -> Event t b
1261+ -> m (Dynamic t a)
10771262accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1078−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
1263+
1264+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1265+-- with the provided 'PushM' action.
1266+accumMDyn
1267+ :: (Reflex t, MonadHold t m, MonadFix m)
1268+ => (a -> b -> PushM t a)
1269+ -> a
1270+ -> Event t b
1271+ -> m (Dynamic t a)
10791272accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1080−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1273+
1274+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1275+-- with the provided function, discarding 'Nothing' results.
1276+accumMaybeDyn
1277+ :: (Reflex t, MonadHold t m, MonadFix m)
1278+ => (a -> b -> Maybe a)
1279+ -> a
1280+ -> Event t b
1281+ -> m (Dynamic t a)
10811282accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1082−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
1283+
1284+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1285+-- with the provided 'PushM' action, discarding 'Nothing' results.
1286+accumMaybeMDyn
1287+ :: (Reflex t, MonadHold t m, MonadFix m)
1288+ => (a -> b -> PushM t (Maybe a))
1289+ -> a
1290+ -> Event t b
1291+ -> m (Dynamic t a)
10831292accumMaybeMDyn f z e = do
10841293 rec let e' = flip push e $ \o -> do
10851294 v <- sample $ current d'
10861295 f v o
10871296 d' <- holdDyn z e'
10881297 return d'
1089−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1298+
1299+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1300+-- with a function that both accumulates and produces a value to fire
1301+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
1302+mapAccumDyn
1303+ :: (Reflex t, MonadHold t m, MonadFix m)
1304+ => (a -> b -> (a, c))
1305+ -> a
1306+ -> Event t b
1307+ -> m (Dynamic t a, Event t c)
10901308mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1091−mapAccumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1309+
1310+-- | Similar to 'mapAccumDyn' except that the combining function is a
1311+-- 'PushM' action.
1312+mapAccumMDyn
1313+ :: (Reflex t, MonadHold t m, MonadFix m)
1314+ => (a -> b -> PushM t (a, c))
1315+ -> a
1316+ -> Event t b
1317+ -> m (Dynamic t a, Event t c)
10921318mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1093−mapAccumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1319+
1320+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event' with
1321+-- a function that both optionally accumulates and optionally produces
1322+-- a value to fire as a separate output 'Event'.
1323+-- Note that because 'Nothing's are discarded in both cases, the output
1324+-- 'Event' may fire even though the output 'Dynamic' has not changed, and
1325+-- the output 'Dynamic' may update even when the output 'Event' is not firing.
1326+mapAccumMaybeDyn
1327+ :: (Reflex t, MonadHold t m, MonadFix m)
1328+ => (a -> b -> (Maybe a, Maybe c))
1329+ -> a
1330+ -> Event t b
1331+ -> m (Dynamic t a, Event t c)
10941332mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1095−mapAccumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1333+
1334+-- | Like 'mapAccumMaybeDyn' except that the combining function is a
1335+-- 'PushM' action.
1336+mapAccumMaybeMDyn
1337+ :: (Reflex t, MonadHold t m, MonadFix m)
1338+ => (a -> b -> PushM t (Maybe a, Maybe c))
1339+ -> a
1340+ -> Event t b
1341+ -> m (Dynamic t a, Event t c)
10961342mapAccumMaybeMDyn f z e = do
10971343 rec let e' = flip push e $ \o -> do
10981344 v <- sample $ current d'
@@ -1100,18 +1346,42 @@ mapAccumMaybeMDyn f z e = do
11001346 return $ case result of
11011347 (Nothing, Nothing) -> Nothing
11021348 _ -> Just result
1103− d' <- holdDyn z $ fmapMaybe fst e'
1104− return (d', fmapMaybe snd e')
1349+ d' <- holdDyn z $ mapMaybe fst e'
1350+ return (d', mapMaybe snd e')
11051351
1352+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1353+-- with the provided function.
11061354{-# INLINE accumB #-}
1107−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1355+accumB
1356+ :: (Reflex t, MonadHold t m, MonadFix m)
1357+ => (a -> b -> a)
1358+ -> a
1359+ -> Event t b
1360+ -> m (Behavior t a)
11081361accumB f = accumMaybeB $ \v o -> Just $ f v o
1362+
1363+-- | Like 'accumB' except that the combining function is a 'PushM' action.
11091364{-# INLINE accumMB #-}
1110−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
1365+accumMB
1366+ :: (Reflex t, MonadHold t m, MonadFix m)
1367+ => (a -> b -> PushM t a)
1368+ -> a
1369+ -> Event t b
1370+ -> m (Behavior t a)
11111371accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1372+
1373+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1374+-- with the provided function, discarding 'Nothing' results.
11121375{-# INLINE accumMaybeB #-}
1113−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1376+accumMaybeB
1377+ :: (Reflex t, MonadHold t m, MonadFix m)
1378+ => (a -> b -> Maybe a)
1379+ -> a
1380+ -> Event t b
1381+ -> m (Behavior t a)
11141382accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1383+
1384+-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action.
11151385{-# INLINE accumMaybeMB #-}
11161386accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
11171387accumMaybeMB f z e = do
@@ -1120,16 +1390,42 @@ accumMaybeMB f z e = do
11201390 f v o
11211391 d' <- hold z e'
11221392 return d'
1393+
1394+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1395+-- with a function that both accumulates and produces a value to fire
1396+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
11231397{-# INLINE mapAccumB #-}
1124−mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1398+mapAccumB
1399+ :: (Reflex t, MonadHold t m, MonadFix m)
1400+ => (a -> b -> (a, c))
1401+ -> a
1402+ -> Event t b
1403+ -> m (Behavior t a, Event t c)
11251404mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1405+
1406+-- | Like 'mapAccumB' except that the combining function is a 'PushM' action.
11261407{-# INLINE mapAccumMB #-}
1127−mapAccumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1408+mapAccumMB
1409+ :: (Reflex t, MonadHold t m, MonadFix m)
1410+ => (a -> b -> PushM t (a, c))
1411+ -> a
1412+ -> Event t b
1413+ -> m (Behavior t a, Event t c)
11281414mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1415+
1416+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event' with
1417+-- a function that both optionally accumulates and optionally produces
1418+-- a value to fire as a separate output 'Event'. 'Nothing's are discarded.
11291419{-# INLINE mapAccumMaybeB #-}
1130−mapAccumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1420+mapAccumMaybeB
1421+ :: (Reflex t, MonadHold t m, MonadFix m)
1422+ => (a -> b -> (Maybe a, Maybe c))
1423+ -> a
1424+ -> Event t b
1425+ -> m (Behavior t a, Event t c)
11311426mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
11321427
1428+-- | Like 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
11331429{-# INLINE mapAccumMaybeMB #-}
11341430mapAccumMaybeMB :: (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)
11351431mapAccumMaybeMB f z e = do
@@ -1139,8 +1435,8 @@ mapAccumMaybeMB f z e = do
11391435 return $ case result of
11401436 (Nothing, Nothing) -> Nothing
11411437 _ -> Just result
1142− d' <- hold z $ fmapMaybe fst e'
1143− return (d', fmapMaybe snd e')
1438+ d' <- hold z $ mapMaybe fst e'
1439+ return (d', mapMaybe snd e')
11441440
11451441-- | Accumulate occurrences of an 'Event', producing an output occurrence each
11461442-- time. Discard the underlying 'Accumulator'.
@@ -1284,38 +1580,70 @@ infixl 4 <@
12841580-- Cheap Functions
12851581------------------
12861582
1583+-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'.
12871584{-# INLINE pushAlwaysCheap #-}
12881585pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
12891586pushAlwaysCheap f = pushCheap (fmap Just . f)
12901587
1588+-- | A "cheap" version of 'mapMaybe'. See the performance note on 'pushCheap'.
1589+{-# INLINE mapMaybeCheap #-}
1590+mapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1591+mapMaybeCheap f = pushCheap $ return . f
1592+
1593+-- | An alias for 'mapMaybeCheap'
12911594{-# INLINE fmapMaybeCheap #-}
12921595fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1293−fmapMaybeCheap f = pushCheap $ return . f
1596+fmapMaybeCheap = mapMaybeCheap
1597+
12941598
1599+-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'.
12951600{-# INLINE fforMaybeCheap #-}
12961601fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1297−fforMaybeCheap = flip fmapMaybeCheap
1602+fforMaybeCheap = flip mapMaybeCheap
12981603
1604+-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'.
12991605{-# INLINE fforCheap #-}
13001606fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
13011607fforCheap = flip fmapCheap
13021608
1609+-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'.
13031610{-# INLINE fmapCheap #-}
13041611fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
13051612fmapCheap f = pushCheap $ return . Just . f
13061613
1614+-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'.
13071615{-# INLINE tagCheap #-}
13081616tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
13091617tagCheap b = pushAlwaysCheap $ \_ -> sample b
13101618
1619+-- | Merge a collection of events; the resulting 'Event' will only occur if at
1620+-- least one input event is occurring, and will contain all of the input keys
1621+-- that are occurring simultaneously
1622+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
1623+merge = mergeG coerceEvent
1624+{-# INLINE merge #-}
1625+
1626+-- | Create a merge whose parents can change over time
1627+mergeIncremental :: (Reflex t, GCompare k)
1628+ => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
1629+mergeIncremental = mergeIncrementalG coerceEvent
1630+
1631+-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
1632+mergeIncrementalWithMove :: (Reflex t, GCompare k)
1633+ => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
1634+mergeIncrementalWithMove = mergeIncrementalWithMoveG coerceEvent
1635+
1636+-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
13111637{-# INLINE mergeWithCheap #-}
13121638mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
13131639mergeWithCheap = mergeWithCheap' id
13141640
1641+-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'.
13151642{-# INLINE mergeWithCheap' #-}
13161643mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
13171644mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
13181645
1646+-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
13191647{-# INLINE mergeWithFoldCheap' #-}
13201648mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
13211649mergeWithFoldCheap' f es =
@@ -1328,31 +1656,11 @@ mergeWithFoldCheap' f es =
13281656-- Deprecated functions
13291657--------------------------------------------------------------------------------
13301658
1331−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1332−-- occurs. If both occur at the same time they are combined using 'mappend'.
1333−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1334−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1335−appendEvents = alignWith $ mergeThese mappend
1336−
1337−-- | Alias for 'headE'
1338−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1339−onceE :: MonadHold t m => Event t a -> m (Event t a)
1340−onceE = headE
1341−
1342−-- | Run both sides of a 'These' monadically, combining the results.
1343−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1344−#ifdef USE_TEMPLATE_HASKELL
1345−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1346−#endif
1347−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1348−sequenceThese t = case t of
1349− This ma -> fmap This ma
1350− These ma mb -> liftM2 These ma mb
1351− That mb -> fmap That mb
1352−
13531659{-# 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'. " #-}
1660+-- | See 'switchHoldPromptly'
13541661switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
13551662switchPromptly = switchHoldPromptly
13561663{-# 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'. " #-}
1664+-- | See 'switchHoldPromptOnly'
13571665switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
13581666switchPromptOnly = switchHoldPromptOnly
src/Reflex/Collection.hs+12−10
@@ -10,12 +10,14 @@
1010#ifdef USE_REFLEX_OPTIMIZER
1111{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1212#endif
13+-- |
14+-- Module:
15+-- Reflex.Collection
1316module Reflex.Collection
1417 (
1518 -- * Widgets on Collections
1619 listHoldWithKey
1720 , listWithKey
18− , listWithKey'
1921 , listWithKeyShallowDiff
2022 , listViewWithKey
2123 , selectViewListWithKey
@@ -25,6 +27,10 @@ module Reflex.Collection
2527 , simpleList
2628 ) where
2729
30+#if defined(MIN_VERSION_semialign)
31+import Prelude hiding (zip, zipWith)
32+#endif
33+
2834import Control.Monad.Identity
2935import Data.Align
3036import Data.Functor.Misc
@@ -38,6 +44,9 @@ import Reflex.Adjustable.Class
3844import Reflex.Dynamic
3945import Reflex.PostBuild.Class
4046
47+-- | Create a set of widgets based on the provided 'Map'. When the
48+-- input 'Event' fires, remove widgets for keys with the value 'Nothing'
49+-- and add/replace widgets for keys with 'Just' values.
4150listHoldWithKey
4251 :: forall t m k v a
4352 . (Ord k, Adjustable t m, MonadHold t m)
@@ -96,15 +105,6 @@ listWithKey vals mkChild = do
96105 listHoldWithKey Map.empty changeVals $ \k v ->
97106 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
98107
99−{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
100−listWithKey'
101− :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m)
102− => Map k v
103− -> Event t (Map k (Maybe v))
104− -> (k -> v -> Event t v -> m a)
105− -> m (Dynamic t (Map k a))
106−listWithKey' = listWithKeyShallowDiff
107−
108108-- | Display the given map of items (in key order) using the builder
109109-- function provided, and update it with the given event. 'Nothing'
110110-- update entries will delete the corresponding children, and 'Just'
@@ -182,6 +182,8 @@ selectViewListWithKey selection vals mkChild = do
182182 return $ fmap ((,) k) selectSelf
183183 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
184184
185+-- | Like 'selectViewListWithKey' but discards the value of the list
186+-- item widget's output 'Event'.
185187selectViewListWithKey_
186188 :: forall t m k v a
187189 . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
src/Reflex/Dynamic.hs+18−168
@@ -17,8 +17,12 @@
1717#ifdef USE_REFLEX_OPTIMIZER
1818{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1919#endif
20−-- | This module contains various functions for working with 'Dynamic' values.
21−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
20+-- |
21+-- Module:
22+-- Reflex.Dynamic
23+-- Description:
24+-- This module contains various functions for working with 'Dynamic' values.
25+-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
2226module Reflex.Dynamic
2327 ( -- * Basics
2428 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
@@ -70,32 +74,12 @@ module Reflex.Dynamic
7074 , distributeFHListOverDynPure
7175 -- * Unsafe
7276 , unsafeDynamic
73− -- * Deprecated functions
74− , apDyn
75− , attachDyn
76− , attachDynWith
77− , attachDynWithMaybe
78− , collectDyn
79− , combineDyn
80− , distributeDMapOverDyn
81− , distributeFHListOverDyn
82− , forDyn
83− , getDemuxed
84− , joinDyn
85− , mapDyn
86− , mconcatDyn
87− , nubDyn
88− , splitDyn
89− , tagDyn
90− , uniqDyn
91− , uniqDynBy
9277 ) where
9378
9479import Data.Functor.Compose
9580import Data.Functor.Misc
9681import Reflex.Class
9782
98−import Control.Applicative ((<*>))
9983import Control.Monad
10084import Control.Monad.Fix
10185import Control.Monad.Identity
@@ -103,11 +87,10 @@ import Data.Align
10387import Data.Dependent.Map (DMap)
10488import qualified Data.Dependent.Map as DMap
10589import Data.Dependent.Sum (DSum (..))
106−import Data.Functor.Product
10790import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
10891import Data.Map (Map)
10992import Data.Maybe
110−import Data.Monoid hiding (Product)
93+import Data.Monoid ((<>))
11194import Data.These
11295
11396import Debug.Trace
@@ -220,23 +203,15 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
220203
221204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
222205-- non-'Dynamic' elements, preserving the order of the elements.
206+{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
223207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
224−distributeListOverDynPure =
225− fmap (map fromDSum . DMap.toAscList) .
226− distributeDMapOverDynPure .
227− DMap.fromDistinctAscList .
228− zipWith toDSum [0..]
229− where
230− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
231− toDSum k v = Const2 k :=> v
232− fromDSum :: DSum (Const2 Int a) Identity -> a
233− fromDSum (Const2 _ :=> Identity v) = v
208+distributeListOverDynPure = distributeListOverDyn
234209
235210--TODO: Generalize this to functors other than Maps
236211-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
237212-- with the current values of the 'Dynamic's in a map.
238213joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
239−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
214+joinDynThroughMap = (distributeMapOverDynPure =<<)
240215
241216-- | Print the value of the 'Dynamic' when it is first read and on each
242217-- subsequent change that is observed (as 'traceEvent'), prefixed with 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+8−3
@@ -22,6 +22,7 @@ module Reflex.DynamicWriter.Base
2222import Control.Monad.Exception
2323import Control.Monad.Identity
2424import Control.Monad.IO.Class
25+import Control.Monad.Primitive
2526import Control.Monad.Reader
2627import Control.Monad.Ref
2728import Control.Monad.State.Strict
@@ -34,7 +35,7 @@ import Data.IntMap (IntMap)
3435import qualified Data.IntMap as IntMap
3536import Data.Map (Map)
3637import qualified Data.Map as Map
37−import Data.Semigroup
38+import Data.Semigroup (Semigroup(..))
3839import Data.Some (Some)
3940import Data.These
4041
@@ -87,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
8788 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
8889 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
8990
90−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
9192newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
9293 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
9394
@@ -115,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
115116 (result, ws) <- runStateT a []
116117 return (result, mconcat $ reverse ws)
117118
118−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
119+instance (Monad m, Monoid w, Reflex t) => DynamicWriter t w (DynamicWriterT t w m) where
119120 tellDyn w = DynamicWriterT $ modify (w :)
120121
121122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
@@ -140,6 +141,10 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where
140141 get = lift get
141142 put = lift . put
142143
144+instance PrimMonad m => PrimMonad (DynamicWriterT t w m) where
145+ type PrimState (DynamicWriterT t w m) = PrimState m
146+ primitive = lift . primitive
147+
143148newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
144149
145150-- | When the execution of a 'DynamicWriterT' action is adjusted using
src/Reflex/DynamicWriter/Class.hs+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+3−3
@@ -25,7 +25,7 @@ module Reflex.EventWriter.Base
2525import Reflex.Adjustable.Class
2626import Reflex.Class
2727import Reflex.EventWriter.Class (EventWriter, tellEvent)
28−import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
2929import Reflex.Host.Class
3030import Reflex.PerformEvent.Class
3131import Reflex.PostBuild.Class
@@ -52,7 +52,7 @@ import qualified Data.Map as Map
5252import Data.Semigroup
5353import Data.Some (Some)
5454import Data.Tuple
55−import Data.Type.Equality hiding (apply)
55+import Data.Type.Equality
5656
5757import Unsafe.Coerce
5858
@@ -278,7 +278,7 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
278278 askQueryResult = lift askQueryResult
279279 queryIncremental = lift . queryIncremental
280280
281−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (EventWriterT t v m) where
281+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where
282282 tellDyn = lift . tellDyn
283283
284284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
src/Reflex/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+10−6
@@ -6,14 +6,18 @@
66{-# LANGUAGE JavaScriptFFI #-}
77#endif
88
9−-- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
10−-- referred to by a 'FastWeak' stays live while the ticket is held (live).
9+-- |
10+-- Module:
11+-- Reflex.FastWeak
12+-- Description:
13+-- 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
14+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
1115--
12−-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
13−-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
16+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
17+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
1418--
15−-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
16−-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
19+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
20+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
1721module Reflex.FastWeak
1822 ( FastWeakTicket
1923 , FastWeak
src/Reflex/FunctorMaybe.hs+25−21
@@ -1,43 +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
717import Data.IntMap (IntMap)
8−import qualified Data.IntMap as IntMap
918import Data.Map (Map)
10−import qualified Data.Map as Map
11−import Data.Maybe
19+#if MIN_VERSION_base(4,9,0)
20+import Data.Semigroup (Option(..))
21+#endif
22+import Data.Witherable
1223
1324--TODO: See if there's a better class in the standard libraries already
1425
1526-- | A class for values that combines filtering and mapping using 'Maybe'.
16−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
17−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
18−-- and e.g. 'Event' is instance of neither.
19−--
20−-- A definition of 'fmapMaybe' must satisfy the following laws:
21−--
22−-- [/identity/]
23−-- @'fmapMaybe' 'Just' ≡ 'id'@
24−--
25−-- [/composition/]
26−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
27+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.
28+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-}
2729class FunctorMaybe f where
2830 -- | Combined mapping and filtering function.
2931 fmapMaybe :: (a -> Maybe b) -> f a -> f b
3032
31−-- | @fmapMaybe = (=<<)
3233instance FunctorMaybe Maybe where
33− fmapMaybe = (=<<)
34+ fmapMaybe = mapMaybe
35+
36+#if MIN_VERSION_base(4,9,0)
37+deriving instance FunctorMaybe Option
38+#endif
3439
35−-- | @fmapMaybe = mapMaybe@
3640instance FunctorMaybe [] where
3741 fmapMaybe = mapMaybe
3842
3943instance FunctorMaybe (Map k) where
40− fmapMaybe = Map.mapMaybe
44+ fmapMaybe = mapMaybe
4145
4246instance FunctorMaybe IntMap where
43− fmapMaybe = IntMap.mapMaybe
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+8−4
@@ -1,10 +1,14 @@
1−-- | This module provides a GHC plugin designed to improve code that uses
2−-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
3−-- definition that doesn't have an explicit inlining pragma. In the future,
4−-- additional optimizations are likely to be added.
51{-# LANGUAGE CPP #-}
62{-# LANGUAGE LambdaCase #-}
73{-# LANGUAGE OverloadedStrings #-}
4+-- |
5+-- Module:
6+-- Reflex.Optimizer
7+-- Description:
8+-- This module provides a GHC plugin designed to improve code that uses
9+-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
10+-- definition that doesn't have an explicit inlining pragma. In the future,
11+-- additional optimizations are likely to be added.
812module Reflex.Optimizer
913 ( plugin
1014 ) where
src/Reflex/Patch.hs+12−5
@@ -1,6 +1,10 @@
1−-- | This module defines the 'Patch' class, which is used by Reflex to manage
2−-- changes to 'Reflex.Class.Incremental' values.
31{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Reflex.Patch
5+-- Description:
6+-- This module defines the 'Patch' class, which is used by Reflex to manage
7+-- changes to 'Reflex.Class.Incremental' values.
48module Reflex.Patch
59 ( module Reflex.Patch
610 , module X
@@ -17,11 +21,9 @@ import Reflex.Patch.Map as X
1721import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
1822 patchMapWithMoveNewElementsMap, unPatchMapWithMove,
1923 unsafePatchMapWithMove)
20−
24+import Data.Map.Monoidal (MonoidalMap)
2125import Data.Semigroup (Semigroup (..), (<>))
2226
23−---- Patches based on commutative groups
24−
2527-- | A 'Group' is a 'Monoid' where every element has an inverse.
2628class (Semigroup q, Monoid q) => Group q where
2729 negateG :: q -> q
@@ -37,3 +39,8 @@ newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
3739instance Additive p => Patch (AdditivePatch p) where
3840 type PatchTarget (AdditivePatch p) = p
3941 apply (AdditivePatch p) q = Just $ p <> q
42+
43+instance (Ord k, Group q) => Group (MonoidalMap k q) where
44+ negateG = fmap negateG
45+
46+instance (Ord k, Additive q) => Additive (MonoidalMap k q)
src/Reflex/Patch/Class.hs+1−1
@@ -4,7 +4,7 @@ module Reflex.Patch.Class where
44
55import Control.Monad.Identity
66import Data.Maybe
7−import Data.Semigroup
7+import Data.Semigroup (Semigroup(..))
88
99-- | A 'Patch' type represents a kind of change made to a datastructure.
1010--
src/Reflex/Patch/DMapWithMove.hs+6−7
@@ -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
@@ -105,8 +104,8 @@ validationErrorsForPatchDMapWithMove m =
105104 unbalancedMove _ = Nothing
106105
107106-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
108−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
109− PatchDMapWithMove a == PatchDMapWithMove b = a == b
107+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
110109
111110-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
112111data Pair1 f g a = Pair1 (f a) (g a)
@@ -311,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
311310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
312311 From_Insert v -> MapWithMove.From_Insert $ f v
313312 From_Delete -> MapWithMove.From_Delete
314− From_Move k -> MapWithMove.From_Move $ Some.This k
315− , 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)
316315 }
317316
318317-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
src/Reflex/Patch/IntMap.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DeriveFoldable #-}
32{-# LANGUAGE DeriveFunctor #-}
43{-# LANGUAGE DeriveTraversable #-}
@@ -33,14 +32,7 @@ instance Patch (PatchIntMap a) where
3332instance Semigroup (PatchIntMap v) where
3433 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3534 -- PatchMap is idempotent, so stimes n is id for every n
36−#if MIN_VERSION_semigroups(0,17,0)
3735 stimes = stimesIdempotentMonoid
38−#else
39− times1p n x = case compare n 0 of
40− LT -> error "stimesIdempotentMonoid: negative multiplier"
41− EQ -> mempty
42− GT -> x
43−#endif
4436
4537-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
4638-- (that is, all inserts/updates), producing a @PatchIntMap b@.
src/Reflex/Patch/Map.hs−8
@@ -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)
@@ -35,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
3534instance Ord k => Semigroup (PatchMap k v) where
3635 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3736 -- PatchMap is idempotent, so stimes n is id for every n
38−#if MIN_VERSION_semigroups(0,17,0)
3937 stimes = stimesIdempotentMonoid
40−#else
41− times1p n x = case compare n 0 of
42− LT -> error "stimesIdempotentMonoid: negative multiplier"
43− EQ -> mempty
44− GT -> x
45−#endif
4638
4739-- | The empty 'PatchMap' contains no insertions or deletions
4840instance Ord k => Monoid (PatchMap k v) where
src/Reflex/Patch/MapWithMove.hs+1−1
@@ -22,7 +22,7 @@ import qualified Data.Map as Map
2222import Data.Maybe
2323import Data.Semigroup (Semigroup (..), (<>))
2424import qualified Data.Set as Set
25−import Data.These
25+import Data.These (These(..))
2626import Data.Tuple
2727
2828-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
src/Reflex/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+28−22
@@ -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,10 +41,11 @@ 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
4647-- with the type @/t/@.
47−data Pure t
48+data Pure (t :: Type)
4849
4950-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
5051-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
@@ -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+28−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 #-}
@@ -38,8 +37,7 @@ import Data.Map (Map)
3837import qualified Data.Map as Map
3938import Data.Monoid ((<>))
4039import qualified Data.Semigroup as S
41−import Data.Some (Some)
42−import qualified Data.Some as Some
40+import Data.Some (Some(Some))
4341import Data.These
4442
4543import Reflex.Class
@@ -74,26 +72,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
7472getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
7573getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
7674
77−{-
78−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
79− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
80− bs' = fmapCheap snd $ r'
81− patches = unsafeBuildIncremental (sampleBs bs0) $
82− flip pushCheap bs' $ \bs -> do
83− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
84− return (Just (AdditivePatch p))
85−-}
75+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
76+maskMempty x = if x == mempty then Nothing else Just x
8677
87−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
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
8879 runWithReplace (QueryT a0) a' = do
8980 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
9081 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
9182 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
9283 bs' = fmapCheap snd $ r'
9384 bbs <- hold bs0 bs'
94− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
85+ let patches = flip pushCheap bs' $ \newBs -> do
9586 oldBs <- sample bbs
96− (~~) <$> sampleBs newBs <*> sampleBs oldBs
87+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
9788 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
9889 QueryT $ lift $ tellEvent patches
9990 return (r0, fmapCheap fst r')
@@ -152,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
152143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
153144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
154145 liftedBs0 :: Map (Some k) [Behavior t q]
155− 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
156147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
157148 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
158− 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
159150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
160151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
161152 accumBehaviors :: forall m'. MonadHold t m'
@@ -171,19 +162,19 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
171162 let p k bs = case Map.lookup k bs0 of
172163 Nothing -> case bs of
173164 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
174− Nothing -> return mempty
165+ Nothing -> return Nothing
175166 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
176− Just newBs -> sampleBs newBs
167+ Just newBs -> maskMempty <$> sampleBs newBs
177168 Just oldBs -> case bs of
178169 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
179− Nothing -> negateG <$> sampleBs oldBs
170+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
180171 -- 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
181172 -- composed with the sampling the child's new state.
182− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
173+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
183174 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
184175 -- child patches and wrap them in AdditivePatch.
185− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
186− return (apply pbs bs0, Just patch)
176+ patch <- fold <$> Map.traverseWithKey p bs'
177+ return (apply pbs bs0, AdditivePatch <$> patch)
187178 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
188179 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
189180 return (liftedResult0, liftedResult')
@@ -196,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
196187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
197188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
198189 liftedBs0 :: Map (Some k) [Behavior t q]
199− 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
200191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
201192 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
202− 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 -}
203194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
204195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
205196 accumBehaviors' :: forall m'. MonadHold t m'
@@ -216,28 +207,28 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
216207 p k bs = case Map.lookup k bs0 of
217208 Nothing -> case MapWithMove._nodeInfo_from bs of
218209 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
219− MapWithMove.From_Delete -> return mempty
210+ MapWithMove.From_Delete -> return Nothing
220211 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
221− MapWithMove.From_Insert newBs -> sampleBs newBs
212+ MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs
222213 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
223− Nothing -> return mempty
224− Just newBs -> sampleBs newBs
214+ Nothing -> return Nothing
215+ Just newBs -> maskMempty <$> sampleBs newBs
225216 Just oldBs -> case MapWithMove._nodeInfo_from bs of
226217 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
227− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
218+ MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs
228219 -- 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
229220 -- composed with the sampling the child's new state.
230− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
221+ MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
231222 MapWithMove.From_Move k'
232− | k' == k -> return mempty
223+ | k' == k -> return Nothing
233224 | otherwise -> case Map.lookup k' bs0 of
234225 -- If we are moving from a non-existent key, that is a delete
235− Nothing -> negateG <$> sampleBs oldBs
236− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
226+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
227+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
237228 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
238229 -- child patches and wrap them in AdditivePatch.
239− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
240− return (apply pbs bs0, Just patch)
230+ patch <- fold <$> Map.traverseWithKey p bs'
231+ return (apply pbs bs0, AdditivePatch <$> patch)
241232 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
242233 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
243234 return (liftedResult0, liftedResult')
@@ -340,5 +331,5 @@ instance Requester t m => Requester t (QueryT t q m) where
340331instance EventWriter t w m => EventWriter t w (QueryT t q m) where
341332 tellEvent = lift . tellEvent
342333
343−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
344335 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+108−5
@@ -21,6 +21,7 @@
2121module Reflex.Requester.Base
2222 ( RequesterT (..)
2323 , runRequesterT
24+ , withRequesterT
2425 , runWithReplaceRequesterTWith
2526 , traverseIntMapWithKeyWithAdjustRequesterTWith
2627 , traverseDMapWithKeyWithAdjustRequesterTWith
@@ -30,6 +31,10 @@ module Reflex.Requester.Base
3031 , forRequesterData
3132 , requesterDataToList
3233 , singletonRequesterData
34+ , matchResponsesWithRequests
35+ , multiEntry
36+ , unMultiEntry
37+ , requesting'
3338 ) where
3439
3540import Reflex.Class
@@ -44,6 +49,7 @@ import Reflex.TriggerEvent.Class
4449import Control.Applicative (liftA2)
4550import Control.Monad.Exception
4651import Control.Monad.Identity
52+import Control.Monad.Primitive
4753import Control.Monad.Reader
4854import Control.Monad.Ref
4955import Control.Monad.State.Strict
@@ -60,8 +66,7 @@ import qualified Data.Map as Map
6066import Data.Monoid ((<>))
6167import Data.Proxy
6268import qualified Data.Semigroup as S
63−import Data.Some (Some)
64−import qualified Data.Some as Some
69+import Data.Some (Some(Some))
6570import Data.Type.Equality
6671import Data.Unique.Tag
6772
@@ -244,6 +249,10 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
244249deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
245250deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
246251
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+
247256-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
248257instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
249258 mempty = pure mempty
@@ -257,7 +266,6 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response
257266-- requests are made, and responses should be provided in the input 'Event'.
258267-- The 'Tag' keys will be used to return the responses to the same place the
259268-- requests were issued.
260−
261269runRequesterT :: (Reflex t, Monad m)
262270 => RequesterT t request response m a
263271 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
@@ -267,6 +275,20 @@ runRequesterT (RequesterT a) responses = do
267275 coerceEvent responses
268276 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
269277
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+
270292instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
271293 type Request (RequesterT t request response m) = request
272294 type Response (RequesterT t request response m) = response
@@ -370,7 +392,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
370392 pack = Entry
371393 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
372394 f' k (n, v) = do
373− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
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?
374396 return (fmapCheap (IntMap.singleton n) myRequests, result)
375397 ndm' <- numberOccurrencesFrom 1 dm'
376398 (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable
@@ -418,7 +440,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
418440 pack = Entry
419441 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
420442 f' k (Compose (n, v)) = do
421− (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))
422444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
423445 ndm' <- numberOccurrencesFrom 1 dm'
424446 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
@@ -432,3 +454,84 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
432454 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
433455 requests <- holdIncremental requests0 requests'
434456 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+264−201
@@ -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,27 @@ 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+#if defined(MIN_VERSION_semialign)
76+#if MIN_VERSION_these(0,8,0)
77+import Data.These.Combinators (justThese)
78+#endif
79+#endif
80+
6681#ifdef DEBUG_CYCLES
6782import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
6883import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -74,9 +89,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
7489import Data.FastWeakBag (FastWeakBag)
7590import qualified Data.FastWeakBag as FastWeakBag
7691import Data.Reflection
77−import Data.Some (Some)
78−import qualified Data.Some as Some
92+import Data.Some (Some(Some))
7993import Data.Type.Coercion
94+import Data.Profunctor.Unsafe ((#.), (.#))
8095import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
8196import qualified Data.WeakBag as WeakBag
8297import qualified Reflex.Class
@@ -193,12 +208,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193208
194209newNodeId :: IO Int
195210newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202211#endif
203212
204213--------------------------------------------------------------------------------
@@ -302,9 +311,10 @@ cacheEvent e =
302311#else
303312 Event $
304313#endif
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
314+ unsafePerformIO $ do
315+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
316+ <- newIORef emptyFastWeak
317+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308318#ifdef DEBUG_TRACE_EVENTS
309319 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310320#endif
@@ -370,7 +380,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370380eventNever :: Event x a
371381eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372382
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
383+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
374384eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375385
376386eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +432,14 @@ newSubscriberHold h = return $ Subscriber
422432 , subscriberRecalculateHeight = \_ -> return ()
423433 }
424434
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
435+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
426436newSubscriberFan subscribed = return $ Subscriber
427437 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428438 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429439 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430440 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431441 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
442+ let f _ (Pair v subsubs) = do
433443 propagate v $ _fanSubscribedChildren_list subsubs
434444 return $ Constant ()
435445 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -545,6 +555,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
545555toAny :: a -> Any
546556toAny = unsafeCoerce
547557
558+-- Why do we use Any here, instead of just giving eventSubscribedRetained an
559+-- existential type? Sadly, GHC does not currently know how to unbox types
560+-- with existentially quantified fields. So instead we just coerce values
561+-- to type Any on the way in. Since we never coerce them back, this is
562+-- perfectly safe.
548563data EventSubscribed x = EventSubscribed
549564 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550565 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -577,12 +592,12 @@ eventSubscribedNever = EventSubscribed
577592#endif
578593 }
579594
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
595+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581596eventSubscribedFan !subscribed = EventSubscribed
582597 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583598 , eventSubscribedRetained = toAny subscribed
584599#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
600+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586601 , eventSubscribedHasOwnHeightRef = False
587602 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588603#endif
@@ -595,7 +610,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595610#ifdef DEBUG_CYCLES
596611 , eventSubscribedGetParents = do
597612 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
613+ return [Some $ _eventSubscription_subscribed s]
599614 , eventSubscribedHasOwnHeightRef = True
600615 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601616#endif
@@ -608,8 +623,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608623#ifdef DEBUG_CYCLES
609624 , eventSubscribedGetParents = do
610625 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
626+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
627+ innerParents = maybeToList $ fmap Some innerSubscription
613628 return $ outerParent : innerParents
614629 , eventSubscribedHasOwnHeightRef = True
615630 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +640,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625640
626641walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627642walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
643+ subscribers <- flip execStateT mempty $ ($ Some s0) $ fix $ \loop (Some s) -> do
629644 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630645 when (h == invalidHeight) $ do
631646 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
647+ modify (Some s :)
633648 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
649+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635650 return subscribers
636651#endif
637652
@@ -659,7 +674,7 @@ behaviorPull !p = Behavior $ do
659674 val <- liftIO $ readIORef $ pullValue p
660675 case val of
661676 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
677+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663678 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664679 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665680 return $ pullSubscribedValue subscribed
@@ -668,7 +683,7 @@ behaviorPull !p = Behavior $ do
668683 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669684 parentsRef <- liftIO $ newIORef []
670685 holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
686+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672687 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673688 parents <- liftIO $ readIORef parentsRef
674689 let subscribed = PullSubscribed
@@ -678,7 +693,7 @@ behaviorPull !p = Behavior $ do
678693 , pullSubscribedParents = parents
679694 }
680695 liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
696+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
682697 return a
683698
684699behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +704,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689704readHoldTracked h = do
690705 result <- liftIO $ readIORef $ holdValue h
691706 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
707+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
693708 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694709 return result
695710
@@ -703,42 +718,46 @@ readBehaviorUntracked b = do
703718-- Dynamic
704719--------------------------------------------------------------------------------
705720
706−data Dynamic x p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
708− , dynamicUpdated :: !(Event x p)
721+type DynamicS x p = Dynamic x (PatchTarget p) p
722+
723+data Dynamic x target p = Dynamic
724+ { dynamicCurrent :: !(Behavior x target)
725+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709726 }
710727
711−dynamicHold :: Hold x p -> Dynamic x p
728+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
729+
730+
731+
732+
733+dynamicHold :: Hold x p -> DynamicS x p
712734dynamicHold !h = Dynamic
713735 { dynamicCurrent = behaviorHold h
714736 , dynamicUpdated = eventHold h
715737 }
716738
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
739+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
718740dynamicHoldIdentity = dynamicHold
719741
720−dynamicConst :: PatchTarget p -> Dynamic x p
742+dynamicConst :: PatchTarget p -> DynamicS x p
721743dynamicConst !a = Dynamic
722744 { dynamicCurrent = behaviorConst a
723745 , dynamicUpdated = eventNever
724746 }
725747
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
748+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
727749dynamicDyn !d = Dynamic
728750 { dynamicCurrent = behaviorDyn d
729751 , dynamicUpdated = eventDyn d
730752 }
731753
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
754+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
733755dynamicDynIdentity = dynamicDyn
734756
735757--------------------------------------------------------------------------------
736758-- Combinators
737759--------------------------------------------------------------------------------
738760
739−--TODO: Figure out why certain things are not 'representational', then make them
740−--representational so we can use coerce
741−
742761--type role Hold representational
743762data Hold x p
744763 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -759,20 +778,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759778
760779-- | Stores all global data relevant to a particular Spider timeline; only one
761780-- value should exist for each type @x@
762−data SpiderTimelineEnv x = SpiderTimelineEnv
781+newtype SpiderTimelineEnv x = STE {unSTE :: SpiderTimelineEnv' x}
782+-- We implement SpiderTimelineEnv with a newtype wrapper so
783+-- we can get the coercions we want safely.
784+type role SpiderTimelineEnv nominal
785+
786+data SpiderTimelineEnv' x = SpiderTimelineEnv
763787 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764788 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765789#ifdef DEBUG
766790 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767791#endif
768792 }
769−type role SpiderTimelineEnv nominal
793+type role SpiderTimelineEnv' phantom
770794
771795instance Eq (SpiderTimelineEnv x) where
772796 _ == _ = True -- Since only one exists of each type
773797
774798instance GEq SpiderTimelineEnv where
775− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
799+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
776800 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777801 else Nothing
778802
@@ -782,9 +806,9 @@ data EventEnv x
782806 , eventEnvDynInits :: !(IORef [SomeDynInit x])
783807 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784808 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
809+ , eventEnvClears :: !(IORef [Some Clear]) -- Needed for Subscribe
810+ , eventEnvIntClears :: !(IORef [Some IntClear])
811+ , eventEnvRootClears :: !(IORef [Some RootClear])
788812 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789813 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790814 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
@@ -795,7 +819,7 @@ runEventM :: EventM x a -> IO a
795819runEventM = unEventM
796820
797821asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
798−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
822+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
799823
800824class MonadIO m => Defer a m where
801825 getDeferralQueue :: m (IORef [a])
@@ -856,29 +880,29 @@ putCurrentHeight h = do
856880 heightRef <- asksEventEnv eventEnvCurrentHeight
857881 liftIO $ writeIORef heightRef $! h
858882
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
883+instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
860884 {-# INLINE getDeferralQueue #-}
861885 getDeferralQueue = asksEventEnv eventEnvClears
862886
863887{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
888+scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
889+scheduleClear r = defer $ Some $ Clear r
866890
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
891+instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868892 {-# INLINE getDeferralQueue #-}
869893 getDeferralQueue = asksEventEnv eventEnvIntClears
870894
871895{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
896+scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
897+scheduleIntClear r = defer $ Some $ IntClear r
874898
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
899+instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876900 {-# INLINE getDeferralQueue #-}
877901 getDeferralQueue = asksEventEnv eventEnvRootClears
878902
879903{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
904+scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
905+scheduleRootClear r = defer $ Some $ RootClear r
882906
883907instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884908 {-# INLINE getDeferralQueue #-}
@@ -933,9 +957,9 @@ getHoldEventSubscription h = do
933957
934958type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935959
936−--type role BehaviorM representational
937960-- BehaviorM can sample behaviors
938−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
961+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderIO (BehaviorEnv x) a }
962+ deriving (Functor, Applicative, MonadIO, MonadFix, MonadReader (BehaviorEnv x))
939963
940964instance Monad (BehaviorM x) where
941965 {-# INLINE (>>=) #-}
@@ -951,7 +975,7 @@ data BehaviorSubscribed x a
951975 = forall p. BehaviorSubscribedHold (Hold x p)
952976 | BehaviorSubscribedPull (PullSubscribed x a)
953977
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
978+newtype SomeBehaviorSubscribed x = SomeBehaviorSubscribed (Some (BehaviorSubscribed x))
955979
956980--type role PullSubscribed representational
957981data PullSubscribed x a
@@ -986,7 +1010,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
9861010#endif
9871011 }
9881012
989−data Root x (k :: * -> *)
1013+data Root x k
9901014 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
9911015 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
9921016 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1080,25 @@ heightBagVerify b@(HeightBag s c) = if
10561080heightBagVerify = id
10571081#endif
10581082
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)))
1083+data FanSubscribedChildren x k v a = FanSubscribedChildren
1084+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1085+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1086+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
10631087 }
10641088
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
1089+data FanSubscribed x k v
1090+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1091+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1092+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
10691093 , fanSubscribedParent :: !(EventSubscription x)
10701094#ifdef DEBUG_NODEIDS
10711095 , fanSubscribedNodeId :: Int
10721096#endif
10731097 }
10741098
1075−data Fan x k
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1099+data Fan x k v
1100+ = Fan { fanParent :: !(Event x (DMap k v))
1101+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
10781102 }
10791103
10801104data SwitchSubscribed x a
@@ -1128,24 +1152,32 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
11281152newInvalidatorPull :: Pull x a -> IO (Invalidator x)
11291153newInvalidatorPull p = return $! InvalidatorPull p
11301154
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1132− fmapMaybe f = push $ return . f
1155+instance HasSpiderTimeline x => Filterable (Event x) where
1156+ mapMaybe f = push $ return . f
11331157
11341158instance HasSpiderTimeline x => Align (Event x) where
11351159 nil = eventNever
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1160+#if MIN_VERSION_these(0, 8, 0)
1161+instance HasSpiderTimeline x => Semialign (Event x) where
1162+#endif
1163+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1164+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1165+
1166+#if defined(MIN_VERSION_semialign)
1167+ zip x y = mapMaybe justThese $ align x y
1168+#endif
11371169
11381170data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11391171 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11401172 | HoldDyn !(Hold x p)
11411173
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1174+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11431175
1144−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1176+newMapDyn :: HasSpiderTimeline x => (a -> b) -> DynamicS x (Identity a) -> DynamicS x (Identity b)
11451177newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
11461178
11471179--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)
1180+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> DynamicS x (Identity a) -> DynamicS x (Identity b) -> DynamicS x (Identity c)
11491181zipDynWith f da db =
11501182 let eab = align (dynamicUpdated da) (dynamicUpdated db)
11511183 ec = flip push eab $ \o -> do
@@ -1168,18 +1200,12 @@ buildDynamic readV0 v' = do
11681200 return d
11691201
11701202unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1172− where x = (readV0, v')
1203+unsafeBuildDynamic readV0 v' =
1204+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11731205
11741206-- ResultM can read behaviors and events
11751207type ResultM = EventM
11761208
1177−{-# NOINLINE unsafeNewIORef #-}
1178−unsafeNewIORef :: a -> b -> IORef b
1179−unsafeNewIORef a b = unsafePerformIO $ do
1180− touch a
1181− newIORef b
1182−
11831209instance HasSpiderTimeline x => Functor (Event x) where
11841210 fmap f = push $ return . Just . f
11851211
@@ -1192,33 +1218,42 @@ push f e = cacheEvent (pushCheap f e)
11921218
11931219{-# INLINABLE pull #-}
11941220pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1196− { pullCompute = a
1197− , pullValue = unsafeNewIORef a Nothing
1221+pull a = unsafePerformIO $ do
1222+ ref <- newIORef Nothing
11981223#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1224+ nid <- newNodeId
12001225#endif
1201− }
1226+ pure $ behaviorPull $ Pull
1227+ { pullCompute = a
1228+ , pullValue = ref
1229+#ifdef DEBUG_NODEIDS
1230+ , pullNodeId = nid
1231+#endif
1232+ }
12021233
12031234{-# INLINABLE switch #-}
12041235switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1205−switch a = eventSwitch $ Switch
1206− { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1208− }
1236+switch a = unsafePerformIO $ do
1237+ ref <- newIORef Nothing
1238+ pure $ eventSwitch $ Switch
1239+ { switchParent = a
1240+ , switchSubscribed = ref
1241+ }
12091242
12101243coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1211−coincidence a = eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1214− }
1244+coincidence a = unsafePerformIO $ do
1245+ ref <- newIORef Nothing
1246+ pure $ eventCoincidence $ Coincidence
1247+ { coincidenceParent = a
1248+ , coincidenceSubscribed = ref
1249+ }
12151250
12161251-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12171252run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
12181253run roots after = do
12191254 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
12201255 let t = spiderTimeline :: SpiderTimelineEnv x
1221− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1256+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
12221257 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
12231258 occBefore <- liftIO $ do
12241259 occBefore <- readIORef occRef
@@ -1256,11 +1291,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
12561291 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
12571292 EQ -> a
12581293
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1294+newtype Clear a = Clear (IORef (Maybe a))
12601295
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1296+newtype IntClear a = IntClear (IORef (IntMap a))
12621297
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1298+newtype RootClear k = RootClear (IORef (DMap k Identity))
12641299
12651300data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
12661301
@@ -1282,9 +1317,9 @@ type WeakList a = [Weak a]
12821317withIncreasedDepth :: CanTrace x m => m a -> m a
12831318withIncreasedDepth a = do
12841319 spiderTimeline <- askSpiderTimelineEnv
1285− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1320+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
12861321 result <- a
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1322+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
12881323 return result
12891324#else
12901325withIncreasedDepth :: m a -> m a
@@ -1311,7 +1346,7 @@ traceMWhen _ b getMessage = when b $ do
13111346 message <- getMessage
13121347#ifdef DEBUG
13131348 spiderTimeline <- askSpiderTimelineEnv
1314− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1349+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
13151350#else
13161351 let d = 0
13171352#endif
@@ -1352,25 +1387,25 @@ propagateSubscriberHold h a = do
13521387data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
13531388
13541389runBehaviorM :: 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)
1390+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
13561391
13571392askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
13581393askInvalidator = do
1359− (!m, _) <- BehaviorM ask
1394+ (!m, _) <- ask
13601395 case m of
13611396 Nothing -> return Nothing
13621397 Just (!wi, _) -> return $ Just wi
13631398
13641399askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
13651400askParentsRef = do
1366− (!m, _) <- BehaviorM ask
1401+ (!m, _) <- ask
13671402 case m of
13681403 Nothing -> return Nothing
13691404 Just (_, !p) -> return $ Just p
13701405
13711406askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
13721407askBehaviorHoldInits = do
1373− (_, !his) <- BehaviorM ask
1408+ (_, !his) <- ask
13741409 return his
13751410
13761411{-# INLINE getDynHold #-}
@@ -1415,6 +1450,9 @@ getRootSubscribed k r sub = do
14151450 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14161451 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14171452 writeIORef uninitRef $! uninit
1453+#ifdef DEBUG_NODEIDS
1454+ nid <- newNodeId
1455+#endif
14181456 let !subscribed = RootSubscribed
14191457 { rootSubscribedKey = k
14201458 , rootSubscribedCachedSubscribed = cached
@@ -1423,7 +1461,7 @@ getRootSubscribed k r sub = do
14231461 , rootSubscribedUninit = uninit
14241462 , rootSubscribedWeakSelf = weakSelf
14251463#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1464+ , rootSubscribedNodeId = nid
14271465#endif
14281466 }
14291467 -- If we die at the same moment that all our children die, they will
@@ -1472,16 +1510,10 @@ newFanInt = do
14721510 , _fanInt_occRef = occRef
14731511 }
14741512
1475−{-# NOINLINE unsafeNewFanInt #-}
1476−unsafeNewFanInt :: b -> FanInt x a
1477−unsafeNewFanInt b = unsafePerformIO $ do
1478− touch b
1479− newFanInt
1480−
14811513fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1483− let self = unsafeNewFanInt p
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1514+fanInt p = unsafePerformIO $ do
1515+ self <- newFanInt
1516+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14851517 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14861518 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14871519 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1517,7 +1549,7 @@ fanInt p =
15171549 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15181550
15191551{-# INLINABLE getFanSubscribed #-}
1520−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1552+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))
15211553getFanSubscribed k f sub = do
15221554 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15231555 case mSubscribed of
@@ -1535,13 +1567,16 @@ getFanSubscribed k f sub = do
15351567 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15361568 occRef <- liftIO $ newIORef parentOcc
15371569 when (isJust parentOcc) $ scheduleClear occRef
1570+#ifdef DEBUG_NODEIDS
1571+ nid <- liftIO newNodeId
1572+#endif
15381573 let subscribed = FanSubscribed
15391574 { fanSubscribedCachedSubscribed = fanSubscribed f
15401575 , fanSubscribedOccurrence = occRef
15411576 , fanSubscribedParent = subscription
15421577 , fanSubscribedSubscribers = subscribersRef
15431578#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1579+ , fanSubscribedNodeId = nid
15451580#endif
15461581 }
15471582 let !self = (k, subscribed)
@@ -1551,7 +1586,7 @@ getFanSubscribed k f sub = do
15511586 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15521587 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15531588
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1589+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15551590cleanupFanSubscribed (k, subscribed) = do
15561591 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15571592 let reducedSubscribers = DMap.delete k subscribers
@@ -1563,7 +1598,7 @@ cleanupFanSubscribed (k, subscribed) = do
15631598 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15641599
15651600{-# INLINE subscribeFanSubscribed #-}
1566−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1601+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
15671602subscribeFanSubscribed k subscribed sub = do
15681603 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15691604 case DMap.lookup k subscribers of
@@ -1601,6 +1636,9 @@ getSwitchSubscribed s sub = do
16011636 when (isJust parentOcc) $ scheduleClear occRef
16021637 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16031638 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1639+#ifdef DEBUG_NODEIDS
1640+ nid <- liftIO newNodeId
1641+#endif
16041642 let !subscribed = SwitchSubscribed
16051643 { switchSubscribedCachedSubscribed = switchSubscribed s
16061644 , switchSubscribedOccurrence = occRef
@@ -1613,7 +1651,7 @@ getSwitchSubscribed s sub = do
16131651 , switchSubscribedCurrentParent = subscriptionRef
16141652 , switchSubscribedWeakSelf = weakSelf
16151653#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1654+ , switchSubscribedNodeId = nid
16171655#endif
16181656 }
16191657 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1658,6 +1696,9 @@ getCoincidenceSubscribed c sub = do
16581696 scheduleClear innerSubdRef
16591697 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16601698 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1699+#ifdef DEBUG_NODEIDS
1700+ nid <- liftIO newNodeId
1701+#endif
16611702 let subscribed = CoincidenceSubscribed
16621703 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16631704 , coincidenceSubscribedOccurrence = occRef
@@ -1668,7 +1709,7 @@ getCoincidenceSubscribed c sub = do
16681709 , coincidenceSubscribedInnerParent = innerSubdRef
16691710 , coincidenceSubscribedWeakSelf = weakSelf
16701711#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1712+ , coincidenceSubscribedNodeId = nid
16721713#endif
16731714 }
16741715 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1685,26 +1726,34 @@ cleanupCoincidenceSubscribed subscribed = do
16851726subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16861727subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16871728
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)
1729+{-# INLINE mergeG #-}
1730+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1731+ => (forall a. q a -> Event x (v a))
1732+ -> DynamicS x (PatchDMap k q) -> Event x (DMap k v)
1733+mergeG nt d = cacheEvent (mergeCheap nt d)
16911734
16921735{-# 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)
1736+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1737+ => (forall a. q a -> Event x (v a))
1738+ -> DynamicS x (PatchDMapWithMove k q) -> Event x (DMap k v)
1739+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
16951740
16961741{-# 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
1742+mergeCheap
1743+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1744+ => (forall a. q a -> Event x (v a))
1745+ -> DynamicS x (PatchDMap k q)
1746+ -> Event x (DMap k v)
1747+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
16991748 where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1749+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17011750 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17021751 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17031752 (mOldSubd, newPs) <- case me of
17041753 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17051754 Just e -> do
17061755 let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1756+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17081757 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17091758 let newParent = MergeSubscribedParent subscription
17101759 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1714,28 +1763,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17141763 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17151764 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17161765 foldM f ([], oldParents) $ DMap.toList p
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1766+
1767+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17181768 getInitialSubscribers initialParents subscriber = do
17191769 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17201770 let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1771+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17221772 height <- liftIO $ getEventSubscribedHeight parentSubd
1723− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1773+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
17241774 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17251775 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17261776 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17271777 )
1778+
17281779 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17291780 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17301781
17311782{-# 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
1783+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1784+ => (forall a. q a -> Event x (v a))
1785+ -> DynamicS x (PatchDMapWithMove k q)
1786+ -> Event x (DMap k v)
1787+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
17341788 where
1735− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1789+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17361790 updateMe subscriber heightBagRef oldParents p = do
17371791 -- Prepare new parents for insertion
1738− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1792+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
17391793 subscribeParent k e = do
17401794 keyRef <- liftIO $ newIORef k
17411795 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1744,9 +1798,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17441798 newParentHeight <- getEventSubscribedHeight subd
17451799 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17461800 return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1801+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17481802 -- 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
1803+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
17501804 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17511805 Nothing -> do
17521806 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1757,46 +1811,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17571811 return Nothing
17581812 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17591813 return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1814+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17611815 getInitialSubscribers initialParents subscriber = do
17621816 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17631817 keyRef <- liftIO $ newIORef k
17641818 let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1819+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17661820 height <- liftIO $ getEventSubscribedHeight parentSubd
1767− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1821+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17681822 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17691823 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17701824 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17711825 )
1826+
17721827 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17731828 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17741829
1775−type MergeUpdateFunc k x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1830+type MergeUpdateFunc k v x p s
1831+ = (forall a. EventM x (k a) -> Subscriber x (v a))
17771832 -> IORef HeightBag
17781833 -> DMap k s
17791834 -> p
17801835 -> EventM x ([EventSubscription x], DMap k s)
17811836
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)
1837+type MergeInitFunc k v q x s
1838+ = DMap k q
1839+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1840+ -> EventM x (DMap k v, [Height], DMap k s)
17861841
17871842type MergeDestroyFunc k s
17881843 = DMap k s
17891844 -> IO ()
17901845
1791−data Merge x k s = Merge
1846+data Merge x k v s = Merge
17921847 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17931848 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17941849 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1850+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1851+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
17971852 }
17981853
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1854+invalidateMergeHeight :: Merge x k v s -> IO ()
18001855invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18011856
18021857invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1806,8 +1861,7 @@ invalidateMergeHeight' heightRef sub = do
18061861 writeIORef heightRef $! invalidHeight
18071862 subscriberInvalidateHeight sub oldHeight
18081863
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1864+revalidateMergeHeight :: Merge x k v s -> IO ()
18111865revalidateMergeHeight m = do
18121866 currentHeight <- readIORef $ _merge_heightRef m
18131867 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1823,19 +1877,19 @@ revalidateMergeHeight m = do
18231877 subscriberRecalculateHeight (_merge_sub m) height
18241878 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18251879
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1880+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18271881scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18281882 vals <- liftIO $ readIORef $ _merge_accumRef m
18291883 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
18301884 --TODO: Assert that m is not empty
18311885 subscriberPropagate (_merge_sub m) vals
18321886
1833−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1887+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
18341888mergeSubscriber m getKey = Subscriber
18351889 { subscriberPropagate = \a -> do
18361890 oldM <- liftIO $ readIORef $ _merge_accumRef m
18371891 k <- getKey
1838− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1892+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
18391893 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18401894 liftIO $ writeIORef (_merge_accumRef m) $! newM
18411895 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1850,7 +1904,7 @@ mergeSubscriber m getKey = Subscriber
18501904 else liftIO $ do
18511905#ifdef DEBUG_CYCLES
18521906 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1907+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
18541908 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
18551909#else
18561910 let cycleInfo = ""
@@ -1866,7 +1920,7 @@ mergeSubscriber m getKey = Subscriber
18661920 }
18671921
18681922--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
1923+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
18701924updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18711925 where updateMe = do
18721926 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1874,9 +1928,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18741928 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18751929 return subscriptionsToKill
18761930
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
1931+{-# INLINE mergeGCheap' #-}
1932+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
1933+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1934+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
18801935 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18811936 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18821937 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1924,11 +1979,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
19241979 , occ
19251980 )
19261981
1927−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1982+mergeInt :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19281983mergeInt = cacheEvent . mergeIntCheap
19291984
19301985{-# INLINABLE mergeIntCheap #-}
1931−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1986+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19321987mergeIntCheap d = Event $ \sub -> do
19331988 initialParents <- readBehaviorUntracked $ dynamicCurrent d
19341989 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2025,14 +2080,16 @@ mergeIntCheap d = Event $ \sub -> do
20252080 )
20262081
20272082newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2083+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
20282084
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2030−fan e =
2085+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2086+fanG e = unsafePerformIO $ do
2087+ ref <- newIORef Nothing
20312088 let f = Fan
20322089 { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2090+ , fanSubscribed = ref
20342091 }
2035− in EventSelector $ \k -> eventFan k f
2092+ pure $ EventSelectorG $ \k -> eventFan k f
20362093
20372094runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20382095runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2086,18 +2143,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
20862143-- | Run an event action outside of a frame
20872144runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
20882145runFrame a = SpiderHost $ do
2089− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2146+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
20902147 let go = do
20912148 result <- a
20922149 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
20932150 return result
20942151 result <- runEventM go
20952152 toClear <- readIORef $ eventEnvClears env
2096− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2153+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
20972154 toClearInt <- readIORef $ eventEnvIntClears env
2098− forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2155+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
20992156 toClearRoot <- readIORef $ eventEnvRootClears env
2100− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2157+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
21012158 toAssign <- readIORef $ eventEnvAssignments env
21022159 toReconnectRef <- newIORef []
21032160 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2297,7 +2354,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
22972354 fail _ = error "Dynamic does not support 'fail'"
22982355
22992356{-# 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)
2357+newJoinDyn :: HasSpiderTimeline x => DynamicS x (Identity (DynamicS x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
23012358newJoinDyn d =
23022359 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
23032360 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2447,7 +2504,7 @@ unsafeNewSpiderTimelineEnv = do
24472504#ifdef DEBUG
24482505 depthRef <- newIORef 0
24492506#endif
2450− return $ SpiderTimelineEnv
2507+ return $ STE $ SpiderTimelineEnv
24512508 { _spiderTimeline_lock = lock
24522509 , _spiderTimeline_eventEnv = env
24532510#ifdef DEBUG
@@ -2457,19 +2514,19 @@ unsafeNewSpiderTimelineEnv = do
24572514
24582515-- | Create a new SpiderTimelineEnv
24592516newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2460−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2517+newSpiderTimeline = withSpiderTimeline (pure . Some)
24612518
2462−data LocalSpiderTimeline x s
2519+data LocalSpiderTimeline (x :: Type) s
24632520
24642521instance Reifies s (SpiderTimelineEnv x) =>
24652522 HasSpiderTimeline (LocalSpiderTimeline x s) where
2466− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2523+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
24672524
24682525localSpiderTimeline
2469− :: Proxy s
2526+ :: proxy s
24702527 -> SpiderTimelineEnv x
24712528 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2472−localSpiderTimeline _ = unsafeCoerce
2529+localSpiderTimeline _ = coerce
24732530
24742531-- | Pass a new timeline to the given function.
24752532withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2477,18 +2534,18 @@ withSpiderTimeline k = do
24772534 env <- unsafeNewSpiderTimelineEnv
24782535 reify env $ \s -> k $ localSpiderTimeline s env
24792536
2480−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2537+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24812538
24822539type ComputeM = EventM
24832540
2484−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2541+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24852542
24862543instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24872544 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
24882545 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
24892546 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2490− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2491− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2547+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2548+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
24922549 type PullM (SpiderTimeline x) = SpiderPullM x
24932550 type PushM (SpiderTimeline x) = SpiderPushM x
24942551 {-# INLINABLE never #-}
@@ -2501,10 +2558,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25012558 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
25022559 {-# INLINABLE pull #-}
25032560 pull = SpiderBehavior . pull . coerce
2504− {-# INLINABLE merge #-}
2505− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2506− {-# INLINABLE fan #-}
2507− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2561+ {-# INLINABLE fanG #-}
2562+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2563+ {-# INLINABLE mergeG #-}
2564+ mergeG
2565+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2566+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2567+ -> DMap k q
2568+ -> R.Event (SpiderTimeline x) (DMap k v)
2569+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
25082570 {-# INLINABLE switch #-}
25092571 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25102572 {-# INLINABLE coincidence #-}
@@ -2512,15 +2574,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25122574 {-# INLINABLE current #-}
25132575 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
25142576 {-# INLINABLE updated #-}
2515− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2577+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
25162578 {-# INLINABLE unsafeBuildDynamic #-}
25172579 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25182580 {-# INLINABLE unsafeBuildIncremental #-}
25192581 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2520− {-# INLINABLE mergeIncremental #-}
2521− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2522− {-# INLINABLE mergeIncrementalWithMove #-}
2523− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2582+ {-# INLINABLE mergeIncrementalG #-}
2583+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2584+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2585+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
25242586 {-# INLINABLE currentIncremental #-}
25252587 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25262588 {-# INLINABLE updatedIncremental #-}
@@ -2531,9 +2593,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25312593 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
25322594 eventCoercion Coercion = Coercion
25332595 behaviorCoercion Coercion = Coercion
2534− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2596+ dynamicCoercion Coercion = Coercion
2597+ incrementalCoercion Coercion Coercion = Coercion
25352598 {-# INLINABLE mergeIntIncremental #-}
2536− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2599+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
25372600 {-# INLINABLE fanInt #-}
25382601 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
25392602
@@ -2558,7 +2621,7 @@ instance MonadAtomicRef (EventM x) where
25582621 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25592622
25602623-- | The monad for actions that manipulate a Spider timeline identified by @x@
2561−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2624+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25622625
25632626instance Monad (SpiderHost x) where
25642627 {-# INLINABLE (>>=) #-}
@@ -2580,7 +2643,7 @@ runSpiderHost (SpiderHost a) = a
25802643runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25812644runSpiderHostForTimeline (SpiderHost a) _ = a
25822645
2583−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2646+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25842647 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25852648
25862649instance 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+24−4
@@ -1,3 +1,5 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
13{-# LANGUAGE FlexibleContexts #-}
24{-# LANGUAGE GADTs #-}
35{-# LANGUAGE RecursiveDo #-}
@@ -12,6 +14,10 @@ import Data.Functor.Misc
1214import qualified Data.Map as M
1315import Data.These
1416
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+
1521import Reflex
1622import Reflex.EventWriter.Base
1723import Test.Run
@@ -36,6 +42,9 @@ main = do
3642 print os4
3743 os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
3844 print os5
45+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
46+ print os6
47+ let ![[Nothing, Nothing]] = os6
3948 return ()
4049
4150unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
@@ -44,16 +53,13 @@ unwrapApp x appIn = do
4453 return e
4554
4655testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
47−testOrdering pulse = do
48− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
49− return ()
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
5057
5158testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
5259testSimultaneous pulse = do
5360 let e0 = fmapMaybe (^? here) pulse
5461 e1 = fmapMaybe (^? there) pulse
5562 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
56− return ()
5763
5864-- | Test that a widget telling and event which fires at the same time it has been replaced
5965-- doesn't count along with the new widget.
@@ -112,3 +118,17 @@ testLiveTellEventDMap pulse = do
112118 (mapToDMap $ M.singleton 1 ())
113119 ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
114120 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+4
@@ -19,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
1919import Data.Semigroup
2020import Data.These
2121
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+
2226import Reflex
2327import Reflex.Patch.MapWithMove
2428import Test.Run
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/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