on develop help sign in
Merge branch 'develop' into dn-add-test-folddyn
7ae1e23Oliver Batchelor
.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
57[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
57[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
58[ ] leftmost :: [Event a] -> Event a
58[ ] leftmost :: [Event a] -> Event a
59[ ] mergeList :: [Event a] -> Event (NonEmpty a)
59[ ] 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)
61[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
61[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
62
62
63-- Efficient one-to-many fanout
63-- Efficient one-to-many fanout
64−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
64+[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
65+[ ] fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
66−[ ] select :: EventSelector k -> k a -> Event a
66+[ ] select :: EventSelector k -> k a -> Event a
67−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
67+[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
68+[ ] fanThese :: Event (These a b) -> (Event a, Event b)
69
69
70-- Event to Event via function that can sample current values
70-- Event to Event via function that can sample current values
71[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
71[ ] 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
166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
166-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
167[ ] switch :: Behavior (Event a) -> Event a
167[ ] switch :: Behavior (Event a) -> Event a
168
168
169−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
169+-- Flatten Dynamic-of-Event to Event. New Event is used immediately.
170[ ] switchDyn :: Dynamic (Event a) -> Event a
170[ ] switchDyn :: Dynamic (Event a) -> Event a
171
171
172-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
172-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
README.md+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+
2### Practical Functional Reactive Programming
5### Practical Functional Reactive Programming
3
6
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.
5
8
6[Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
9[Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
7
10
8−Comprehensive documentation is still a work in progress, but a tutorial for Reflex and Reflex-DOM is available at https://github.com/reflex-frp/reflex-platform and an introductory talk given at the New York Haskell Meetup is available here: [Part 1](https://www.youtube.com/watch?v=mYvkcskJbc4) / [Part 2](https://www.youtube.com/watch?v=3qfc9XFVo2c).
9−
10A summary of Reflex functions is available in the [quick reference](Quickref.md).
11A summary of Reflex functions is available in the [quick reference](Quickref.md).
11
12
12−### Additional resources
13+**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
14+
15+### Resources
13[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
16[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
14
17
15[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
18[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
16
19
17−[hackage](https://hackage.haskell.org/package/reflex)
18−
19[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
20[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 @@
6, template-haskell , these, time, transformers
6, template-haskell , these, time, transformers
7, transformers-compat, unbounded-delays, prim-uniq
7, transformers-compat, unbounded-delays, prim-uniq
8, data-default, filepath, directory, filemanip, ghcjs-base
8, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers
9+, monoidal-containers, witherable, profunctors
10+, semialign ? null, splitThese ? (semialign != null)
10, useTemplateHaskell ? true
11, useTemplateHaskell ? true
11}:
12}:
12mkDerivation {
13mkDerivation {
13 pname = "reflex";
14 pname = "reflex";
14− version = "0.5";
15+ version = "0.6.2.2";
15 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16 src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16 libraryHaskellDepends = [
17 libraryHaskellDepends = [
17 base bifunctors containers dependent-map dependent-sum
18 base bifunctors containers dependent-map dependent-sum
@@ -21,18 +22,21 @@ mkDerivation {
21 transformers-compat prim-uniq
22 transformers-compat prim-uniq
22 base bifunctors containers deepseq dependent-map dependent-sum
23 base bifunctors containers deepseq dependent-map dependent-sum
23 mtl ref-tf split transformers data-default
24 mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
25+ random time unbounded-delays monoidal-containers witherable
26+ profunctors
25 ] ++ (if ghc.isGhcjs or false then [
27 ] ++ (if ghc.isGhcjs or false then [
26 ghcjs-base
28 ghcjs-base
27 ] else []) ++ (if !useTemplateHaskell then [] else [
29 ] else []) ++ (if !useTemplateHaskell then [] else [
28 haskell-src-exts haskell-src-meta
30 haskell-src-exts haskell-src-meta
29− ]);
31+ ]) ++ (if splitThese then [
32+ semialign
33+ ] else []);
30 testHaskellDepends = if ghc.isGhcjs or false then [] else [
34 testHaskellDepends = if ghc.isGhcjs or false then [] else [
31 hlint filepath directory filemanip
35 hlint filepath directory filemanip
32 ];
36 ];
33− configureFlags = if useTemplateHaskell then [] else [
37+ configureFlags =
34− "-f-use-template-haskell"
38+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
35− ];
39+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
36 homepage = "https://github.com/reflex-frp/reflex";
40 homepage = "https://github.com/reflex-frp/reflex";
37 description = "Higher-order Functional Reactive Programming";
41 description = "Higher-order Functional Reactive Programming";
38 license = stdenv.lib.licenses.bsd3;
42 license = stdenv.lib.licenses.bsd3;
reflex.cabal+69−33
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.5
2+Version: 0.6.2.2
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5License: BSD3
5License: BSD3
@@ -15,6 +15,11 @@ bug-reports: https://github.com/reflex-frp/reflex/issues
15extra-source-files:
15extra-source-files:
16 README.md
16 README.md
17 Quickref.md
17 Quickref.md
18+ ChangeLog.md
19+
20+tested-with:
21+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5,
22+ GHCJS ==8.4
18
23
19flag use-reflex-optimizer
24flag use-reflex-optimizer
20 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
25 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
36 default: False
41 default: False
37 manual: True
42 manual: True
38
43
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
39library
49library
40 hs-source-dirs: src
50 hs-source-dirs: src
41 build-depends:
51 build-depends:
42 MemoTrie == 0.6.*,
52 MemoTrie == 0.6.*,
43 base >= 4.9 && < 4.13,
53 base >= 4.9 && < 4.13,
44 bifunctors >= 5.2 && < 5.6,
54 bifunctors >= 5.2 && < 5.6,
45− comonad,
55+ comonad >= 5.0.4 && < 5.1,
56+ constraints-extras >= 0.3 && < 0.4,
46− containers >= 0.5 && < 0.7,
57+ containers >= 0.6 && < 0.7,
47 data-default >= 0.5 && < 0.8,
58 data-default >= 0.5 && < 0.8,
48− dependent-map >= 0.2.4 && < 0.3,
59+ dependent-map >= 0.3 && < 0.4,
49 exception-transformers == 0.4.*,
60 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
50 lens >= 4.7 && < 5,
62 lens >= 4.7 && < 5,
51 monad-control >= 1.0.1 && < 1.1,
63 monad-control >= 1.0.1 && < 1.1,
52− monoidal-containers == 0.4.*,
53 mtl >= 2.1 && < 2.3,
64 mtl >= 2.1 && < 2.3,
54 prim-uniq >= 0.1.0.1 && < 0.2,
65 prim-uniq >= 0.1.0.1 && < 0.2,
55− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
56 random == 1.1.*,
68 random == 1.1.*,
57 ref-tf == 0.4.*,
69 ref-tf == 0.4.*,
58 reflection == 2.1.*,
70 reflection == 2.1.*,
59 semigroupoids >= 4.0 && < 6,
71 semigroupoids >= 4.0 && < 6,
60− semigroups >= 0.16 && < 0.19,
61 stm >= 2.4 && < 2.6,
72 stm >= 2.4 && < 2.6,
62 syb >= 0.5 && < 0.8,
73 syb >= 0.5 && < 0.8,
63− these >= 0.4 && < 0.8,
64 time >= 1.4 && < 1.9,
74 time >= 1.4 && < 1.9,
65− transformers >= 0.2,
75+ transformers >= 0.5.6.0 && < 0.6,
66− transformers-compat >= 0.3,
67− unbounded-delays >= 0.1.0.0 && < 0.2
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
68
86
69 exposed-modules:
87 exposed-modules:
88+ Control.Monad.ReaderIO
70 Data.AppendMap,
89 Data.AppendMap,
71 Data.FastMutableIntMap,
90 Data.FastMutableIntMap,
72 Data.FastWeakBag,
91 Data.FastWeakBag,
@@ -131,8 +150,8 @@ library
131 if flag(use-template-haskell)
150 if flag(use-template-haskell)
132 cpp-options: -DUSE_TEMPLATE_HASKELL
151 cpp-options: -DUSE_TEMPLATE_HASKELL
133 build-depends:
152 build-depends:
134− dependent-sum >= 0.3 && < 0.5,
153+ dependent-sum >= 0.6 && < 0.7,
135− haskell-src-exts >= 1.16 && < 1.21,
154+ haskell-src-exts >= 1.16 && < 1.22,
136 haskell-src-meta >= 0.6 && < 0.9,
155 haskell-src-meta >= 0.6 && < 0.9,
137 template-haskell >= 2.9 && < 2.15
156 template-haskell >= 2.9 && < 2.15
138 exposed-modules:
157 exposed-modules:
@@ -140,7 +159,7 @@ library
140 other-extensions: TemplateHaskell
159 other-extensions: TemplateHaskell
141 else
160 else
142 build-depends:
161 build-depends:
143− dependent-sum == 0.4.*
162+ dependent-sum >= 0.6 && < 0.7
144
163
145 if flag(fast-weak) && impl(ghcjs)
164 if flag(fast-weak) && impl(ghcjs)
146 cpp-options: -DGHCJS_FAST_WEAK
165 cpp-options: -DGHCJS_FAST_WEAK
@@ -157,14 +176,14 @@ test-suite semantics
157 base,
176 base,
158 bifunctors,
177 bifunctors,
159 containers,
178 containers,
160− deepseq >= 1.3 && < 1.5,
179+ deepseq,
161 dependent-map,
180 dependent-map,
162 dependent-sum,
181 dependent-sum,
163 mtl,
182 mtl,
164 ref-tf,
183 ref-tf,
165 reflex,
184 reflex,
166 split,
185 split,
167− transformers >= 0.3
186+ transformers
168 other-modules:
187 other-modules:
169 Reflex.Bench.Focused
188 Reflex.Bench.Focused
170 Reflex.Plan.Pure
189 Reflex.Plan.Pure
@@ -183,7 +202,7 @@ test-suite CrossImpl
183 containers,
202 containers,
184 dependent-map,
203 dependent-map,
185 dependent-sum,
204 dependent-sum,
186− deepseq >= 1.3 && < 1.5,
205+ deepseq,
187 mtl,
206 mtl,
188 transformers,
207 transformers,
189 ref-tf,
208 ref-tf,
@@ -202,7 +221,7 @@ test-suite hlint
202 , directory
221 , directory
203 , filepath
222 , filepath
204 , filemanip
223 , filemanip
205− , hlint
224+ , hlint < 2.1 || >= 2.2.2
206 if impl(ghcjs)
225 if impl(ghcjs)
207 buildable: False
226 buildable: False
208
227
@@ -212,7 +231,7 @@ test-suite EventWriterT
212 hs-source-dirs: test
231 hs-source-dirs: test
213 build-depends: base
232 build-depends: base
214 , containers
233 , containers
215− , deepseq >= 1.3 && < 1.5
234+ , deepseq
216 , dependent-map
235 , dependent-map
217 , dependent-sum
236 , dependent-sum
218 , lens
237 , lens
@@ -221,6 +240,10 @@ test-suite EventWriterT
221 , transformers
240 , transformers
222 , reflex
241 , reflex
223 , ref-tf
242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
224 other-modules:
247 other-modules:
225 Reflex.Test
248 Reflex.Test
226 Reflex.TestPlan
249 Reflex.TestPlan
@@ -228,22 +251,30 @@ test-suite EventWriterT
228 Reflex.Plan.Pure
251 Reflex.Plan.Pure
229 Test.Run
252 Test.Run
230
253
254+
231test-suite RequesterT
255test-suite RequesterT
232 type: exitcode-stdio-1.0
256 type: exitcode-stdio-1.0
233 main-is: RequesterT.hs
257 main-is: RequesterT.hs
234 hs-source-dirs: test
258 hs-source-dirs: test
235 build-depends: base
259 build-depends: base
260+ , containers
261+ , deepseq
236 , dependent-sum
262 , dependent-sum
237 , dependent-map
263 , dependent-map
238 , lens
264 , lens
265+ , mtl
239 , these
266 , these
240 , transformers
267 , transformers
241 , reflex
268 , reflex
242 , ref-tf
269 , ref-tf
243− buildable: False
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
244 other-modules:
274 other-modules:
245 Reflex.TestPlan
275 Reflex.TestPlan
246 Reflex.Plan.Pure
276 Reflex.Plan.Pure
277+ Test.Run
247
278
248test-suite QueryT
279test-suite QueryT
249 type: exitcode-stdio-1.0
280 type: exitcode-stdio-1.0
@@ -253,15 +284,18 @@ test-suite QueryT
253 , containers
284 , containers
254 , dependent-map
285 , dependent-map
255 , dependent-sum
286 , dependent-sum
256− , deepseq >= 1.3 && < 1.5
287+ , deepseq
257 , lens
288 , lens
258 , monoidal-containers
289 , monoidal-containers
259 , mtl
290 , mtl
260 , ref-tf
291 , ref-tf
261 , reflex
292 , reflex
262− , semigroups
263 , these
293 , these
264 , transformers
294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
265 other-modules:
299 other-modules:
266 Test.Run
300 Test.Run
267 Reflex.TestPlan
301 Reflex.TestPlan
@@ -276,14 +310,16 @@ test-suite GC-Semantics
276 , containers
310 , containers
277 , dependent-sum
311 , dependent-sum
278 , dependent-map
312 , dependent-map
279− , deepseq >= 1.3 && < 1.5
313+ , deepseq
280 , mtl
314 , mtl
281 , these
315 , these
282 , transformers
316 , transformers
283 , reflex
317 , reflex
284 , ref-tf
318 , ref-tf
285− if impl(ghc < 8)
319+
320+ if flag(split-these)
286− build-depends: semigroups
321+ build-depends: semialign
322+
287 other-modules:
323 other-modules:
288 Reflex.Plan.Pure
324 Reflex.Plan.Pure
289 Reflex.Plan.Reflex
325 Reflex.Plan.Reflex
@@ -296,7 +332,7 @@ test-suite rootCleanup
296 hs-source-dirs: test
332 hs-source-dirs: test
297 build-depends: base
333 build-depends: base
298 , containers
334 , containers
299− , deepseq >= 1.3 && < 1.5
335+ , deepseq
300 , dependent-sum
336 , dependent-sum
301 , mtl
337 , mtl
302 , reflex
338 , reflex
@@ -315,8 +351,8 @@ benchmark spider-bench
315 build-depends:
351 build-depends:
316 base,
352 base,
317 containers,
353 containers,
318− criterion >= 1.1 && < 1.6,
354+ criterion,
319− deepseq >= 1.3 && < 1.5,
355+ deepseq,
320 dependent-map,
356 dependent-map,
321 dependent-sum,
357 dependent-sum,
322 ref-tf,
358 ref-tf,
@@ -325,7 +361,7 @@ benchmark spider-bench
325 reflex,
361 reflex,
326 split,
362 split,
327 stm,
363 stm,
328− transformers >= 0.3
364+ transformers
329 other-modules:
365 other-modules:
330 Reflex.TestPlan
366 Reflex.TestPlan
331 Reflex.Plan.Reflex
367 Reflex.Plan.Reflex
@@ -339,9 +375,9 @@ benchmark saulzar-bench
339 ghc-options: -Wall -O2 -rtsopts -threaded
375 ghc-options: -Wall -O2 -rtsopts -threaded
340 build-depends:
376 build-depends:
341 base,
377 base,
342− containers >= 0.5 && < 0.7,
378+ containers,
343− criterion >= 1.1 && < 1.6,
379+ criterion,
344− deepseq >= 1.3 && < 1.5,
380+ deepseq,
345 dependent-map,
381 dependent-map,
346 dependent-sum,
382 dependent-sum,
347 loch-th,
383 loch-th,
@@ -353,7 +389,7 @@ benchmark saulzar-bench
353 split,
389 split,
354 stm,
390 stm,
355 time,
391 time,
356− transformers >= 0.3
392+ transformers
357 other-modules:
393 other-modules:
358 Reflex.TestPlan
394 Reflex.TestPlan
359 Reflex.Plan.Reflex
395 Reflex.Plan.Reflex
release.nix+4−4
@@ -3,7 +3,7 @@
3let
3let
4 inherit (rp.nixpkgs) lib;
4 inherit (rp.nixpkgs) lib;
5 compilers = ["ghc8_4" "ghc8_0" "ghcjs8_4" "ghcjs8_0"];
5 compilers = ["ghc8_4" "ghc8_0" "ghcjs8_4" "ghcjs8_0"];
6−in lib.genAttrs compilers (ghc: {
6+in lib.attrValues (lib.genAttrs compilers (ghc: {
7− reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; };
7+ reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; splitThese = false; };
8− reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; };
8+ reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; splitThese = false; };
9−})
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 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveGeneric #-}
4−{-# LANGUAGE DeriveTraversable #-}
5{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE PatternSynonyms #-}
5{-# LANGUAGE PatternSynonyms #-}
10{-# LANGUAGE RoleAnnotations #-}
6{-# LANGUAGE RoleAnnotations #-}
11{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE StandaloneDeriving #-}
13{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
14{-# OPTIONS_GHC -fno-warn-orphans #-}
9{-# OPTIONS_GHC -fno-warn-orphans #-}
15−-- | 'Data.Map' with a better 'Monoid' instance
10+-- |
16−--
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.
17−-- 'Data.Map' has @mappend = union@, which is left-biased. AppendMap has
16+-- 'Data.Map' has @mappend = union@, which is left-biased. 'MonoidalMap'
18−-- @mappend = unionWith mappend@ instead.
17+-- has @mappend = unionWith mappend@ instead.
19module Data.AppendMap
18module Data.AppendMap
20 ( module Data.AppendMap
19 ( module Data.AppendMap
21 , module Data.Map.Monoidal
20 , module Data.Map.Monoidal
@@ -31,22 +30,26 @@ import qualified Data.Map.Internal.Debug as Map (showTree, showTreeWith)
31#else
30#else
32import qualified Data.Map as Map (showTree, showTreeWith)
31import qualified Data.Map as Map (showTree, showTreeWith)
33#endif
32#endif
33+import qualified Data.Witherable as W
34import Data.Map.Monoidal
34import Data.Map.Monoidal
35−import Reflex.Class (FunctorMaybe (..))
36−import Reflex.Patch (Additive, Group (..))
37
35
38{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
36{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
37+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
39type AppendMap = MonoidalMap
38type AppendMap = MonoidalMap
40
39
41{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
40{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
41+-- | A synonym for 'getMonoidalMap'
42_unAppendMap :: MonoidalMap k v -> Map k v
42_unAppendMap :: MonoidalMap k v -> Map k v
43_unAppendMap = getMonoidalMap
43_unAppendMap = getMonoidalMap
44
44
45+-- | Pattern synonym for 'MonoidalMap'
45pattern AppendMap :: Map k v -> MonoidalMap k v
46pattern AppendMap :: Map k v -> MonoidalMap k v
46pattern AppendMap m = MonoidalMap m
47pattern AppendMap m = MonoidalMap m
47
48
49+#if !MIN_VERSION_witherable(0,3,2)
48−instance FunctorMaybe (MonoidalMap k) where
50+instance W.Filterable (MonoidalMap k) where
49− fmapMaybe = mapMaybe
51+ mapMaybe = mapMaybe
52+#endif
50
53
51-- | Deletes a key, returning 'Nothing' if the result is empty.
54-- | Deletes a key, returning 'Nothing' if the result is empty.
52nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
55nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -56,24 +59,22 @@ nonEmptyDelete k vs =
56 then Nothing
59 then Nothing
57 else Just deleted
60 else Just deleted
58
61
62+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
59mapMaybeNoNull :: (a -> Maybe b)
63mapMaybeNoNull :: (a -> Maybe b)
60 -> MonoidalMap token a
64 -> MonoidalMap token a
61 -> Maybe (MonoidalMap token b)
65 -> Maybe (MonoidalMap token b)
62mapMaybeNoNull f as =
66mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
67+ let bs = mapMaybe f as
64 in if null bs
68 in if null bs
65 then Nothing
69 then Nothing
66 else Just bs
70 else Just bs
67
71
68-- TODO: Move instances to `Reflex.Patch`
72-- TODO: Move instances to `Reflex.Patch`
69−instance (Ord k, Group q) => Group (MonoidalMap k q) where
73+-- | Displays a 'MonoidalMap' as a tree. See 'Data.Map.Lazy.showTree' for details.
70− negateG = map negateG
71−
72−instance (Ord k, Additive q) => Additive (MonoidalMap k q)
73−
74showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
74showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
75showTree = coerce (Map.showTree :: Map k a -> String)
75showTree = coerce (Map.showTree :: Map k a -> String)
76
76
77+-- | Displays a 'MonoidalMap' as a tree, using the supplied function to convert nodes to string.
77showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
78showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
78showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
79showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
79
80
src/Data/FastMutableIntMap.hs+17
@@ -1,4 +1,9 @@
1{-# LANGUAGE TypeFamilies #-}
1{-# LANGUAGE TypeFamilies #-}
2+-- |
3+-- Module:
4+-- Data.FastMutableIntMap
5+-- Description:
6+-- A mutable version of 'IntMap'
2module Data.FastMutableIntMap
7module Data.FastMutableIntMap
3 ( FastMutableIntMap
8 ( FastMutableIntMap
4 , new
9 , new
@@ -32,33 +37,43 @@ import Data.IORef
32import Reflex.Patch.Class
37import Reflex.Patch.Class
33import Reflex.Patch.IntMap
38import Reflex.Patch.IntMap
34
39
40+-- | A 'FastMutableIntMap' holds a map of values of type @a@ and allows low-overhead modifications via IO.
41+-- Operations on 'FastMutableIntMap' run in IO.
35newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
42newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
36
43
44+-- | Create a new 'FastMutableIntMap' out of an 'IntMap'
37new :: IntMap a -> IO (FastMutableIntMap a)
45new :: IntMap a -> IO (FastMutableIntMap a)
38new m = FastMutableIntMap <$> newIORef m
46new m = FastMutableIntMap <$> newIORef m
39
47
48+-- | Create a new empty 'FastMutableIntMap'
40newEmpty :: IO (FastMutableIntMap a)
49newEmpty :: IO (FastMutableIntMap a)
41newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
50newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
42
51
52+-- | Insert an element into a 'FastMutableIntMap' at the given key
43insert :: FastMutableIntMap a -> Int -> a -> IO ()
53insert :: FastMutableIntMap a -> Int -> a -> IO ()
44insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
54insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
45
55
56+-- | Attempt to lookup an element by key in a 'FastMutableIntMap'
46lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
57lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
47lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
58lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
48
59
60+-- | Runs the provided action over the intersection of a 'FastMutableIntMap' and an 'IntMap'
49forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
61forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
50forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
62forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
51 a <- liftIO $ readIORef r
63 a <- liftIO $ readIORef r
52 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
64 traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
53
65
66+-- | Runs the provided action over the values of a 'FastMutableIntMap'
54for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
67for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
55for_ (FastMutableIntMap r) f = do
68for_ (FastMutableIntMap r) f = do
56 a <- liftIO $ readIORef r
69 a <- liftIO $ readIORef r
57 traverse_ f a
70 traverse_ f a
58
71
72+-- | Checks whether a 'FastMutableIntMap' is empty
59isEmpty :: FastMutableIntMap a -> IO Bool
73isEmpty :: FastMutableIntMap a -> IO Bool
60isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
74isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
61
75
76+-- | Retrieves the size of a 'FastMutableIntMap'
62size :: FastMutableIntMap a -> IO Int
77size :: FastMutableIntMap a -> IO Int
63size (FastMutableIntMap r) = IntMap.size <$> readIORef r
78size (FastMutableIntMap r) = IntMap.size <$> readIORef r
64
79
@@ -69,6 +84,8 @@ getFrozenAndClear (FastMutableIntMap r) = do
69 writeIORef r IntMap.empty
84 writeIORef r IntMap.empty
70 return result
85 return result
71
86
87+-- | Updates the value of a 'FastMutableIntMap' with the given patch (see 'Reflex.Patch.IntMap'),
88+-- and returns an 'IntMap' with the modified keys and values.
72applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
89applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
73applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
90applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
74 v <- readIORef r
91 v <- readIORef r
src/Data/FastWeakBag.hs+1−1
@@ -50,7 +50,7 @@ newtype FastWeakBag a = FastWeakBag JSVal
50#else
50#else
51data FastWeakBag a = FastWeakBag
51data FastWeakBag a = FastWeakBag
52 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
52 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
53+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
54 }
54 }
55#endif
55#endif
56
56
src/Data/Functor/Misc.hs+3−56
@@ -4,7 +4,6 @@
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
8{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE PatternSynonyms #-}
@@ -38,17 +37,10 @@ module Data.Functor.Misc
38 , dmapToThese
37 , dmapToThese
39 , eitherToDSum
38 , eitherToDSum
40 , dsumToEither
39 , dsumToEither
41− -- * Deprecated functions
42− , sequenceDmap
43− , wrapDMap
44− , rewrapDMap
45− , unwrapDMap
46− , unwrapDMapMaybe
47− , extractFunctorDMap
48 , ComposeMaybe (..)
40 , ComposeMaybe (..)
49 ) where
41 ) where
50
42
51−import Control.Applicative (Applicative, (<$>))
43+import Control.Applicative ((<$>))
52import Control.Monad.Identity
44import Control.Monad.Identity
53import Data.Dependent.Map (DMap)
45import Data.Dependent.Map (DMap)
54import qualified Data.Dependent.Map as DMap
46import qualified Data.Dependent.Map as DMap
@@ -59,8 +51,7 @@ import Data.IntMap (IntMap)
59import qualified Data.IntMap as IntMap
51import qualified Data.IntMap as IntMap
60import Data.Map (Map)
52import Data.Map (Map)
61import qualified Data.Map as Map
53import qualified Data.Map as Map
62−import Data.Some (Some)
54+import Data.Some (Some(Some))
63−import qualified Data.Some as Some
64import Data.These
55import Data.These
65import Data.Typeable hiding (Refl)
56import Data.Typeable hiding (Refl)
66
57
@@ -86,9 +77,6 @@ deriving instance Read k => Read (Const2 k v v)
86instance Show k => GShow (Const2 k v) where
77instance Show k => GShow (Const2 k v) where
87 gshowsPrec n x@(Const2 _) = showsPrec n x
78 gshowsPrec n x@(Const2 _) = showsPrec n x
88
79
89−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
90− showTaggedPrec (Const2 _) = showsPrec
91−
92instance Eq k => GEq (Const2 k v) where
80instance Eq k => GEq (Const2 k v) where
93 geq (Const2 a) (Const2 b) =
81 geq (Const2 a) (Const2 b) =
94 if a == b
82 if a == b
@@ -131,7 +119,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
131-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
119-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
132-- the keys, using a function to remove the wrapping 'Functor'
120-- the keys, using a function to remove the wrapping 'Functor'
133weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
121weakenDMapWith :: (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
135
123
136--------------------------------------------------------------------------------
124--------------------------------------------------------------------------------
137-- WrapArg
125-- WrapArg
@@ -221,11 +209,6 @@ instance GShow (EitherTag l r) where
221 LeftTag -> showString "LeftTag"
209 LeftTag -> showString "LeftTag"
222 RightTag -> showString "RightTag"
210 RightTag -> showString "RightTag"
223
211
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−
229-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
212-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
230eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
213eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
231eitherToDSum = \case
214eitherToDSum = \case
@@ -249,39 +232,3 @@ newtype ComposeMaybe f a =
249 ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
232 ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
250
233
251deriving instance Functor f => Functor (ComposeMaybe f)
234deriving 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
46-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
46-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
47applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
47applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
48applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
48applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
49− where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
49+ where (deletions, insertions) = Map.mapEither maybeToEither patch
50 maybeToEither = \case
50 maybeToEither = \case
51 Nothing -> Left ()
51 Nothing -> Left ()
52 Just r -> Right r
52 Just r -> Right r
53
53
54-- |Split a @'Map' k (Either a b)@ into @Map k a@ and @Map k b@, equivalent to @'Map.mapEither' id@
54-- |Split a @'Map' k (Either a b)@ into @Map k a@ and @Map k b@, equivalent to @'Map.mapEither' id@
55+{-# DEPRECATED mapPartitionEithers "Use 'mapEither' instead" #-}
55mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
56mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
56mapPartitionEithers = Map.mapEither id
57mapPartitionEithers = Map.mapEither id
57
58
src/Reflex.hs+1
@@ -19,6 +19,7 @@ import Reflex.Dynamic.TH as X
19import Reflex.Dynamic.Uniq as X
19import Reflex.Dynamic.Uniq as X
20import Reflex.DynamicWriter.Base as X
20import Reflex.DynamicWriter.Base as X
21import Reflex.DynamicWriter.Class as X
21import Reflex.DynamicWriter.Class as X
22+import Reflex.NotReady.Class as X
22import Reflex.PerformEvent.Base as X
23import Reflex.PerformEvent.Base as X
23import Reflex.PerformEvent.Class as X
24import Reflex.PerformEvent.Class as X
24import Reflex.PostBuild.Base as X
25import Reflex.PostBuild.Base as X
src/Reflex/Adjustable/Class.hs+30−4
@@ -3,7 +3,6 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
@@ -11,6 +10,13 @@
11#ifdef USE_REFLEX_OPTIMIZER
10#ifdef USE_REFLEX_OPTIMIZER
12{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13#endif
12#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.
14module Reflex.Adjustable.Class
20module Reflex.Adjustable.Class
15 (
21 (
16 -- * The Adjustable typeclass
22 -- * The Adjustable typeclass
@@ -94,13 +100,32 @@ instance Adjustable t m => Adjustable t (ReaderT r m) where
94 r <- ask
100 r <- ask
95 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
101 lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
96
102
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)
97−sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
109+ -> m (DMap k Identity, Event t (PatchDMap k Identity))
98sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
110sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
99
111
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)
100−sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
118+ -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
101sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
119sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
102
120
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)
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'))
128+ -> m (Map k v', Event t (PatchMapWithMove k v'))
104mapMapWithAdjustWithMove f m0 m' = do
129mapMapWithAdjustWithMove f m0 m' = do
105 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
130 (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
106 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
131 return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
@@ -110,4 +135,5 @@ mapMapWithAdjustWithMove f m0 m' = do
110--------------------------------------------------------------------------------
135--------------------------------------------------------------------------------
111
136
112{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
137{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
138+-- | Synonym for 'Adjustable'
113type MonadAdjust = Adjustable
139type MonadAdjust = Adjustable
src/Reflex/BehaviorWriter/Class.hs−1
@@ -5,7 +5,6 @@ Description: This module defines the 'MonadBehaviorWriter' class
5{-# LANGUAGE CPP #-}
5{-# LANGUAGE CPP #-}
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
10#ifdef USE_REFLEX_OPTIMIZER
9#ifdef USE_REFLEX_OPTIMIZER
11{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Class.hs+425−117
@@ -7,7 +7,6 @@
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8{-# LANGUAGE GADTs #-}
8{-# LANGUAGE GADTs #-}
9{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE NoMonomorphismRestriction #-}
10{-# LANGUAGE NoMonomorphismRestriction #-}
12{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE PolyKinds #-}
13{-# LANGUAGE RankNTypes #-}
12{-# LANGUAGE RankNTypes #-}
@@ -17,13 +16,18 @@
17{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE TypeOperators #-}
19{-# LANGUAGE UndecidableInstances #-}
18{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
20#ifdef USE_REFLEX_OPTIMIZER
20#ifdef USE_REFLEX_OPTIMIZER
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
22#endif
22#endif
23
23
24+-- |
25+-- Module:
26+-- Reflex.Class
27+-- Description:
24−-- | This module contains the Reflex interface, as well as a variety of
28+-- This module contains the Reflex interface, as well as a variety of
25−-- convenience functions for working with 'Event's, 'Behavior's, and other
29+-- convenience functions for working with 'Event's, 'Behavior's, and other
26−-- signals.
30+-- signals.
27module Reflex.Class
31module Reflex.Class
28 ( module Reflex.Patch
32 ( module Reflex.Patch
29 -- * Primitives
33 -- * Primitives
@@ -32,16 +36,21 @@ module Reflex.Class
32 , coerceBehavior
36 , coerceBehavior
33 , coerceEvent
37 , coerceEvent
34 , coerceDynamic
38 , coerceDynamic
39+ , coerceIncremental
35 , MonadSample (..)
40 , MonadSample (..)
36 , MonadHold (..)
41 , MonadHold (..)
37 -- ** 'fan' related types
42 -- ** 'fan' related types
38 , EventSelector (..)
43 , EventSelector (..)
44+ , EventSelectorG (..)
39 , EventSelectorInt (..)
45 , EventSelectorInt (..)
40 -- * Convenience functions
46 -- * Convenience functions
41 , constDyn
47 , constDyn
42 , pushAlways
48 , pushAlways
43 -- ** Combining 'Event's
49 -- ** Combining 'Event's
44 , leftmost
50 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
45 , mergeMap
54 , mergeMap
46 , mergeIntMap
55 , mergeIntMap
47 , mergeMapIncremental
56 , mergeMapIncremental
@@ -56,6 +65,7 @@ module Reflex.Class
56 , alignEventWithMaybe
65 , alignEventWithMaybe
57 -- ** Breaking up 'Event's
66 -- ** Breaking up 'Event's
58 , splitE
67 , splitE
68+ , fan
59 , fanEither
69 , fanEither
60 , fanThese
70 , fanThese
61 , fanMap
71 , fanMap
@@ -80,6 +90,7 @@ module Reflex.Class
80 , gate
90 , gate
81 -- ** Combining 'Dynamic's
91 -- ** Combining 'Dynamic's
82 , distributeDMapOverDynPure
92 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
83 , distributeListOverDyn
94 , distributeListOverDyn
84 , distributeListOverDynWith
95 , distributeListOverDynWith
85 , zipDyn
96 , zipDyn
@@ -133,8 +144,10 @@ module Reflex.Class
133 -- * Unsafe functions
144 -- * Unsafe functions
134 , unsafeDynamic
145 , unsafeDynamic
135 , unsafeMapIncremental
146 , unsafeMapIncremental
136− -- * 'FunctorMaybe'
147+ -- * 'Filterable' convenience functions
137− , FunctorMaybe (..)
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
138 , fforMaybe
151 , fforMaybe
139 , ffilter
152 , ffilter
140 , filterLeft
153 , filterLeft
@@ -144,10 +157,11 @@ module Reflex.Class
144 , ffor2
157 , ffor2
145 , ffor3
158 , ffor3
146 -- * Deprecated functions
159 -- * Deprecated functions
147− , appendEvents
160+ , switchPromptly
148− , onceE
161+ , switchPromptOnly
149− , sequenceThese
162+ -- * "Cheap" functions
150 , fmapMaybeCheap
163 , fmapMaybeCheap
164+ , mapMaybeCheap
151 , fmapCheap
165 , fmapCheap
152 , fforCheap
166 , fforCheap
153 , fforMaybeCheap
167 , fforMaybeCheap
@@ -155,12 +169,18 @@ module Reflex.Class
155 , tagCheap
169 , tagCheap
156 , mergeWithCheap
170 , mergeWithCheap
157 , mergeWithCheap'
171 , mergeWithCheap'
158− , switchPromptly
159− , switchPromptOnly
160 -- * Slow, but general, implementations
172 -- * Slow, but general, implementations
161 , slowHeadE
173 , slowHeadE
162 ) where
174 ) where
163
175
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+
164import Control.Applicative
184import Control.Applicative
165import Control.Monad.Identity
185import Control.Monad.Identity
166import Control.Monad.Reader
186import Control.Monad.Reader
@@ -188,12 +208,14 @@ import qualified Data.IntMap.Strict as IntMap
188import Data.List.NonEmpty (NonEmpty (..))
208import Data.List.NonEmpty (NonEmpty (..))
189import Data.Map (Map)
209import Data.Map (Map)
190import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
210import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
191−import Data.Some (Some)
211+import Data.Some (Some(Some))
192−import qualified Data.Some as Some
193import Data.String
212import Data.String
194import Data.These
213import Data.These
195import Data.Type.Coercion
214import Data.Type.Coercion
215+import Data.Witherable (Filterable(..))
216+import qualified Data.Witherable as W
217+import Reflex.FunctorMaybe (FunctorMaybe)
196−import Reflex.FunctorMaybe
218+import qualified Reflex.FunctorMaybe
197import Reflex.Patch
219import Reflex.Patch
198import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
220import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
199
221
@@ -248,16 +270,20 @@ class ( MonadHold t (PushM t)
248 -- | Merge a collection of events; the resulting 'Event' will only occur if at
270 -- | Merge a collection of events; the resulting 'Event' will only occur if at
249 -- least one input event is occurring, and will contain all of the input keys
271 -- least one input event is occurring, and will contain all of the input keys
250 -- that are occurring simultaneously
272 -- that are occurring simultaneously
273+
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
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+
252− -- | Efficiently fan-out an event to many destinations. This function should
278+ -- | Efficiently fan-out an event to many destinations. You should save the
253− -- be partially applied, and then the result applied repeatedly to create
279+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
254− -- child events
280+ -- create 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
281+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
282+
256 -- | Create an 'Event' that will occur whenever the currently-selected input
283 -- | Create an 'Event' that will occur whenever the currently-selected input
257 -- 'Event' occurs
284 -- 'Event' occurs
258 switch :: Behavior t (Event t a) -> Event t a
285 switch :: Behavior t (Event t a) -> Event t a
259− -- | Create an 'Event' that will occur whenever the input event is occurring
286+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring
260− -- and its occurrence value, another 'Event', is also occurring
261 coincidence :: Event t (Event t a) -> Event t a
287 coincidence :: Event t (Event t a) -> Event t a
262 -- | Extract the 'Behavior' of a 'Dynamic'.
288 -- | Extract the 'Behavior' of a 'Dynamic'.
263 current :: Dynamic t a -> Behavior t a
289 current :: Dynamic t a -> Behavior t a
@@ -271,9 +297,14 @@ class ( MonadHold t (PushM t)
271 -- that value.
297 -- that value.
272 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
298 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
273 -- | Create a merge whose parents can change over time
299 -- | Create a merge whose parents can change over time
300+ mergeIncrementalG :: GCompare k
301+ => (forall a. q a -> Event t (v a))
274− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
302+ -> Incremental t (PatchDMap k q)
303+ -> Event t (DMap k v)
275 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
304 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
305+ mergeIncrementalWithMoveG :: GCompare k
306+ => (forall a. q a -> Event t (v a))
276− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
307+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
277 -- | Extract the 'Behavior' component of an 'Incremental'
308 -- | Extract the 'Behavior' component of an 'Incremental'
278 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
309 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
279 -- | Extract the 'Event' component of an 'Incremental'
310 -- | Extract the 'Event' component of an 'Incremental'
@@ -289,10 +320,28 @@ class ( MonadHold t (PushM t)
289 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
320 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
290 -- occurrence type
321 -- occurrence type
291 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
322 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)
292 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
327 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
293 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
328 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
294
329
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+
295--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
342--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.
296mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
345mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
297mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
346mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
298
347
@@ -308,6 +357,12 @@ coerceEvent = coerceWith $ eventCoercion Coercion
308coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
357coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
309coerceDynamic = coerceWith $ dynamicCoercion Coercion
358coerceDynamic = coerceWith $ dynamicCoercion Coercion
310
359
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+
311-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
366-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
312-- __must__ change when and only when the 'Event' fires, such that the
367-- __must__ change when and only when the 'Event' fires, such that the
313-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
368-- '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
361 -- the supplied 'Event'.
416 -- the supplied 'Event'.
362 headE :: Event t a -> m (Event t a)
417 headE :: Event t a -> m (Event t a)
363
418
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
364−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
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)
365accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
427accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
428+
429+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
430+accumMIncremental
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)
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)
367accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
436accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
437+
438+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
439+-- combining function produces @Nothing@)
440+accumMaybeIncremental
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)
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)
369accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
446accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
447+
448+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
449+accumMaybeMIncremental
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)
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)
371accumMaybeMIncremental f z e = do
455accumMaybeMIncremental f z e = do
372 rec let e' = flip push e $ \o -> do
456 rec let e' = flip push e $ \o -> do
373 v <- sample $ currentIncremental d'
457 v <- sample $ currentIncremental d'
374 f v o
458 f v o
375 d' <- holdIncremental z e'
459 d' <- holdIncremental z e'
376 return d'
460 return d'
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
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)
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)
378mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
472mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
473+
474+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
475+mapAccumMIncremental
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)
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)
380mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
481mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
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
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)
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)
382mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
495mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
496+
497+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
498+mapAccumMaybeMIncremental
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)
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)
384mapAccumMaybeMIncremental f z e = do
504mapAccumMaybeMIncremental f z e = do
385 rec let e' = flip push e $ \o -> do
505 rec let e' = flip push e $ \o -> do
386 v <- sample $ currentIncremental d'
506 v <- sample $ currentIncremental d'
@@ -388,9 +508,10 @@ mapAccumMaybeMIncremental f z e = do
388 return $ case result of
508 return $ case result of
389 (Nothing, Nothing) -> Nothing
509 (Nothing, Nothing) -> Nothing
390 _ -> Just result
510 _ -> Just result
391− d' <- holdIncremental z $ fmapMaybe fst e'
511+ d' <- holdIncremental z $ mapMaybe fst e'
392− return (d', fmapMaybe snd e')
512+ return (d', mapMaybe snd e')
393
513
514+-- | A somewhat slow implementation of 'headE'
394slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
515slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
395slowHeadE e = do
516slowHeadE e = do
396 rec be <- hold e $ fmapCheap (const never) e'
517 rec be <- hold e $ fmapCheap (const never) e'
@@ -399,18 +520,31 @@ slowHeadE e = do
399
520
400-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
521-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
401-- key. This is much more efficient than filtering for each key with
522-- key. This is much more efficient than filtering for each key with
402−-- 'fmapMaybe'.
523+-- 'mapMaybe'.
403newtype EventSelector t k = EventSelector
524newtype EventSelector t k = EventSelector
404 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
525 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
405 -- determined by the type of the key, so this can be used to fan-out
526 -- determined by the type of the key, so this can be used to fan-out
406 -- 'Event's whose sub-'Event's have different types.
527 -- 'Event's whose sub-'Event's have different types.
407 --
528 --
408 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
529 -- 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
410 -- occurrences of an 'Event'.
531 -- occurrences of an 'Event'.
411 select :: forall a. k a -> Event t a
532 select :: forall a. k a -> Event t a
412 }
533 }
413
534
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.
414newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
548newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
415
549
416--------------------------------------------------------------------------------
550--------------------------------------------------------------------------------
@@ -541,31 +675,39 @@ instance (Reflex t, Num a) => Num (Behavior t a) where
541 negate = fmap negate
675 negate = fmap negate
542 signum = fmap signum
676 signum = fmap signum
543
677
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+
544instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
687instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
545 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
688 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
546 sconcat = pull . fmap sconcat . mapM sample
689 sconcat = pull . fmap sconcat . mapM sample
547−#if MIN_VERSION_semigroups(0,17,0)
548 stimes n = fmap $ stimes n
690 stimes n = fmap $ stimes n
549−#else
550− times1p n = fmap $ times1p n
551−#endif
552
691
692+-- | Alias for 'mapMaybe'
693+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
694+fmapMaybe = mapMaybe
695+
553−-- | Flipped version of 'fmapMaybe'.
696+-- | Flipped version of 'mapMaybe'.
554−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
697+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b
555−fforMaybe = flip fmapMaybe
698+fforMaybe = flip mapMaybe
556
699
557-- | Filter 'f a' using the provided predicate.
700-- | Filter 'f a' using the provided predicate.
558−-- Relies on 'fforMaybe'.
559−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
701+ffilter :: Filterable f => (a -> Bool) -> f a -> f a
560−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
702+ffilter = W.filter
561
703
562-- | Filter 'Left's from 'f (Either a b)' into 'a'.
704-- | Filter 'Left's from 'f (Either a b)' into 'a'.
563−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
705+filterLeft :: Filterable f => f (Either a b) -> f a
564−filterLeft = fmapMaybe (either Just (const Nothing))
706+filterLeft = mapMaybe (either Just (const Nothing))
565
707
566-- | Filter 'Right's from 'f (Either a b)' into 'b'.
708-- | Filter 'Right's from 'f (Either a b)' into 'b'.
567−filterRight :: FunctorMaybe f => f (Either a b) -> f b
709+filterRight :: Filterable f => f (Either a b) -> f b
568−filterRight = fmapMaybe (either (const Nothing) Just)
710+filterRight = mapMaybe (either (const Nothing) Just)
569
711
570-- | Left-biased event union (prefers left event on simultaneous
712-- | Left-biased event union (prefers left event on simultaneous
571-- occurrence).
713-- occurrence).
@@ -583,13 +725,18 @@ instance Reflex t => Bind (Event t) where
583
725
584instance Reflex t => Functor (Event t) where
726instance Reflex t => Functor (Event t) where
585 {-# INLINE fmap #-}
727 {-# INLINE fmap #-}
586− fmap f = fmapMaybe $ Just . f
728+ fmap f = mapMaybe $ Just . f
587 {-# INLINE (<$) #-}
729 {-# INLINE (<$) #-}
588 x <$ e = fmapCheap (const x) e
730 x <$ e = fmapCheap (const x) e
589
731
732+-- TODO Remove this instance
590instance Reflex t => FunctorMaybe (Event t) where
733instance Reflex t => FunctorMaybe (Event t) where
591 {-# INLINE fmapMaybe #-}
734 {-# INLINE fmapMaybe #-}
735+ fmapMaybe = mapMaybe
736+
737+instance Reflex t => Filterable (Event t) where
738+ {-# INLINE mapMaybe #-}
592− fmapMaybe f = push $ return . f
739+ mapMaybe f = push $ return . f
593
740
594-- | Never: @'zero' = 'never'@.
741-- | Never: @'zero' = 'never'@.
595instance Reflex t => Plus (Event t) where
742instance Reflex t => Plus (Event t) where
@@ -738,11 +885,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
738instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
885instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
739 (<>) = alignWith (mergeThese (<>))
886 (<>) = alignWith (mergeThese (<>))
740 sconcat = fmap sconcat . mergeList . toList
887 sconcat = fmap sconcat . mergeList . toList
741−#if MIN_VERSION_semigroups(0,17,0)
742 stimes n = fmap $ stimes n
888 stimes n = fmap $ stimes n
743−#else
744− times1p n = fmap $ times1p n
745−#endif
746
889
747instance (Semigroup a, Reflex t) => Monoid (Event t a) where
890instance (Semigroup a, Reflex t) => Monoid (Event t a) where
748 mempty = never
891 mempty = never
@@ -777,7 +920,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
777mergeList [] = never
920mergeList [] = never
778mergeList es = mergeWithFoldCheap' id es
921mergeList es = mergeWithFoldCheap' id es
779
922
923+unsafeMapIncremental
924+ :: (Reflex t, Patch p, Patch p')
780−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
925+ => (PatchTarget p -> PatchTarget p')
926+ -> (p -> p')
927+ -> Incremental t p
928+ -> Incremental t p'
781unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
929unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
782
930
783-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
931-- | 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)
807fanEither e =
955fanEither e =
808 let justLeft = either Just (const Nothing)
956 let justLeft = either Just (const Nothing)
809 justRight = either (const Nothing) Just
957 justRight = either (const Nothing) Just
810− in (fmapMaybe justLeft e, fmapMaybe justRight e)
958+ in (mapMaybe justLeft e, mapMaybe justRight e)
811
959
812-- | Split the event into separate events for 'This' and 'That' values,
960-- | Split the event into separate events for 'This' and 'That' values,
813-- allowing them to fire simultaneously when the input value is 'These'.
961-- allowing them to fire simultaneously when the input value is 'These'.
@@ -819,7 +967,7 @@ fanThese e =
819 that (That y) = Just y
967 that (That y) = Just y
820 that (These _ y) = Just y
968 that (These _ y) = Just y
821 that _ = Nothing
969 that _ = Nothing
822− in (fmapMaybe this e, fmapMaybe that e)
970+ in (mapMaybe this e, mapMaybe that e)
823
971
824-- | Split the event into an 'EventSelector' that allows efficient selection of
972-- | Split the event into an 'EventSelector' that allows efficient selection of
825-- the individual 'Event's.
973-- the individual 'Event's.
@@ -894,6 +1042,9 @@ coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $
894 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1042 ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
895 ]
1043 ]
896
1044
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.
897switchHoldPromptOnlyIncremental
1048switchHoldPromptOnlyIncremental
898 :: forall t m p pt w
1049 :: forall t m p pt w
899 . ( Reflex t
1050 . ( Reflex t
@@ -918,7 +1069,15 @@ switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = d
918
1069
919instance Reflex t => Align (Event t) where
1070instance Reflex t => Align (Event t) where
920 nil = never
1071 nil = never
1072+#if MIN_VERSION_these(0, 8, 0)
1073+instance Reflex t => Semialign (Event t) where
1074+#endif
921 align = alignEventWithMaybe Just
1075 align = alignEventWithMaybe Just
1076+
1077+#if defined(MIN_VERSION_semialign)
1078+ zip x y = mapMaybe justThese $ align x y
1079+#endif
1080+
922
1081
923-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
1082-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
924-- the 'Behavior' is true at the time of occurrence.
1083-- the 'Behavior' is true at the time of occurrence.
@@ -959,11 +1118,7 @@ zipDynWith f da db =
959
1118
960instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
1119instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
961 (<>) = zipDynWith (<>)
1120 (<>) = zipDynWith (<>)
962−#if MIN_VERSION_semigroups(0,17,0)
963 stimes n = fmap $ stimes n
1121 stimes n = fmap $ stimes n
964−#else
965− times1p n = fmap $ times1p n
966−#endif
967
1122
968instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
1123instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
969 mconcat = distributeListOverDynWith mconcat
1124 mconcat = distributeListOverDynWith mconcat
@@ -974,12 +1129,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
974-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1129-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
975-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1130-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
976distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1131distributeDMapOverDynPure :: 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
978 [] -> constDyn DMap.empty
1142 [] -> constDyn DMap.empty
979− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1143+ [k :=> v] -> DMap.singleton k <$> nt v
980 _ ->
1144 _ ->
981− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1145+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
982− edmPre = merge $ DMap.map updated dm
1146+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
983 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
1147 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
984 olds <- sample $ current result
1148 olds <- sample $ current result
985 return $ DMap.unionWithKey (\_ _ new -> new) olds news
1149 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -991,21 +1155,32 @@ distributeListOverDyn = distributeListOverDynWith id
991
1155
992-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
1156-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
993distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
1157distributeListOverDynWith :: 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
995
1168
996-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
1169-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
997-- unless the second supplied 'Event' occurs simultaneously.
1170-- unless the second supplied 'Event' occurs simultaneously.
998difference :: Reflex t => Event t a -> Event t b -> Event t a
1171difference :: Reflex t => Event t a -> Event t b -> Event t a
999difference = alignEventWithMaybe $ \case
1172difference = alignEventWithMaybe $ \case
1000 This a -> Just a
1173 This a -> Just a
1001− _ -> Nothing
1174+ _ -> Nothing
1002
1175
1176+-- | Zips two values by taking the union of their shapes and combining with the provided function.
1177+-- 'Nothing' values are dropped.
1003alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1178alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1004−alignEventWithMaybe f ea eb =
1179+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $
1005− fmapMaybe (f <=< dmapToThese)
1006− $ merge
1007− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1180+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1008
1181
1182+-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that
1183+-- matches the provided key.
1009filterEventKey
1184filterEventKey
1010 :: forall t m k v a.
1185 :: forall t m k v a.
1011 ( Reflex t
1186 ( Reflex t
@@ -1023,7 +1198,10 @@ filterEventKey k kv' = do
1023 Nothing -> Nothing
1198 Nothing -> Nothing
1024 takeWhileJustE f kv'
1199 takeWhileJustE f kv'
1025
1200
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.
1027factorEvent
1205factorEvent
1028 :: forall t m k v a.
1206 :: forall t m k v a.
1029 ( Reflex t
1207 ( Reflex t
@@ -1035,9 +1213,9 @@ factorEvent
1035 -> Event t (DSum k v)
1213 -> Event t (DSum k v)
1036 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
1214 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
1037factorEvent k0 kv' = do
1215factorEvent 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'
1039 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1217 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
1041 Just Refl -> return Nothing
1219 Just Refl -> return Nothing
1042 Nothing -> do
1220 Nothing -> do
1043 newInner <- filterEventKey newKey kv'
1221 newInner <- filterEventKey newKey kv'
@@ -1073,26 +1251,94 @@ class Reflex t => Accumulator t f | f -> t where
1073 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
1251 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
1074 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1252 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1075
1253
1254+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1255+-- with the provided function. See 'foldDyn'.
1256+accumDyn
1076−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1257+ :: (Reflex t, MonadHold t m, MonadFix m)
1258+ => (a -> b -> a)
1259+ -> a
1260+ -> Event t b
1261+ -> m (Dynamic t a)
1077accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1262accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1263+
1264+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1265+-- with the provided 'PushM' action.
1266+accumMDyn
1078−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
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)
1079accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1272accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1273+
1274+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1275+-- with the provided function, discarding 'Nothing' results.
1276+accumMaybeDyn
1080−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1277+ :: (Reflex t, MonadHold t m, MonadFix m)
1278+ => (a -> b -> Maybe a)
1279+ -> a
1280+ -> Event t b
1281+ -> m (Dynamic t a)
1081accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1282accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1283+
1284+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1285+-- with the provided 'PushM' action, discarding 'Nothing' results.
1286+accumMaybeMDyn
1082−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
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)
1083accumMaybeMDyn f z e = do
1292accumMaybeMDyn f z e = do
1084 rec let e' = flip push e $ \o -> do
1293 rec let e' = flip push e $ \o -> do
1085 v <- sample $ current d'
1294 v <- sample $ current d'
1086 f v o
1295 f v o
1087 d' <- holdDyn z e'
1296 d' <- holdDyn z e'
1088 return d'
1297 return d'
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
1089−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
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)
1090mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1308mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1309+
1310+-- | Similar to 'mapAccumDyn' except that the combining function is a
1311+-- 'PushM' action.
1312+mapAccumMDyn
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)
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)
1092mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1318mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
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
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)
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)
1094mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1332mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1333+
1334+-- | Like 'mapAccumMaybeDyn' except that the combining function is a
1335+-- 'PushM' action.
1336+mapAccumMaybeMDyn
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)
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)
1096mapAccumMaybeMDyn f z e = do
1342mapAccumMaybeMDyn f z e = do
1097 rec let e' = flip push e $ \o -> do
1343 rec let e' = flip push e $ \o -> do
1098 v <- sample $ current d'
1344 v <- sample $ current d'
@@ -1100,18 +1346,42 @@ mapAccumMaybeMDyn f z e = do
1100 return $ case result of
1346 return $ case result of
1101 (Nothing, Nothing) -> Nothing
1347 (Nothing, Nothing) -> Nothing
1102 _ -> Just result
1348 _ -> Just result
1103− d' <- holdDyn z $ fmapMaybe fst e'
1349+ d' <- holdDyn z $ mapMaybe fst e'
1104− return (d', fmapMaybe snd e')
1350+ return (d', mapMaybe snd e')
1105
1351
1352+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1353+-- with the provided function.
1106{-# INLINE accumB #-}
1354{-# INLINE accumB #-}
1355+accumB
1107−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1356+ :: (Reflex t, MonadHold t m, MonadFix m)
1357+ => (a -> b -> a)
1358+ -> a
1359+ -> Event t b
1360+ -> m (Behavior t a)
1108accumB f = accumMaybeB $ \v o -> Just $ f v o
1361accumB f = accumMaybeB $ \v o -> Just $ f v o
1362+
1363+-- | Like 'accumB' except that the combining function is a 'PushM' action.
1109{-# INLINE accumMB #-}
1364{-# INLINE accumMB #-}
1365+accumMB
1110−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
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)
1111accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1371accumMB 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.
1112{-# INLINE accumMaybeB #-}
1375{-# INLINE accumMaybeB #-}
1376+accumMaybeB
1113−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1377+ :: (Reflex t, MonadHold t m, MonadFix m)
1378+ => (a -> b -> Maybe a)
1379+ -> a
1380+ -> Event t b
1381+ -> m (Behavior t a)
1114accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1382accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1383+
1384+-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action.
1115{-# INLINE accumMaybeMB #-}
1385{-# INLINE accumMaybeMB #-}
1116accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
1386accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
1117accumMaybeMB f z e = do
1387accumMaybeMB f z e = do
@@ -1120,16 +1390,42 @@ accumMaybeMB f z e = do
1120 f v o
1390 f v o
1121 d' <- hold z e'
1391 d' <- hold z e'
1122 return d'
1392 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'.
1123{-# INLINE mapAccumB #-}
1397{-# INLINE mapAccumB #-}
1398+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)
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)
1125mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1404mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1405+
1406+-- | Like 'mapAccumB' except that the combining function is a 'PushM' action.
1126{-# INLINE mapAccumMB #-}
1407{-# INLINE mapAccumMB #-}
1408+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)
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)
1128mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1414mapAccumMB 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.
1129{-# INLINE mapAccumMaybeB #-}
1419{-# INLINE mapAccumMaybeB #-}
1420+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)
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)
1131mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
1426mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
1132
1427
1428+-- | Like 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
1133{-# INLINE mapAccumMaybeMB #-}
1429{-# INLINE mapAccumMaybeMB #-}
1134mapAccumMaybeMB :: (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)
1430mapAccumMaybeMB :: (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)
1135mapAccumMaybeMB f z e = do
1431mapAccumMaybeMB f z e = do
@@ -1139,8 +1435,8 @@ mapAccumMaybeMB f z e = do
1139 return $ case result of
1435 return $ case result of
1140 (Nothing, Nothing) -> Nothing
1436 (Nothing, Nothing) -> Nothing
1141 _ -> Just result
1437 _ -> Just result
1142− d' <- hold z $ fmapMaybe fst e'
1438+ d' <- hold z $ mapMaybe fst e'
1143− return (d', fmapMaybe snd e')
1439+ return (d', mapMaybe snd e')
1144
1440
1145-- | Accumulate occurrences of an 'Event', producing an output occurrence each
1441-- | Accumulate occurrences of an 'Event', producing an output occurrence each
1146-- time. Discard the underlying 'Accumulator'.
1442-- time. Discard the underlying 'Accumulator'.
@@ -1284,38 +1580,70 @@ infixl 4 <@
1284-- Cheap Functions
1580-- Cheap Functions
1285------------------
1581------------------
1286
1582
1583+-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'.
1287{-# INLINE pushAlwaysCheap #-}
1584{-# INLINE pushAlwaysCheap #-}
1288pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1585pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1289pushAlwaysCheap f = pushCheap (fmap Just . f)
1586pushAlwaysCheap f = pushCheap (fmap Just . f)
1290
1587
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'
1291{-# INLINE fmapMaybeCheap #-}
1594{-# INLINE fmapMaybeCheap #-}
1292fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1595fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1293−fmapMaybeCheap f = pushCheap $ return . f
1596+fmapMaybeCheap = mapMaybeCheap
1597+
1294
1598
1599+-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'.
1295{-# INLINE fforMaybeCheap #-}
1600{-# INLINE fforMaybeCheap #-}
1296fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1601fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1297−fforMaybeCheap = flip fmapMaybeCheap
1602+fforMaybeCheap = flip mapMaybeCheap
1298
1603
1604+-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'.
1299{-# INLINE fforCheap #-}
1605{-# INLINE fforCheap #-}
1300fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1606fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1301fforCheap = flip fmapCheap
1607fforCheap = flip fmapCheap
1302
1608
1609+-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'.
1303{-# INLINE fmapCheap #-}
1610{-# INLINE fmapCheap #-}
1304fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1611fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1305fmapCheap f = pushCheap $ return . Just . f
1612fmapCheap f = pushCheap $ return . Just . f
1306
1613
1614+-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'.
1307{-# INLINE tagCheap #-}
1615{-# INLINE tagCheap #-}
1308tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1616tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1309tagCheap b = pushAlwaysCheap $ \_ -> sample b
1617tagCheap b = pushAlwaysCheap $ \_ -> sample b
1310
1618
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'.
1311{-# INLINE mergeWithCheap #-}
1637{-# INLINE mergeWithCheap #-}
1312mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1638mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1313mergeWithCheap = mergeWithCheap' id
1639mergeWithCheap = mergeWithCheap' id
1314
1640
1641+-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'.
1315{-# INLINE mergeWithCheap' #-}
1642{-# INLINE mergeWithCheap' #-}
1316mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1643mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1317mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1644mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1318
1645
1646+-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
1319{-# INLINE mergeWithFoldCheap' #-}
1647{-# INLINE mergeWithFoldCheap' #-}
1320mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1648mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1321mergeWithFoldCheap' f es =
1649mergeWithFoldCheap' f es =
@@ -1328,31 +1656,11 @@ mergeWithFoldCheap' f es =
1328-- Deprecated functions
1656-- Deprecated functions
1329--------------------------------------------------------------------------------
1657--------------------------------------------------------------------------------
1330
1658
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−
1353{-# 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'. " #-}
1659{-# 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'
1354switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1661switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1355switchPromptly = switchHoldPromptly
1662switchPromptly = switchHoldPromptly
1356{-# 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'. " #-}
1663{-# 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'
1357switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1665switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1358switchPromptOnly = switchHoldPromptOnly
1666switchPromptOnly = switchHoldPromptOnly
src/Reflex/Collection.hs+12−10
@@ -10,12 +10,14 @@
10#ifdef USE_REFLEX_OPTIMIZER
10#ifdef USE_REFLEX_OPTIMIZER
11{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12#endif
12#endif
13+-- |
14+-- Module:
15+-- Reflex.Collection
13module Reflex.Collection
16module Reflex.Collection
14 (
17 (
15 -- * Widgets on Collections
18 -- * Widgets on Collections
16 listHoldWithKey
19 listHoldWithKey
17 , listWithKey
20 , listWithKey
18− , listWithKey'
19 , listWithKeyShallowDiff
21 , listWithKeyShallowDiff
20 , listViewWithKey
22 , listViewWithKey
21 , selectViewListWithKey
23 , selectViewListWithKey
@@ -25,6 +27,10 @@ module Reflex.Collection
25 , simpleList
27 , simpleList
26 ) where
28 ) where
27
29
30+#if defined(MIN_VERSION_semialign)
31+import Prelude hiding (zip, zipWith)
32+#endif
33+
28import Control.Monad.Identity
34import Control.Monad.Identity
29import Data.Align
35import Data.Align
30import Data.Functor.Misc
36import Data.Functor.Misc
@@ -38,6 +44,9 @@ import Reflex.Adjustable.Class
38import Reflex.Dynamic
44import Reflex.Dynamic
39import Reflex.PostBuild.Class
45import Reflex.PostBuild.Class
40
46
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.
41listHoldWithKey
50listHoldWithKey
42 :: forall t m k v a
51 :: forall t m k v a
43 . (Ord k, Adjustable t m, MonadHold t m)
52 . (Ord k, Adjustable t m, MonadHold t m)
@@ -96,15 +105,6 @@ listWithKey vals mkChild = do
96 listHoldWithKey Map.empty changeVals $ \k v ->
105 listHoldWithKey Map.empty changeVals $ \k v ->
97 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
106 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
98
107
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−
108-- | Display the given map of items (in key order) using the builder
108-- | Display the given map of items (in key order) using the builder
109-- function provided, and update it with the given event. 'Nothing'
109-- function provided, and update it with the given event. 'Nothing'
110-- update entries will delete the corresponding children, and 'Just'
110-- update entries will delete the corresponding children, and 'Just'
@@ -182,6 +182,8 @@ selectViewListWithKey selection vals mkChild = do
182 return $ fmap ((,) k) selectSelf
182 return $ fmap ((,) k) selectSelf
183 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
183 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
184
184
185+-- | Like 'selectViewListWithKey' but discards the value of the list
186+-- item widget's output 'Event'.
185selectViewListWithKey_
187selectViewListWithKey_
186 :: forall t m k v a
188 :: forall t m k v a
187 . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
189 . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
src/Reflex/Dynamic.hs+18−168
@@ -17,8 +17,12 @@
17#ifdef USE_REFLEX_OPTIMIZER
17#ifdef USE_REFLEX_OPTIMIZER
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
18{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19#endif
19#endif
20+-- |
21+-- Module:
22+-- Reflex.Dynamic
23+-- Description:
20−-- | This module contains various functions for working with 'Dynamic' values.
24+-- This module contains various functions for working with 'Dynamic' values.
21−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
25+-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
22module Reflex.Dynamic
26module Reflex.Dynamic
23 ( -- * Basics
27 ( -- * Basics
24 Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
28 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
70 , distributeFHListOverDynPure
74 , distributeFHListOverDynPure
71 -- * Unsafe
75 -- * Unsafe
72 , unsafeDynamic
76 , 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
92 ) where
77 ) where
93
78
94import Data.Functor.Compose
79import Data.Functor.Compose
95import Data.Functor.Misc
80import Data.Functor.Misc
96import Reflex.Class
81import Reflex.Class
97
82
98−import Control.Applicative ((<*>))
99import Control.Monad
83import Control.Monad
100import Control.Monad.Fix
84import Control.Monad.Fix
101import Control.Monad.Identity
85import Control.Monad.Identity
@@ -103,11 +87,10 @@ import Data.Align
103import Data.Dependent.Map (DMap)
87import Data.Dependent.Map (DMap)
104import qualified Data.Dependent.Map as DMap
88import qualified Data.Dependent.Map as DMap
105import Data.Dependent.Sum (DSum (..))
89import Data.Dependent.Sum (DSum (..))
106−import Data.Functor.Product
107import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
90import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
108import Data.Map (Map)
91import Data.Map (Map)
109import Data.Maybe
92import Data.Maybe
110−import Data.Monoid hiding (Product)
93+import Data.Monoid ((<>))
111import Data.These
94import Data.These
112
95
113import Debug.Trace
96import Debug.Trace
@@ -220,23 +203,15 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
220
203
221-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
204-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
222-- non-'Dynamic' elements, preserving the order of the elements.
205-- non-'Dynamic' elements, preserving the order of the elements.
206+{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
223distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
224−distributeListOverDynPure =
208+distributeListOverDynPure = distributeListOverDyn
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
234
209
235--TODO: Generalize this to functors other than Maps
210--TODO: Generalize this to functors other than Maps
236-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
211-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
237-- with the current values of the 'Dynamic's in a map.
212-- with the current values of the 'Dynamic's in a map.
238joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
213joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
239−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
214+joinDynThroughMap = (distributeMapOverDynPure =<<)
240
215
241-- | Print the value of the 'Dynamic' when it is first read and on each
216-- | Print the value of the 'Dynamic' when it is first read and on each
242-- subsequent change that is observed (as 'traceEvent'), prefixed with the
217-- subsequent change that is observed (as 'traceEvent'), prefixed with the
@@ -318,6 +293,8 @@ maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
318 Left _ -> Nothing
293 Left _ -> Nothing
319 Right a -> Just a
294 Right a -> Just a
320
295
296+-- | Turns a 'Dynamic t (Either a b)' into a 'Dynamic t (Either (Dynamic t a) (Dynamic t b))' such that
297+-- the outer 'Dynamic' is updated only when the 'Either' constructor changes (e.g., from 'Left' to 'Right').
321eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
298eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
322eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
299eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
323 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
300 where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
@@ -325,6 +302,9 @@ eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
325 LeftTag :=> Compose a -> Left $ coerceDynamic a
302 LeftTag :=> Compose a -> Left $ coerceDynamic a
326 RightTag :=> Compose b -> Right $ coerceDynamic b
303 RightTag :=> Compose b -> Right $ coerceDynamic b
327
304
305+-- | Factor a 'Dynamic t DSum' into a 'Dynamic' 'DSum' containing nested 'Dynamic' values.
306+-- The outer 'Dynamic' updates only when the key of the 'DSum' changes, while the update of the inner
307+-- 'Dynamic' represents updates within the current key.
328factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
308factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
329 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
309 => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
330factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
310factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
@@ -339,6 +319,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
339 inner' <- filterEventKey k (updated d)
319 inner' <- filterEventKey k (updated d)
340 inner <- holdDyn v inner'
320 inner <- holdDyn v inner'
341 return $ k :=> Compose inner
321 return $ k :=> Compose inner
322+
342--------------------------------------------------------------------------------
323--------------------------------------------------------------------------------
343-- Demux
324-- Demux
344--------------------------------------------------------------------------------
325--------------------------------------------------------------------------------
@@ -352,7 +333,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
352--
333--
353-- > demuxed (demux d) k === fmap (== k) d
334-- > demuxed (demux d) k === fmap (== k) d
354--
335--
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
356-- /O(log(n))/, rather than /O(n)/ for fmap.
337-- /O(log(n))/, rather than /O(n)/ for fmap.
357data Demux t k = Demux { demuxValue :: Behavior t k
338data Demux t k = Demux { demuxValue :: Behavior t k
358 , demuxSelector :: EventSelector t (Const2 k Bool)
339 , demuxSelector :: EventSelector t (Const2 k Bool)
@@ -506,7 +487,7 @@ instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
506
487
507-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
488-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
508-- single 'Dynamic' whose value represents all the current values of the input's
489-- single 'Dynamic' whose value represents all the current values of the input's
509−-- consitutent 'Dynamic's.
490+-- constituent 'Dynamic's.
510collectDynPure :: ( RebuildSortedHList (HListElems b)
491collectDynPure :: ( RebuildSortedHList (HListElems b)
511 , IsHList a, IsHList b
492 , IsHList a, IsHList b
512 , AllAreFunctors (Dynamic t) (HListElems b)
493 , AllAreFunctors (Dynamic t) (HListElems b)
@@ -547,134 +528,3 @@ instance IsHList (a, b, c, d, e, f) where
547#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
528#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
548 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
529 _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
549#endif
530#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
13 ( qDynPure
13 ( qDynPure
14 , unqDyn
14 , unqDyn
15 , mkDynPure
15 , mkDynPure
16− -- * Deprecated functions
17− , qDyn
18− , mkDyn
19 ) where
16 ) where
20
17
21import Reflex.Dynamic
18import Reflex.Dynamic
@@ -23,7 +20,7 @@ import Reflex.Dynamic
23import Control.Monad.State
20import Control.Monad.State
24import Data.Data
21import Data.Data
25import Data.Generics
22import Data.Generics
26−import Data.Monoid
23+import Data.Monoid ((<>))
27import qualified Language.Haskell.Exts as Hs
24import qualified Language.Haskell.Exts as Hs
28import qualified Language.Haskell.Meta.Syntax.Translate as Hs
25import qualified Language.Haskell.Meta.Syntax.Translate as Hs
29import Language.Haskell.TH
26import Language.Haskell.TH
@@ -84,7 +81,7 @@ mkDynPure = QuasiQuoter
84 }
81 }
85
82
86mkDynExp :: String -> Q Exp
83mkDynExp :: 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
88 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
85 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
89 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
86 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
90 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
87 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
107 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
104 reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
108 | modName == modName' && occName == occName' = 'unqMarker
105 | modName == modName' && occName == occName' = 'unqMarker
109 reinstateUnqDyn x = x
106 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
22import Control.Monad.Exception
22import Control.Monad.Exception
23import Control.Monad.Identity
23import Control.Monad.Identity
24import Control.Monad.IO.Class
24import Control.Monad.IO.Class
25+import Control.Monad.Primitive
25import Control.Monad.Reader
26import Control.Monad.Reader
26import Control.Monad.Ref
27import Control.Monad.Ref
27import Control.Monad.State.Strict
28import Control.Monad.State.Strict
@@ -34,7 +35,7 @@ import Data.IntMap (IntMap)
34import qualified Data.IntMap as IntMap
35import qualified Data.IntMap as IntMap
35import Data.Map (Map)
36import Data.Map (Map)
36import qualified Data.Map as Map
37import qualified Data.Map as Map
37−import Data.Semigroup
38+import Data.Semigroup (Semigroup(..))
38import Data.Some (Some)
39import Data.Some (Some)
39import Data.These
40import Data.These
40
41
@@ -87,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
87 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
88 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
88 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
89 in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
89
90
90−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
91newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
92newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
92 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
93 deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
93
94
@@ -115,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
115 (result, ws) <- runStateT a []
116 (result, ws) <- runStateT a []
116 return (result, mconcat $ reverse ws)
117 return (result, mconcat $ reverse ws)
117
118
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
119 tellDyn w = DynamicWriterT $ modify (w :)
120 tellDyn w = DynamicWriterT $ modify (w :)
120
121
121instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
122instance 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
140 get = lift get
141 get = lift get
141 put = lift . put
142 put = lift . put
142
143
144+instance PrimMonad m => PrimMonad (DynamicWriterT t w m) where
145+ type PrimState (DynamicWriterT t w m) = PrimState m
146+ primitive = lift . primitive
147+
143newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
148newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
144
149
145-- | When the execution of a 'DynamicWriterT' action is adjusted using
150-- | 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 #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
6{-# LANGUAGE UndecidableInstances #-}
7#ifdef USE_REFLEX_OPTIMIZER
7#ifdef USE_REFLEX_OPTIMIZER
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9#endif
9#endif
10module Reflex.DynamicWriter.Class
10module Reflex.DynamicWriter.Class
11− ( MonadDynamicWriter (..)
11+ ( MonadDynamicWriter
12+ , DynamicWriter(..)
12 ) where
13 ) where
13
14
14import Control.Monad.Reader (ReaderT, lift)
15import Control.Monad.Reader (ReaderT, lift)
15import Reflex.Class (Dynamic)
16import Reflex.Class (Dynamic)
16
17
18+{-# DEPRECATED MonadDynamicWriter "Use 'DynamicWriter' instead" #-}
19+-- | Type synonym for 'DynamicWriter'
20+type MonadDynamicWriter = DynamicWriter
21+
17-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
22-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
18-- and combines them monoidally to provide a 'Dynamic' result.
23-- 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
20 tellDyn :: Dynamic t w -> m ()
25 tellDyn :: Dynamic t w -> m ()
21
26
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
23 tellDyn = lift . tellDyn
28 tellDyn = lift . tellDyn
24
29
src/Reflex/EventWriter/Base.hs+3−3
@@ -25,7 +25,7 @@ module Reflex.EventWriter.Base
25import Reflex.Adjustable.Class
25import Reflex.Adjustable.Class
26import Reflex.Class
26import Reflex.Class
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
28−import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
29import Reflex.Host.Class
29import Reflex.Host.Class
30import Reflex.PerformEvent.Class
30import Reflex.PerformEvent.Class
31import Reflex.PostBuild.Class
31import Reflex.PostBuild.Class
@@ -52,7 +52,7 @@ import qualified Data.Map as Map
52import Data.Semigroup
52import Data.Semigroup
53import Data.Some (Some)
53import Data.Some (Some)
54import Data.Tuple
54import Data.Tuple
55−import Data.Type.Equality hiding (apply)
55+import Data.Type.Equality
56
56
57import Unsafe.Coerce
57import Unsafe.Coerce
58
58
@@ -278,7 +278,7 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
278 askQueryResult = lift askQueryResult
278 askQueryResult = lift askQueryResult
279 queryIncremental = lift . queryIncremental
279 queryIncremental = lift . queryIncremental
280
280
281−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (EventWriterT t v m) where
281+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where
282 tellDyn = lift . tellDyn
282 tellDyn = lift . tellDyn
283
283
284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
src/Reflex/EventWriter/Class.hs−1
@@ -2,7 +2,6 @@
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
7#ifdef USE_REFLEX_OPTIMIZER
6#ifdef USE_REFLEX_OPTIMIZER
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/FastWeak.hs+10−6
@@ -6,14 +6,18 @@
6{-# LANGUAGE JavaScriptFFI #-}
6{-# LANGUAGE JavaScriptFFI #-}
7#endif
7#endif
8
8
9+-- |
10+-- Module:
11+-- Reflex.FastWeak
12+-- Description:
9−-- | 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
13+-- '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).
14+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
11--
15--
12−-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
16+-- 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.
17+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
14--
18--
15−-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
19+-- 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.
20+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
17module Reflex.FastWeak
21module Reflex.FastWeak
18 ( FastWeakTicket
22 ( FastWeakTicket
19 , FastWeak
23 , FastWeak
src/Reflex/FunctorMaybe.hs+25−21
@@ -1,43 +1,47 @@
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:
1−-- | This module defines the FunctorMaybe class, which extends Functors with the
11+-- This module defines the FunctorMaybe class, which extends Functors with the
2−-- ability to delete values.
12+-- ability to delete values.
3module Reflex.FunctorMaybe
13module Reflex.FunctorMaybe
4 ( FunctorMaybe (..)
14 ( FunctorMaybe (..)
5 ) where
15 ) where
6
16
7import Data.IntMap (IntMap)
17import Data.IntMap (IntMap)
8−import qualified Data.IntMap as IntMap
9import Data.Map (Map)
18import Data.Map (Map)
10−import qualified Data.Map as Map
19+#if MIN_VERSION_base(4,9,0)
20+import Data.Semigroup (Option(..))
21+#endif
11−import Data.Maybe
22+import Data.Witherable
12
23
13--TODO: See if there's a better class in the standard libraries already
24--TODO: See if there's a better class in the standard libraries already
14
25
15-- | A class for values that combines filtering and mapping using 'Maybe'.
26-- | A class for values that combines filtering and mapping using 'Maybe'.
16−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
27+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.
17−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
28+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-}
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@
27class FunctorMaybe f where
29class FunctorMaybe f where
28 -- | Combined mapping and filtering function.
30 -- | Combined mapping and filtering function.
29 fmapMaybe :: (a -> Maybe b) -> f a -> f b
31 fmapMaybe :: (a -> Maybe b) -> f a -> f b
30
32
31−-- | @fmapMaybe = (=<<)
32instance FunctorMaybe Maybe where
33instance FunctorMaybe Maybe where
33− fmapMaybe = (=<<)
34+ fmapMaybe = mapMaybe
35+
36+#if MIN_VERSION_base(4,9,0)
37+deriving instance FunctorMaybe Option
38+#endif
34
39
35−-- | @fmapMaybe = mapMaybe@
36instance FunctorMaybe [] where
40instance FunctorMaybe [] where
37 fmapMaybe = mapMaybe
41 fmapMaybe = mapMaybe
38
42
39instance FunctorMaybe (Map k) where
43instance FunctorMaybe (Map k) where
40− fmapMaybe = Map.mapMaybe
44+ fmapMaybe = mapMaybe
41
45
42instance FunctorMaybe IntMap where
46instance FunctorMaybe IntMap where
43− fmapMaybe = IntMap.mapMaybe
47+ fmapMaybe = mapMaybe
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE RoleAnnotations #-}
8{-# LANGUAGE RoleAnnotations #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
src/Reflex/Network.hs+10−4
@@ -3,6 +3,11 @@
3#ifdef USE_REFLEX_OPTIMIZER
3#ifdef USE_REFLEX_OPTIMIZER
4{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
4{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
5#endif
5#endif
6+-- |
7+-- Module:
8+-- Reflex.Network
9+-- Description:
10+-- This module provides combinators for building FRP graphs/networks and modifying them dynamically.
6module Reflex.Network
11module Reflex.Network
7 ( networkView
12 ( networkView
8 , networkHold
13 , networkHold
@@ -14,16 +19,17 @@ import Reflex.Adjustable.Class
14import Reflex.NotReady.Class
19import Reflex.NotReady.Class
15import Reflex.PostBuild.Class
20import Reflex.PostBuild.Class
16
21
17−-- | Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
22+-- | A 'Dynamic' "network": Takes a 'Dynamic' of network-creating actions and replaces the network whenever the 'Dynamic' updates.
18−-- The returned Event of network results occurs when the Dynamic does.
23+-- The returned Event of network results fires when the 'Dynamic' updates.
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').
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').
20networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
26networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
21networkView child = do
27networkView child = do
22 postBuild <- getPostBuild
28 postBuild <- getPostBuild
23 let newChild = leftmost [updated child, tagCheap (current child) postBuild]
29 let newChild = leftmost [updated child, tagCheap (current child) postBuild]
24 snd <$> runWithReplace notReady newChild
30 snd <$> runWithReplace notReady newChild
25
31
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.
27-- The returned Dynamic of network results occurs when the Event does.
33-- The returned Dynamic of network results occurs when the Event does.
28-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
34-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
29networkHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
35networkHold :: (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 @@
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
9#ifdef USE_REFLEX_OPTIMIZER
8#ifdef USE_REFLEX_OPTIMIZER
@@ -15,7 +14,9 @@ module Reflex.NotReady.Class
15
14
16import Control.Monad.Reader (ReaderT)
15import Control.Monad.Reader (ReaderT)
17import Control.Monad.Trans
16import Control.Monad.Trans
17+import Control.Monad.Trans.Writer (WriterT)
18
18
19+import Reflex.BehaviorWriter.Base (BehaviorWriterT)
19import Reflex.Class
20import Reflex.Class
20import Reflex.DynamicWriter.Base (DynamicWriterT)
21import Reflex.DynamicWriter.Base (DynamicWriterT)
21import Reflex.EventWriter.Base (EventWriterT)
22import Reflex.EventWriter.Base (EventWriterT)
@@ -39,6 +40,10 @@ instance NotReady t m => NotReady t (ReaderT r m) where
39 notReadyUntil = lift . notReadyUntil
40 notReadyUntil = lift . notReadyUntil
40 notReady = lift notReady
41 notReady = lift notReady
41
42
43+instance (NotReady t m, Monoid w) => NotReady t (WriterT w m) where
44+ notReadyUntil = lift . notReadyUntil
45+ notReady = lift notReady
46+
42instance NotReady t m => NotReady t (PostBuildT t m) where
47instance NotReady t m => NotReady t (PostBuildT t m) where
43 notReadyUntil = lift . notReadyUntil
48 notReadyUntil = lift . notReadyUntil
44 notReady = lift notReady
49 notReady = lift notReady
@@ -51,6 +56,10 @@ instance NotReady t m => NotReady t (DynamicWriterT t w m) where
51 notReadyUntil = lift . notReadyUntil
56 notReadyUntil = lift . notReadyUntil
52 notReady = lift notReady
57 notReady = lift notReady
53
58
59+instance NotReady t m => NotReady t (BehaviorWriterT t w m) where
60+ notReadyUntil = lift . notReadyUntil
61+ notReady = lift notReady
62+
54instance NotReady t m => NotReady t (QueryT t q m) where
63instance NotReady t m => NotReady t (QueryT t q m) where
55 notReadyUntil = lift . notReadyUntil
64 notReadyUntil = lift . notReadyUntil
56 notReady = lift notReady
65 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.
5{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
6{-# LANGUAGE LambdaCase #-}
2{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE OverloadedStrings #-}
3{-# 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.
8module Reflex.Optimizer
12module Reflex.Optimizer
9 ( plugin
13 ( plugin
10 ) where
14 ) 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.
3{-# LANGUAGE TypeFamilies #-}
1{-# 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.
4module Reflex.Patch
8module Reflex.Patch
5 ( module Reflex.Patch
9 ( module Reflex.Patch
6 , module X
10 , module X
@@ -17,11 +21,9 @@ import Reflex.Patch.Map as X
17import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
21import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
18 patchMapWithMoveNewElementsMap, unPatchMapWithMove,
22 patchMapWithMoveNewElementsMap, unPatchMapWithMove,
19 unsafePatchMapWithMove)
23 unsafePatchMapWithMove)
20−
24+import Data.Map.Monoidal (MonoidalMap)
21import Data.Semigroup (Semigroup (..), (<>))
25import Data.Semigroup (Semigroup (..), (<>))
22
26
23−---- Patches based on commutative groups
24−
25-- | A 'Group' is a 'Monoid' where every element has an inverse.
27-- | A 'Group' is a 'Monoid' where every element has an inverse.
26class (Semigroup q, Monoid q) => Group q where
28class (Semigroup q, Monoid q) => Group q where
27 negateG :: q -> q
29 negateG :: q -> q
@@ -37,3 +39,8 @@ newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
37instance Additive p => Patch (AdditivePatch p) where
39instance Additive p => Patch (AdditivePatch p) where
38 type PatchTarget (AdditivePatch p) = p
40 type PatchTarget (AdditivePatch p) = p
39 apply (AdditivePatch p) q = Just $ p <> q
41 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
4
4
5import Control.Monad.Identity
5import Control.Monad.Identity
6import Data.Maybe
6import Data.Maybe
7−import Data.Semigroup
7+import Data.Semigroup (Semigroup(..))
8
8
9-- | A 'Patch' type represents a kind of change made to a datastructure.
9-- | A 'Patch' type represents a kind of change made to a datastructure.
10--
10--
src/Reflex/Patch/DMapWithMove.hs+6−7
@@ -19,9 +19,9 @@ import Reflex.Patch.Class
19import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
19import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
20import qualified Reflex.Patch.MapWithMove as MapWithMove
20import qualified Reflex.Patch.MapWithMove as MapWithMove
21
21
22+import Data.Constraint.Extras
22import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
23import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
23import qualified Data.Dependent.Map as DMap
24import qualified Data.Dependent.Map as DMap
24−import Data.Dependent.Sum (EqTag (..))
25import Data.Functor.Constant
25import Data.Functor.Constant
26import Data.Functor.Misc
26import Data.Functor.Misc
27import Data.Functor.Product
27import Data.Functor.Product
@@ -30,8 +30,7 @@ import Data.GADT.Show (GShow, gshow)
30import qualified Data.Map as Map
30import qualified Data.Map as Map
31import Data.Maybe
31import Data.Maybe
32import Data.Semigroup (Semigroup (..), (<>))
32import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some)
33+import Data.Some (Some(Some))
34−import qualified Data.Some as Some
35import Data.These
34import Data.These
36
35
37-- | 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
36-- | 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 =
105 unbalancedMove _ = Nothing
104 unbalancedMove _ = Nothing
106
105
107-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
106-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
108−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
107+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
109− PatchDMapWithMove a == PatchDMapWithMove b = a == b
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
110
109
111-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
110-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
112data Pair1 f g a = Pair1 (f a) (g a)
111data Pair1 f g a = Pair1 (f a) (g a)
@@ -311,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
311 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
312 From_Insert v -> MapWithMove.From_Insert $ f v
311 From_Insert v -> MapWithMove.From_Insert $ f v
313 From_Delete -> MapWithMove.From_Delete
312 From_Delete -> MapWithMove.From_Delete
314− From_Move k -> MapWithMove.From_Move $ Some.This k
313+ From_Move k -> MapWithMove.From_Move $ Some k
315− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
316 }
315 }
317
316
318-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
317-- |"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 #-}
2{-# LANGUAGE DeriveFoldable #-}
1{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE DeriveTraversable #-}
@@ -33,14 +32,7 @@ instance Patch (PatchIntMap a) where
33instance Semigroup (PatchIntMap v) where
32instance Semigroup (PatchIntMap v) where
34 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
33 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
35 -- PatchMap is idempotent, so stimes n is id for every n
34 -- PatchMap is idempotent, so stimes n is id for every n
36−#if MIN_VERSION_semigroups(0,17,0)
37 stimes = stimesIdempotentMonoid
35 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
44
36
45-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
37-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
46-- (that is, all inserts/updates), producing a @PatchIntMap b@.
38-- (that is, all inserts/updates), producing a @PatchIntMap b@.
src/Reflex/Patch/Map.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE TypeFamilies #-}
2{-# LANGUAGE TypeFamilies #-}
4-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
3-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
@@ -35,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
35instance Ord k => Semigroup (PatchMap k v) where
34instance Ord k => Semigroup (PatchMap k v) where
36 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
35 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
37 -- PatchMap is idempotent, so stimes n is id for every n
36 -- PatchMap is idempotent, so stimes n is id for every n
38−#if MIN_VERSION_semigroups(0,17,0)
39 stimes = stimesIdempotentMonoid
37 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
46
38
47-- | The empty 'PatchMap' contains no insertions or deletions
39-- | The empty 'PatchMap' contains no insertions or deletions
48instance Ord k => Monoid (PatchMap k v) where
40instance Ord k => Monoid (PatchMap k v) where
src/Reflex/Patch/MapWithMove.hs+1−1
@@ -22,7 +22,7 @@ import qualified Data.Map as Map
22import Data.Maybe
22import Data.Maybe
23import Data.Semigroup (Semigroup (..), (<>))
23import Data.Semigroup (Semigroup (..), (<>))
24import qualified Data.Set as Set
24import qualified Data.Set as Set
25−import Data.These
25+import Data.These (These(..))
26import Data.Tuple
26import Data.Tuple
27
27
28-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
28-- | 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
141 case mToPerform of
141 case mToPerform of
142 Nothing -> return [result']
142 Nothing -> return [result']
143 Just toPerform -> do
143 Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
144+ responses <- runHostFrame $ traverseRequesterData (Identity <$>) toPerform
145 mrt <- readRef responseTrigger
145 mrt <- readRef responseTrigger
146 let followupEventTriggers = case mrt of
146 let followupEventTriggers = case mrt of
147 Just rt -> [rt :=> Identity responses]
147 Just rt -> [rt :=> Identity responses]
src/Reflex/PerformEvent/Class.hs−1
@@ -5,7 +5,6 @@
5{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
src/Reflex/PostBuild/Class.hs−1
@@ -4,7 +4,6 @@
4{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE TypeOperators #-}
src/Reflex/Profiled.hs+30−16
@@ -8,9 +8,17 @@
8{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE StandaloneDeriving #-}
9{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE UndecidableInstances #-}
10{-# 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.
11module Reflex.Profiled where
20module Reflex.Profiled where
12
21
13−import Control.Lens hiding (children)
14import Control.Monad
22import Control.Monad
15import Control.Monad.Exception
23import Control.Monad.Exception
16import Control.Monad.Fix
24import Control.Monad.Fix
@@ -27,6 +35,7 @@ import Data.Map (Map)
27import qualified Data.Map.Strict as Map
35import qualified Data.Map.Strict as Map
28import Data.Monoid ((<>))
36import Data.Monoid ((<>))
29import Data.Ord
37import Data.Ord
38+import Data.Profunctor.Unsafe ((#.))
30import qualified Data.Semigroup as S
39import qualified Data.Semigroup as S
31import Data.Type.Coercion
40import Data.Type.Coercion
32import Foreign.Ptr
41import Foreign.Ptr
@@ -71,9 +80,9 @@ getCostCentreStack = go []
71 go (cc : l) parent
80 go (cc : l) parent
72
81
73toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
82toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
74−toCostCentreTree ccs n = do
83+toCostCentreTree ccs n =
75− ccList <- getCostCentreStack ccs
76− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
84+ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
85+ <$> getCostCentreStack ccs
77
86
78getCostCentreTree :: IO CostCentreTree
87getCostCentreTree :: IO CostCentreTree
79getCostCentreTree = do
88getCostCentreTree = do
@@ -127,27 +136,32 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
127 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
136 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
128 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
137 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
129 pull = Behavior_Profiled . pull . coerce
138 pull = Behavior_Profiled . pull . coerce
130− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
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))
131− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
142+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
132− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
143+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
133 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
144 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
134 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
145 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
135 current (Dynamic_Profiled d) = coerce $ current d
146 current (Dynamic_Profiled d) = coerce $ current d
136 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
147 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
137 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
148 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
138 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
149 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)))
150+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
140− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
151+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
141 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
152 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
142 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
153 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
143 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
154 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
144− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
155+ behaviorCoercion c =
145− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
156+ Coercion `trans` behaviorCoercion @t c `trans` Coercion
146− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
157+ eventCoercion c =
147− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
158+ Coercion `trans` eventCoercion @t c `trans` Coercion
159+ dynamicCoercion c =
148− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
160+ Coercion `trans` dynamicCoercion @t c `trans` Coercion
149− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
161+ incrementalCoercion c d =
162+ Coercion `trans` incrementalCoercion @t c d `trans` Coercion
150− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
163+ mergeIntIncremental = Event_Profiled . mergeIntIncremental .
164+ coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
151 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
165 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
152
166
153deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
167deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
src/Reflex/Pure.hs+28−22
@@ -5,6 +5,9 @@
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
8#ifdef USE_REFLEX_OPTIMIZER
11#ifdef USE_REFLEX_OPTIMIZER
9{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10#endif
13#endif
@@ -13,11 +16,13 @@
13-- * MonadSample (Pure t) ((->) t)
16-- * MonadSample (Pure t) ((->) t)
14-- * MonadHold (Pure t) ((->) t)
17-- * MonadHold (Pure t) ((->) t)
15{-# OPTIONS_GHC -fno-warn-orphans #-}
18{-# OPTIONS_GHC -fno-warn-orphans #-}
16−
19+-- |
20+-- Module: Reflex.Pure
21+-- Description:
17−-- | This module provides a pure implementation of Reflex, which is intended to
22+-- 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
23+-- serve as a reference for the semantics of the Reflex class. All
19−-- implementations of Reflex should produce the same results as this
24+-- implementations of Reflex should produce the same results as this
20−-- implementation, although performance and laziness/strictness may differ.
25+-- implementation, although performance and laziness/strictness may differ.
21module Reflex.Pure
26module Reflex.Pure
22 ( Pure
27 ( Pure
23 , Behavior (..)
28 , Behavior (..)
@@ -26,14 +31,9 @@ module Reflex.Pure
26 , Incremental (..)
31 , Incremental (..)
27 ) where
32 ) where
28
33
29−#if !MIN_VERSION_base(4,8,0)
30−import Control.Applicative
31−#endif
32−
33import Control.Monad
34import Control.Monad
34import Data.Dependent.Map (DMap, GCompare)
35import Data.Dependent.Map (DMap, GCompare)
35import qualified Data.Dependent.Map as DMap
36import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Identity
37import Data.IntMap (IntMap)
37import Data.IntMap (IntMap)
38import qualified Data.IntMap as IntMap
38import qualified Data.IntMap as IntMap
39import Data.Maybe
39import Data.Maybe
@@ -41,10 +41,11 @@ import Data.MemoTrie
41import Data.Monoid
41import Data.Monoid
42import Data.Type.Coercion
42import Data.Type.Coercion
43import Reflex.Class
43import Reflex.Class
44+import Data.Kind (Type)
44
45
45-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
46-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
46-- with the type @/t/@.
47-- with the type @/t/@.
47−data Pure t
48+data Pure (t :: Type)
48
49
49-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
50-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
50-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
51-- 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
77 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
78 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
78 -- warning because the GCompare instance is not used; however, removing the
79 -- warning because the GCompare instance is not used; however, removing the
79 -- GCompare instance produces a different warning, due to that constraint
80 -- 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.
81
82
82− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
83+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
84+ -- -> DMap k q -> Event (Pure t) (DMap k v)
83− merge events = Event $ memo $ \t ->
85+ mergeG nt events = Event $ memo $ \t ->
84− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
86+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
85 in if DMap.null currentOccurrences
87 in if DMap.null currentOccurrences
86 then Nothing
88 then Nothing
87 else Just currentOccurrences
89 else Just currentOccurrences
88
90
91+-- The instance signature doeesn't compile, leave commented for documentation
89− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
92+-- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
90− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
93+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
91
94
92 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
95 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
93 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
96 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
110 --a) -> Incremental (Pure t) p a
113 --a) -> Incremental (Pure t) p a
111 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
112
115
113− mergeIncremental = mergeIncrementalImpl
116+ mergeIncrementalG = mergeIncrementalImpl
114− mergeIncrementalWithMove = mergeIncrementalImpl
117+ mergeIncrementalWithMoveG = mergeIncrementalImpl
115
118
116 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
117
120
@@ -126,14 +129,17 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
126 behaviorCoercion Coercion = Coercion
129 behaviorCoercion Coercion = Coercion
127 eventCoercion Coercion = Coercion
130 eventCoercion Coercion = Coercion
128 dynamicCoercion Coercion = Coercion
131 dynamicCoercion Coercion = Coercion
132+ incrementalCoercion Coercion Coercion = Coercion
129
133
130 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
134 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
131
135
132 mergeIntIncremental = mergeIntIncrementalImpl
136 mergeIntIncremental = mergeIntIncrementalImpl
133
137
134−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
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)
135−mergeIncrementalImpl i = Event $ \t ->
141+mergeIncrementalImpl nt i = Event $ \t ->
136− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
142+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
137 in if DMap.null results
143 in if DMap.null results
138 then Nothing
144 then Nothing
139 else Just results
145 else Just results
src/Reflex/Query/Base.hs+28−37
@@ -1,7 +1,6 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE InstanceSigs #-}
3{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RankNTypes #-}
@@ -38,8 +37,7 @@ import Data.Map (Map)
38import qualified Data.Map as Map
37import qualified Data.Map as Map
39import Data.Monoid ((<>))
38import Data.Monoid ((<>))
40import qualified Data.Semigroup as S
39import qualified Data.Semigroup as S
41−import Data.Some (Some)
40+import Data.Some (Some(Some))
42−import qualified Data.Some as Some
43import Data.These
41import Data.These
44
42
45import Reflex.Class
43import Reflex.Class
@@ -74,26 +72,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
74getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
72getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
75getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
73getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
76
74
77−{-
75+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
78−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
76+maskMempty x = if x == mempty then Nothing else Just x
79− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
80− bs' = fmapCheap snd $ r'
81− patches = unsafeBuildIncremental (sampleBs bs0) $
82− flip pushCheap bs' $ \bs -> do
83− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
84− return (Just (AdditivePatch p))
85−-}
86
77
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
88 runWithReplace (QueryT a0) a' = do
79 runWithReplace (QueryT a0) a' = do
89 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
80 ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
90 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
81 let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
91 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
82 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
92 bs' = fmapCheap snd $ r'
83 bs' = fmapCheap snd $ r'
93 bbs <- hold bs0 bs'
84 bbs <- hold bs0 bs'
94− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
85+ let patches = flip pushCheap bs' $ \newBs -> do
95 oldBs <- sample bbs
86 oldBs <- sample bbs
96− (~~) <$> sampleBs newBs <*> sampleBs oldBs
87+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
97 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
88 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
98 QueryT $ lift $ tellEvent patches
89 QueryT $ lift $ tellEvent patches
99 return (r0, fmapCheap fst r')
90 return (r0, fmapCheap fst r')
@@ -152,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
152 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
153 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
154 liftedBs0 :: Map (Some k) [Behavior t q]
145 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
156 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
157 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
148 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
159 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
160 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
161 accumBehaviors :: forall m'. MonadHold t m'
152 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
171 let p k bs = case Map.lookup k bs0 of
162 let p k bs = case Map.lookup k bs0 of
172 Nothing -> case bs of
163 Nothing -> case bs of
173 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
164 -- 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
175 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
166 -- 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
177 Just oldBs -> case bs of
168 Just oldBs -> case bs of
178 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
169 -- 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
180 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
171 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
181 -- composed with the sampling the child's new state.
172 -- composed with the sampling the child's new state.
182− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
173+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
183 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
174 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
184 -- child patches and wrap them in AdditivePatch.
175 -- child patches and wrap them in AdditivePatch.
185− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
176+ patch <- fold <$> Map.traverseWithKey p bs'
186− return (apply pbs bs0, Just patch)
177+ return (apply pbs bs0, AdditivePatch <$> patch)
187 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
178 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
188 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
179 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
189 return (liftedResult0, liftedResult')
180 return (liftedResult0, liftedResult')
@@ -196,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
196 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
197 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
198 liftedBs0 :: Map (Some k) [Behavior t q]
189 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
200 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
201 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
192 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 -}
203 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
204 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
205 accumBehaviors' :: forall m'. MonadHold t m'
196 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
216 p k bs = case Map.lookup k bs0 of
207 p k bs = case Map.lookup k bs0 of
217 Nothing -> case MapWithMove._nodeInfo_from bs of
208 Nothing -> case MapWithMove._nodeInfo_from bs of
218 -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
209 -- 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
220 -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
211 -- 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
222 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
213 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
223− Nothing -> return mempty
214+ Nothing -> return Nothing
224− Just newBs -> sampleBs newBs
215+ Just newBs -> maskMempty <$> sampleBs newBs
225 Just oldBs -> case MapWithMove._nodeInfo_from bs of
216 Just oldBs -> case MapWithMove._nodeInfo_from bs of
226 -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
217 -- 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
228 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
219 -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
229 -- composed with the sampling the child's new state.
220 -- 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)
231 MapWithMove.From_Move k'
222 MapWithMove.From_Move k'
232− | k' == k -> return mempty
223+ | k' == k -> return Nothing
233 | otherwise -> case Map.lookup k' bs0 of
224 | otherwise -> case Map.lookup k' bs0 of
234 -- If we are moving from a non-existent key, that is a delete
225 -- If we are moving from a non-existent key, that is a delete
235− Nothing -> negateG <$> sampleBs oldBs
226+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
236− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
227+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
237 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
228 -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
238 -- child patches and wrap them in AdditivePatch.
229 -- child patches and wrap them in AdditivePatch.
239− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
230+ patch <- fold <$> Map.traverseWithKey p bs'
240− return (apply pbs bs0, Just patch)
231+ return (apply pbs bs0, AdditivePatch <$> patch)
241 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
232 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
242 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
233 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
243 return (liftedResult0, liftedResult')
234 return (liftedResult0, liftedResult')
@@ -340,5 +331,5 @@ instance Requester t m => Requester t (QueryT t q m) where
340instance EventWriter t w m => EventWriter t w (QueryT t q m) where
331instance EventWriter t w m => EventWriter t w (QueryT t q m) where
341 tellEvent = lift . tellEvent
332 tellEvent = lift . tellEvent
342
333
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
344 tellDyn = lift . tellDyn
335 tellDyn = lift . tellDyn
src/Reflex/Query/Class.hs+25−6
@@ -3,9 +3,15 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE UndecidableInstances #-}
7{-# 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.
9module Reflex.Query.Class
15module Reflex.Query.Class
10 ( Query (..)
16 ( Query (..)
11 , QueryMorphism (..)
17 , QueryMorphism (..)
@@ -27,11 +33,15 @@ import Data.Data
27import Data.Ix
33import Data.Ix
28import Data.Map.Monoidal (MonoidalMap)
34import Data.Map.Monoidal (MonoidalMap)
29import qualified Data.Map.Monoidal as MonoidalMap
35import qualified Data.Map.Monoidal as MonoidalMap
30−import Data.Semigroup
36+import Data.Semigroup (Semigroup(..))
31import Foreign.Storable
37import Foreign.Storable
32
38
33import Reflex.Class
39import Reflex.Class
34
40
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'.
35class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
45class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
36 type QueryResult a :: *
46 type QueryResult a :: *
37 crop :: a -> QueryResult a -> QueryResult a
47 crop :: a -> QueryResult a -> QueryResult a
@@ -40,8 +50,8 @@ instance (Ord k, Query v) => Query (MonoidalMap k v) where
40 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
50 type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
41 crop q r = MonoidalMap.intersectionWith (flip crop) r q
51 crop q r = MonoidalMap.intersectionWith (flip crop) r q
42
52
43−-- | NB: QueryMorphism's must be group homomorphisms when acting on the query type
53+-- | 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
54+-- and compatible with the query relationship when acting on the query result.
45data QueryMorphism q q' = QueryMorphism
55data QueryMorphism q q' = QueryMorphism
46 { _queryMorphism_mapQuery :: q -> q'
56 { _queryMorphism_mapQuery :: q -> q'
47 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
57 , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
@@ -54,13 +64,16 @@ instance Category QueryMorphism where
54 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
64 , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
55 }
65 }
56
66
67+-- | Apply a 'QueryMorphism' to a 'Query'
57mapQuery :: QueryMorphism q q' -> q -> q'
68mapQuery :: QueryMorphism q q' -> q -> q'
58mapQuery = _queryMorphism_mapQuery
69mapQuery = _queryMorphism_mapQuery
59
70
71+-- | Map a 'QueryMorphism' to a 'QueryResult'
60mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
72mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
61mapQueryResult = _queryMorphism_mapQueryResult
73mapQueryResult = _queryMorphism_mapQueryResult
62
74
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'
64newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
77newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
65 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
78 deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
66
79
@@ -76,10 +89,14 @@ instance Group SelectedCount where
76
89
77instance Additive SelectedCount
90instance Additive SelectedCount
78
91
92+-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
79−-- | The Semigroup/Monoid/Group instances for a ViewSelector should use this function which returns Nothing if the result is 0. This allows the pruning of leaves that are no longer wanted.
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.
80combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
95combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
81combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
96combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
82
97
98+-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
99+-- used interface.
83class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
100class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
84 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
101 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
85 askQueryResult :: m (Dynamic t (QueryResult q))
102 askQueryResult :: m (Dynamic t (QueryResult q))
@@ -90,9 +107,11 @@ instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
90 askQueryResult = lift askQueryResult
107 askQueryResult = lift askQueryResult
91 queryIncremental = lift . queryIncremental
108 queryIncremental = lift . queryIncremental
92
109
110+-- | Produce and send an 'Incremental' 'Query' from a 'Dynamic' 'Query'.
93tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
111tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
94tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
112tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
95
113
114+-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
96queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
115queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
97queryDyn q = do
116queryDyn q = do
98 tellQueryDyn q
117 tellQueryDyn q
src/Reflex/Requester/Base.hs+108−5
@@ -21,6 +21,7 @@
21module Reflex.Requester.Base
21module Reflex.Requester.Base
22 ( RequesterT (..)
22 ( RequesterT (..)
23 , runRequesterT
23 , runRequesterT
24+ , withRequesterT
24 , runWithReplaceRequesterTWith
25 , runWithReplaceRequesterTWith
25 , traverseIntMapWithKeyWithAdjustRequesterTWith
26 , traverseIntMapWithKeyWithAdjustRequesterTWith
26 , traverseDMapWithKeyWithAdjustRequesterTWith
27 , traverseDMapWithKeyWithAdjustRequesterTWith
@@ -30,6 +31,10 @@ module Reflex.Requester.Base
30 , forRequesterData
31 , forRequesterData
31 , requesterDataToList
32 , requesterDataToList
32 , singletonRequesterData
33 , singletonRequesterData
34+ , matchResponsesWithRequests
35+ , multiEntry
36+ , unMultiEntry
37+ , requesting'
33 ) where
38 ) where
34
39
35import Reflex.Class
40import Reflex.Class
@@ -44,6 +49,7 @@ import Reflex.TriggerEvent.Class
44import Control.Applicative (liftA2)
49import Control.Applicative (liftA2)
45import Control.Monad.Exception
50import Control.Monad.Exception
46import Control.Monad.Identity
51import Control.Monad.Identity
52+import Control.Monad.Primitive
47import Control.Monad.Reader
53import Control.Monad.Reader
48import Control.Monad.Ref
54import Control.Monad.Ref
49import Control.Monad.State.Strict
55import Control.Monad.State.Strict
@@ -60,8 +66,7 @@ import qualified Data.Map as Map
60import Data.Monoid ((<>))
66import Data.Monoid ((<>))
61import Data.Proxy
67import Data.Proxy
62import qualified Data.Semigroup as S
68import qualified Data.Semigroup as S
63−import Data.Some (Some)
69+import Data.Some (Some(Some))
64−import qualified Data.Some as Some
65import Data.Type.Equality
70import Data.Type.Equality
66import Data.Unique.Tag
71import Data.Unique.Tag
67
72
@@ -244,6 +249,10 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
244deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
249deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
245deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
250deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
246
251
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+
247-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
256-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
248instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
257instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
249 mempty = pure mempty
258 mempty = pure mempty
@@ -257,7 +266,6 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response
257-- requests are made, and responses should be provided in the input 'Event'.
266-- requests are made, and responses should be provided in the input 'Event'.
258-- The 'Tag' keys will be used to return the responses to the same place the
267-- The 'Tag' keys will be used to return the responses to the same place the
259-- requests were issued.
268-- requests were issued.
260−
261runRequesterT :: (Reflex t, Monad m)
269runRequesterT :: (Reflex t, Monad m)
262 => RequesterT t request response m a
270 => RequesterT t request response m a
263 -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
271 -> 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
267 coerceEvent responses
275 coerceEvent responses
268 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
276 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
269
277
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+
270instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
292instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
271 type Request (RequesterT t request response m) = request
293 type Request (RequesterT t request response m) = request
272 type Response (RequesterT t request response m) = response
294 type Response (RequesterT t request response m) = response
@@ -370,7 +392,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
370 pack = Entry
392 pack = Entry
371 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
393 f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
372 f' k (n, v) = do
394 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?
374 return (fmapCheap (IntMap.singleton n) myRequests, result)
396 return (fmapCheap (IntMap.singleton n) myRequests, result)
375 ndm' <- numberOccurrencesFrom 1 dm'
397 ndm' <- numberOccurrencesFrom 1 dm'
376 (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
398 (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
418 pack = Entry
440 pack = Entry
419 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
441 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
420 f' k (Compose (n, v)) = do
442 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))
422 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
423 ndm' <- numberOccurrencesFrom 1 dm'
445 ndm' <- numberOccurrencesFrom 1 dm'
424 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
446 (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
432 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
454 promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
433 requests <- holdIncremental requests0 requests'
455 requests <- holdIncremental requests0 requests'
434 return (result0, result')
456 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 @@
6{-# LANGUAGE CPP #-}
6{-# LANGUAGE CPP #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE FunctionalDependencies #-}
8{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE RecursiveDo #-}
9{-# LANGUAGE RecursiveDo #-}
11{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE UndecidableInstances #-}
11{-# LANGUAGE UndecidableInstances #-}
src/Reflex/Spider.hs+5−2
@@ -1,6 +1,9 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2+-- |
3+-- Module:
4+-- Reflex.Spider
5+-- Description:
2−-- | This module exports all of the user-facing functionality of the 'Spider'
6+-- This module exports all of the user-facing functionality of the 'Spider' 'Reflex' engine
3−-- 'Reflex' engine
4module Reflex.Spider
7module Reflex.Spider
5 ( Spider
8 ( Spider
6 , SpiderTimeline
9 , SpiderTimeline
src/Reflex/Spider/Internal.hs+264−201
@@ -9,7 +9,6 @@
9{-# LANGUAGE GADTs #-}
9{-# LANGUAGE GADTs #-}
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11{-# LANGUAGE LambdaCase #-}
11{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
13{-# LANGUAGE MultiWayIf #-}
12{-# LANGUAGE MultiWayIf #-}
14{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE RankNTypes #-}
15{-# LANGUAGE RoleAnnotations #-}
14{-# LANGUAGE RoleAnnotations #-}
@@ -17,9 +16,15 @@
17{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE TypeOperators #-}
19{-# LANGUAGE UndecidableInstances #-}
18{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
20#ifdef USE_REFLEX_OPTIMIZER
24#ifdef USE_REFLEX_OPTIMIZER
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
25{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
22#endif
26#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
23-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
28-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
24-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
29-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
25module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
30module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -33,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
33import Control.Monad.Exception
38import Control.Monad.Exception
34import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
39import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
35import Control.Monad.Primitive
40import 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
37import Control.Monad.Ref
45import Control.Monad.Ref
38import Data.Align
46import Data.Align
39import Data.Coerce
47import Data.Coerce
@@ -49,20 +57,27 @@ import Data.GADT.Compare
49import Data.IntMap.Strict (IntMap)
57import Data.IntMap.Strict (IntMap)
50import qualified Data.IntMap.Strict as IntMap
58import qualified Data.IntMap.Strict as IntMap
51import Data.IORef
59import Data.IORef
52−import Data.Maybe
60+import Data.Kind (Type)
61+import Data.Maybe hiding (mapMaybe)
53import Data.Monoid ((<>))
62import Data.Monoid ((<>))
54import Data.Proxy
63import Data.Proxy
55import Data.These
64import Data.These
56import Data.Traversable
65import Data.Traversable
66+import Data.Witherable (Filterable, mapMaybe)
57import GHC.Exts
67import GHC.Exts
58import GHC.IORef (IORef (..))
68import GHC.IORef (IORef (..))
59import GHC.Stack
69import GHC.Stack
60import Reflex.FastWeak
70import Reflex.FastWeak
61−import Reflex.FunctorMaybe
62import System.IO.Unsafe
71import System.IO.Unsafe
63import System.Mem.Weak
72import System.Mem.Weak
64import Unsafe.Coerce
73import Unsafe.Coerce
65
74
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+
66#ifdef DEBUG_CYCLES
81#ifdef DEBUG_CYCLES
67import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
82import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
68import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
83import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -74,9 +89,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
74import Data.FastWeakBag (FastWeakBag)
89import Data.FastWeakBag (FastWeakBag)
75import qualified Data.FastWeakBag as FastWeakBag
90import qualified Data.FastWeakBag as FastWeakBag
76import Data.Reflection
91import Data.Reflection
77−import Data.Some (Some)
92+import Data.Some (Some(Some))
78−import qualified Data.Some as Some
79import Data.Type.Coercion
93import Data.Type.Coercion
94+import Data.Profunctor.Unsafe ((#.), (.#))
80import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
95import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
81import qualified Data.WeakBag as WeakBag
96import qualified Data.WeakBag as WeakBag
82import qualified Reflex.Class
97import qualified Reflex.Class
@@ -193,12 +208,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193
208
194newNodeId :: IO Int
209newNodeId :: IO Int
195newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
210newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202#endif
211#endif
203
212
204--------------------------------------------------------------------------------
213--------------------------------------------------------------------------------
@@ -302,9 +311,10 @@ cacheEvent e =
302#else
311#else
303 Event $
312 Event $
304#endif
313#endif
314+ unsafePerformIO $ do
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
315+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
316+ <- newIORef emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
317+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308#ifdef DEBUG_TRACE_EVENTS
318#ifdef DEBUG_TRACE_EVENTS
309 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
319 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310#endif
320#endif
@@ -370,7 +380,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370eventNever :: Event x a
380eventNever :: Event x a
371eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
381eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372
382
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)
374eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
384eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375
385
376eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
386eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +432,14 @@ newSubscriberHold h = return $ Subscriber
422 , subscriberRecalculateHeight = \_ -> return ()
432 , subscriberRecalculateHeight = \_ -> return ()
423 }
433 }
424
434
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))
426newSubscriberFan subscribed = return $ Subscriber
436newSubscriberFan subscribed = return $ Subscriber
427 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
437 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
438 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
439 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
440 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431 scheduleClear $ fanSubscribedOccurrence subscribed
441 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
442+ let f _ (Pair v subsubs) = do
433 propagate v $ _fanSubscribedChildren_list subsubs
443 propagate v $ _fanSubscribedChildren_list subsubs
434 return $ Constant ()
444 return $ Constant ()
435 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
445 _ <- 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
545toAny :: a -> Any
555toAny :: a -> Any
546toAny = unsafeCoerce
556toAny = unsafeCoerce
547
557
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.
548data EventSubscribed x = EventSubscribed
563data EventSubscribed x = EventSubscribed
549 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
564 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
565 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -577,12 +592,12 @@ eventSubscribedNever = EventSubscribed
577#endif
592#endif
578 }
593 }
579
594
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
595+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581eventSubscribedFan !subscribed = EventSubscribed
596eventSubscribedFan !subscribed = EventSubscribed
582 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
597 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583 , eventSubscribedRetained = toAny subscribed
598 , eventSubscribedRetained = toAny subscribed
584#ifdef DEBUG_CYCLES
599#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
600+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586 , eventSubscribedHasOwnHeightRef = False
601 , eventSubscribedHasOwnHeightRef = False
587 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
602 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588#endif
603#endif
@@ -595,7 +610,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595#ifdef DEBUG_CYCLES
610#ifdef DEBUG_CYCLES
596 , eventSubscribedGetParents = do
611 , eventSubscribedGetParents = do
597 s <- readIORef $ switchSubscribedCurrentParent subscribed
612 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
613+ return [Some $ _eventSubscription_subscribed s]
599 , eventSubscribedHasOwnHeightRef = True
614 , eventSubscribedHasOwnHeightRef = True
600 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
615 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601#endif
616#endif
@@ -608,8 +623,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608#ifdef DEBUG_CYCLES
623#ifdef DEBUG_CYCLES
609 , eventSubscribedGetParents = do
624 , eventSubscribedGetParents = do
610 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
625 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
626+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
627+ innerParents = maybeToList $ fmap Some innerSubscription
613 return $ outerParent : innerParents
628 return $ outerParent : innerParents
614 , eventSubscribedHasOwnHeightRef = True
629 , eventSubscribedHasOwnHeightRef = True
615 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
630 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +640,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625
640
626walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
641walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627walkInvalidHeightParents s0 = do
642walkInvalidHeightParents 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
629 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
644 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630 when (h == invalidHeight) $ do
645 when (h == invalidHeight) $ do
631 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
646 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
647+ modify (Some s :)
633 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
648 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
649+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635 return subscribers
650 return subscribers
636#endif
651#endif
637
652
@@ -659,7 +674,7 @@ behaviorPull !p = Behavior $ do
659 val <- liftIO $ readIORef $ pullValue p
674 val <- liftIO $ readIORef $ pullValue p
660 case val of
675 case val of
661 Just subscribed -> do
676 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
677+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
678 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
679 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665 return $ pullSubscribedValue subscribed
680 return $ pullSubscribedValue subscribed
@@ -668,7 +683,7 @@ behaviorPull !p = Behavior $ do
668 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
683 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669 parentsRef <- liftIO $ newIORef []
684 parentsRef <- liftIO $ newIORef []
670 holdInits <- askBehaviorHoldInits
685 holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
686+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
687 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673 parents <- liftIO $ readIORef parentsRef
688 parents <- liftIO $ readIORef parentsRef
674 let subscribed = PullSubscribed
689 let subscribed = PullSubscribed
@@ -678,7 +693,7 @@ behaviorPull !p = Behavior $ do
678 , pullSubscribedParents = parents
693 , pullSubscribedParents = parents
679 }
694 }
680 liftIO $ writeIORef (pullValue p) $ Just subscribed
695 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)) :))
682 return a
697 return a
683
698
684behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
699behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +704,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689readHoldTracked h = do
704readHoldTracked h = do
690 result <- liftIO $ readIORef $ holdValue h
705 result <- liftIO $ readIORef $ holdValue h
691 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
706 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)) :))
693 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
708 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694 return result
709 return result
695
710
@@ -703,42 +718,46 @@ readBehaviorUntracked b = do
703-- Dynamic
718-- Dynamic
704--------------------------------------------------------------------------------
719--------------------------------------------------------------------------------
705
720
721+type DynamicS x p = Dynamic x (PatchTarget p) p
722+
706−data Dynamic x p = Dynamic
723+data Dynamic x target p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
724+ { dynamicCurrent :: !(Behavior x target)
708− , dynamicUpdated :: !(Event x p)
725+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709 }
726 }
710
727
728+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
729+
730+
731+
732+
711−dynamicHold :: Hold x p -> Dynamic x p
733+dynamicHold :: Hold x p -> DynamicS x p
712dynamicHold !h = Dynamic
734dynamicHold !h = Dynamic
713 { dynamicCurrent = behaviorHold h
735 { dynamicCurrent = behaviorHold h
714 , dynamicUpdated = eventHold h
736 , dynamicUpdated = eventHold h
715 }
737 }
716
738
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
739+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
718dynamicHoldIdentity = dynamicHold
740dynamicHoldIdentity = dynamicHold
719
741
720−dynamicConst :: PatchTarget p -> Dynamic x p
742+dynamicConst :: PatchTarget p -> DynamicS x p
721dynamicConst !a = Dynamic
743dynamicConst !a = Dynamic
722 { dynamicCurrent = behaviorConst a
744 { dynamicCurrent = behaviorConst a
723 , dynamicUpdated = eventNever
745 , dynamicUpdated = eventNever
724 }
746 }
725
747
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
748+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
727dynamicDyn !d = Dynamic
749dynamicDyn !d = Dynamic
728 { dynamicCurrent = behaviorDyn d
750 { dynamicCurrent = behaviorDyn d
729 , dynamicUpdated = eventDyn d
751 , dynamicUpdated = eventDyn d
730 }
752 }
731
753
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
754+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
733dynamicDynIdentity = dynamicDyn
755dynamicDynIdentity = dynamicDyn
734
756
735--------------------------------------------------------------------------------
757--------------------------------------------------------------------------------
736-- Combinators
758-- Combinators
737--------------------------------------------------------------------------------
759--------------------------------------------------------------------------------
738
760
739−--TODO: Figure out why certain things are not 'representational', then make them
740−--representational so we can use coerce
741−
742--type role Hold representational
761--type role Hold representational
743data Hold x p
762data Hold x p
744 = Hold { holdValue :: !(IORef (PatchTarget p))
763 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -759,20 +778,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759
778
760-- | Stores all global data relevant to a particular Spider timeline; only one
779-- | Stores all global data relevant to a particular Spider timeline; only one
761-- value should exist for each type @x@
780-- value should exist for each type @x@
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+
762−data SpiderTimelineEnv x = SpiderTimelineEnv
786+data SpiderTimelineEnv' x = SpiderTimelineEnv
763 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
787 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
788 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765#ifdef DEBUG
789#ifdef DEBUG
766 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
790 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767#endif
791#endif
768 }
792 }
769−type role SpiderTimelineEnv nominal
793+type role SpiderTimelineEnv' phantom
770
794
771instance Eq (SpiderTimelineEnv x) where
795instance Eq (SpiderTimelineEnv x) where
772 _ == _ = True -- Since only one exists of each type
796 _ == _ = True -- Since only one exists of each type
773
797
774instance GEq SpiderTimelineEnv where
798instance 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)
776 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
800 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777 else Nothing
801 else Nothing
778
802
@@ -782,9 +806,9 @@ data EventEnv x
782 , eventEnvDynInits :: !(IORef [SomeDynInit x])
806 , eventEnvDynInits :: !(IORef [SomeDynInit x])
783 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
807 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
808 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
809+ , eventEnvClears :: !(IORef [Some Clear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
810+ , eventEnvIntClears :: !(IORef [Some IntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
811+ , eventEnvRootClears :: !(IORef [Some RootClear])
788 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
812 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
813 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
814 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
@@ -795,7 +819,7 @@ runEventM :: EventM x a -> IO a
795runEventM = unEventM
819runEventM = unEventM
796
820
797asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
821asksEventEnv :: 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))
799
823
800class MonadIO m => Defer a m where
824class MonadIO m => Defer a m where
801 getDeferralQueue :: m (IORef [a])
825 getDeferralQueue :: m (IORef [a])
@@ -856,29 +880,29 @@ putCurrentHeight h = do
856 heightRef <- asksEventEnv eventEnvCurrentHeight
880 heightRef <- asksEventEnv eventEnvCurrentHeight
857 liftIO $ writeIORef heightRef $! h
881 liftIO $ writeIORef heightRef $! h
858
882
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
883+instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
860 {-# INLINE getDeferralQueue #-}
884 {-# INLINE getDeferralQueue #-}
861 getDeferralQueue = asksEventEnv eventEnvClears
885 getDeferralQueue = asksEventEnv eventEnvClears
862
886
863{-# INLINE scheduleClear #-}
887{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
888+scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
889+scheduleClear r = defer $ Some $ Clear r
866
890
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
891+instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868 {-# INLINE getDeferralQueue #-}
892 {-# INLINE getDeferralQueue #-}
869 getDeferralQueue = asksEventEnv eventEnvIntClears
893 getDeferralQueue = asksEventEnv eventEnvIntClears
870
894
871{-# INLINE scheduleIntClear #-}
895{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
896+scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
897+scheduleIntClear r = defer $ Some $ IntClear r
874
898
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
899+instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876 {-# INLINE getDeferralQueue #-}
900 {-# INLINE getDeferralQueue #-}
877 getDeferralQueue = asksEventEnv eventEnvRootClears
901 getDeferralQueue = asksEventEnv eventEnvRootClears
878
902
879{-# INLINE scheduleRootClear #-}
903{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
904+scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
905+scheduleRootClear r = defer $ Some $ RootClear r
882
906
883instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
907instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884 {-# INLINE getDeferralQueue #-}
908 {-# INLINE getDeferralQueue #-}
@@ -933,9 +957,9 @@ getHoldEventSubscription h = do
933
957
934type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
958type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935
959
936−--type role BehaviorM representational
937-- BehaviorM can sample behaviors
960-- 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))
939
963
940instance Monad (BehaviorM x) where
964instance Monad (BehaviorM x) where
941 {-# INLINE (>>=) #-}
965 {-# INLINE (>>=) #-}
@@ -951,7 +975,7 @@ data BehaviorSubscribed x a
951 = forall p. BehaviorSubscribedHold (Hold x p)
975 = forall p. BehaviorSubscribedHold (Hold x p)
952 | BehaviorSubscribedPull (PullSubscribed x a)
976 | BehaviorSubscribedPull (PullSubscribed x a)
953
977
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
978+newtype SomeBehaviorSubscribed x = SomeBehaviorSubscribed (Some (BehaviorSubscribed x))
955
979
956--type role PullSubscribed representational
980--type role PullSubscribed representational
957data PullSubscribed x a
981data PullSubscribed x a
@@ -986,7 +1010,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
986#endif
1010#endif
987 }
1011 }
988
1012
989−data Root x (k :: * -> *)
1013+data Root x k
990 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
1014 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
991 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
1015 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
992 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
1016 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1080,25 @@ heightBagVerify b@(HeightBag s c) = if
1056heightBagVerify = id
1080heightBagVerify = id
1057#endif
1081#endif
1058
1082
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1083+data FanSubscribedChildren x k v a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1084+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1085+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1086+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
1063 }
1087 }
1064
1088
1065−data FanSubscribed (x :: *) k
1089+data FanSubscribed x k v
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1090+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1091+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1092+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
1069 , fanSubscribedParent :: !(EventSubscription x)
1093 , fanSubscribedParent :: !(EventSubscription x)
1070#ifdef DEBUG_NODEIDS
1094#ifdef DEBUG_NODEIDS
1071 , fanSubscribedNodeId :: Int
1095 , fanSubscribedNodeId :: Int
1072#endif
1096#endif
1073 }
1097 }
1074
1098
1075−data Fan x k
1099+data Fan x k v
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1100+ = Fan { fanParent :: !(Event x (DMap k v))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1101+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1078 }
1102 }
1079
1103
1080data SwitchSubscribed x a
1104data SwitchSubscribed x a
@@ -1128,24 +1152,32 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1128newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1152newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1129newInvalidatorPull p = return $! InvalidatorPull p
1153newInvalidatorPull p = return $! InvalidatorPull p
1130
1154
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1155+instance HasSpiderTimeline x => Filterable (Event x) where
1132− fmapMaybe f = push $ return . f
1156+ mapMaybe f = push $ return . f
1133
1157
1134instance HasSpiderTimeline x => Align (Event x) where
1158instance HasSpiderTimeline x => Align (Event x) where
1135 nil = eventNever
1159 nil = eventNever
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 $
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
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
1137
1169
1138data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1170data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1139 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1171 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1140 | HoldDyn !(Hold x p)
1172 | HoldDyn !(Hold x p)
1141
1173
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1174+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
1143
1175
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)
1145newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1177newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1146
1178
1147--TODO: Avoid the duplication between this and R.zipDynWith
1179--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)
1149zipDynWith f da db =
1181zipDynWith f da db =
1150 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1182 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1151 ec = flip push eab $ \o -> do
1183 ec = flip push eab $ \o -> do
@@ -1168,18 +1200,12 @@ buildDynamic readV0 v' = do
1168 return d
1200 return d
1169
1201
1170unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1202unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1203+unsafeBuildDynamic readV0 v' =
1172− where x = (readV0, v')
1204+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
1173
1205
1174-- ResultM can read behaviors and events
1206-- ResultM can read behaviors and events
1175type ResultM = EventM
1207type ResultM = EventM
1176
1208
1177−{-# NOINLINE unsafeNewIORef #-}
1178−unsafeNewIORef :: a -> b -> IORef b
1179−unsafeNewIORef a b = unsafePerformIO $ do
1180− touch a
1181− newIORef b
1182−
1183instance HasSpiderTimeline x => Functor (Event x) where
1209instance HasSpiderTimeline x => Functor (Event x) where
1184 fmap f = push $ return . Just . f
1210 fmap f = push $ return . Just . f
1185
1211
@@ -1192,33 +1218,42 @@ push f e = cacheEvent (pushCheap f e)
1192
1218
1193{-# INLINABLE pull #-}
1219{-# INLINABLE pull #-}
1194pull :: BehaviorM x a -> Behavior x a
1220pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1221+pull a = unsafePerformIO $ do
1196− { pullCompute = a
1222+ ref <- newIORef Nothing
1197− , pullValue = unsafeNewIORef a Nothing
1198#ifdef DEBUG_NODEIDS
1223#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1224+ nid <- newNodeId
1200#endif
1225#endif
1226+ pure $ behaviorPull $ Pull
1227+ { pullCompute = a
1228+ , pullValue = ref
1229+#ifdef DEBUG_NODEIDS
1230+ , pullNodeId = nid
1231+#endif
1201− }
1232+ }
1202
1233
1203{-# INLINABLE switch #-}
1234{-# INLINABLE switch #-}
1204switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1235switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1236+switch a = unsafePerformIO $ do
1237+ ref <- newIORef Nothing
1205−switch a = eventSwitch $ Switch
1238+ pure $ eventSwitch $ Switch
1206− { switchParent = a
1239+ { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1240+ , switchSubscribed = ref
1208− }
1241+ }
1209
1242
1210coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1243coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1244+coincidence a = unsafePerformIO $ do
1245+ ref <- newIORef Nothing
1211−coincidence a = eventCoincidence $ Coincidence
1246+ pure $ eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1247+ { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1248+ , coincidenceSubscribed = ref
1214− }
1249+ }
1215
1250
1216-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1251-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1217run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1252run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1218run roots after = do
1253run roots after = do
1219 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1254 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1220 let t = spiderTimeline :: SpiderTimelineEnv x
1255 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
1222 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1257 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1223 occBefore <- liftIO $ do
1258 occBefore <- liftIO $ do
1224 occBefore <- readIORef occRef
1259 occBefore <- readIORef occRef
@@ -1256,11 +1291,11 @@ scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1256 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1291 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1257 EQ -> a
1292 EQ -> a
1258
1293
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1294+newtype Clear a = Clear (IORef (Maybe a))
1260
1295
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1296+newtype IntClear a = IntClear (IORef (IntMap a))
1262
1297
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1298+newtype RootClear k = RootClear (IORef (DMap k Identity))
1264
1299
1265data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1300data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1266
1301
@@ -1282,9 +1317,9 @@ type WeakList a = [Weak a]
1282withIncreasedDepth :: CanTrace x m => m a -> m a
1317withIncreasedDepth :: CanTrace x m => m a -> m a
1283withIncreasedDepth a = do
1318withIncreasedDepth a = do
1284 spiderTimeline <- askSpiderTimelineEnv
1319 spiderTimeline <- askSpiderTimelineEnv
1285− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1320+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
1286 result <- a
1321 result <- a
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1322+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
1288 return result
1323 return result
1289#else
1324#else
1290withIncreasedDepth :: m a -> m a
1325withIncreasedDepth :: m a -> m a
@@ -1311,7 +1346,7 @@ traceMWhen _ b getMessage = when b $ do
1311 message <- getMessage
1346 message <- getMessage
1312#ifdef DEBUG
1347#ifdef DEBUG
1313 spiderTimeline <- askSpiderTimelineEnv
1348 spiderTimeline <- askSpiderTimelineEnv
1314− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1349+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
1315#else
1350#else
1316 let d = 0
1351 let d = 0
1317#endif
1352#endif
@@ -1352,25 +1387,25 @@ propagateSubscriberHold h a = do
1352data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1387data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1353
1388
1354runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1389runBehaviorM :: 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)
1356
1391
1357askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1392askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1358askInvalidator = do
1393askInvalidator = do
1359− (!m, _) <- BehaviorM ask
1394+ (!m, _) <- ask
1360 case m of
1395 case m of
1361 Nothing -> return Nothing
1396 Nothing -> return Nothing
1362 Just (!wi, _) -> return $ Just wi
1397 Just (!wi, _) -> return $ Just wi
1363
1398
1364askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1399askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1365askParentsRef = do
1400askParentsRef = do
1366− (!m, _) <- BehaviorM ask
1401+ (!m, _) <- ask
1367 case m of
1402 case m of
1368 Nothing -> return Nothing
1403 Nothing -> return Nothing
1369 Just (_, !p) -> return $ Just p
1404 Just (_, !p) -> return $ Just p
1370
1405
1371askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1406askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1372askBehaviorHoldInits = do
1407askBehaviorHoldInits = do
1373− (_, !his) <- BehaviorM ask
1408+ (_, !his) <- ask
1374 return his
1409 return his
1375
1410
1376{-# INLINE getDynHold #-}
1411{-# INLINE getDynHold #-}
@@ -1415,6 +1450,9 @@ getRootSubscribed k r sub = do
1415 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1450 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1416 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1451 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1417 writeIORef uninitRef $! uninit
1452 writeIORef uninitRef $! uninit
1453+#ifdef DEBUG_NODEIDS
1454+ nid <- newNodeId
1455+#endif
1418 let !subscribed = RootSubscribed
1456 let !subscribed = RootSubscribed
1419 { rootSubscribedKey = k
1457 { rootSubscribedKey = k
1420 , rootSubscribedCachedSubscribed = cached
1458 , rootSubscribedCachedSubscribed = cached
@@ -1423,7 +1461,7 @@ getRootSubscribed k r sub = do
1423 , rootSubscribedUninit = uninit
1461 , rootSubscribedUninit = uninit
1424 , rootSubscribedWeakSelf = weakSelf
1462 , rootSubscribedWeakSelf = weakSelf
1425#ifdef DEBUG_NODEIDS
1463#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1464+ , rootSubscribedNodeId = nid
1427#endif
1465#endif
1428 }
1466 }
1429 -- If we die at the same moment that all our children die, they will
1467 -- If we die at the same moment that all our children die, they will
@@ -1472,16 +1510,10 @@ newFanInt = do
1472 , _fanInt_occRef = occRef
1510 , _fanInt_occRef = occRef
1473 }
1511 }
1474
1512
1475−{-# NOINLINE unsafeNewFanInt #-}
1476−unsafeNewFanInt :: b -> FanInt x a
1477−unsafeNewFanInt b = unsafePerformIO $ do
1478− touch b
1479− newFanInt
1480−
1481fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1513fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1514+fanInt p = unsafePerformIO $ do
1483− let self = unsafeNewFanInt p
1515+ self <- newFanInt
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1516+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
1485 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1517 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1486 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1518 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1487 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1519 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1517,7 +1549,7 @@ fanInt p =
1517 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1549 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1518
1550
1519{-# INLINABLE getFanSubscribed #-}
1551{-# 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))
1521getFanSubscribed k f sub = do
1553getFanSubscribed k f sub = do
1522 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1554 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1523 case mSubscribed of
1555 case mSubscribed of
@@ -1535,13 +1567,16 @@ getFanSubscribed k f sub = do
1535 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1567 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1536 occRef <- liftIO $ newIORef parentOcc
1568 occRef <- liftIO $ newIORef parentOcc
1537 when (isJust parentOcc) $ scheduleClear occRef
1569 when (isJust parentOcc) $ scheduleClear occRef
1570+#ifdef DEBUG_NODEIDS
1571+ nid <- liftIO newNodeId
1572+#endif
1538 let subscribed = FanSubscribed
1573 let subscribed = FanSubscribed
1539 { fanSubscribedCachedSubscribed = fanSubscribed f
1574 { fanSubscribedCachedSubscribed = fanSubscribed f
1540 , fanSubscribedOccurrence = occRef
1575 , fanSubscribedOccurrence = occRef
1541 , fanSubscribedParent = subscription
1576 , fanSubscribedParent = subscription
1542 , fanSubscribedSubscribers = subscribersRef
1577 , fanSubscribedSubscribers = subscribersRef
1543#ifdef DEBUG_NODEIDS
1578#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1579+ , fanSubscribedNodeId = nid
1545#endif
1580#endif
1546 }
1581 }
1547 let !self = (k, subscribed)
1582 let !self = (k, subscribed)
@@ -1551,7 +1586,7 @@ getFanSubscribed k f sub = do
1551 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1586 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1552 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1587 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1553
1588
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1589+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
1555cleanupFanSubscribed (k, subscribed) = do
1590cleanupFanSubscribed (k, subscribed) = do
1556 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1591 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1557 let reducedSubscribers = DMap.delete k subscribers
1592 let reducedSubscribers = DMap.delete k subscribers
@@ -1563,7 +1598,7 @@ cleanupFanSubscribed (k, subscribed) = do
1563 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1598 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1564
1599
1565{-# INLINE subscribeFanSubscribed #-}
1600{-# 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
1567subscribeFanSubscribed k subscribed sub = do
1602subscribeFanSubscribed k subscribed sub = do
1568 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1603 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1569 case DMap.lookup k subscribers of
1604 case DMap.lookup k subscribers of
@@ -1601,6 +1636,9 @@ getSwitchSubscribed s sub = do
1601 when (isJust parentOcc) $ scheduleClear occRef
1636 when (isJust parentOcc) $ scheduleClear occRef
1602 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1637 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1603 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1638 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1639+#ifdef DEBUG_NODEIDS
1640+ nid <- liftIO newNodeId
1641+#endif
1604 let !subscribed = SwitchSubscribed
1642 let !subscribed = SwitchSubscribed
1605 { switchSubscribedCachedSubscribed = switchSubscribed s
1643 { switchSubscribedCachedSubscribed = switchSubscribed s
1606 , switchSubscribedOccurrence = occRef
1644 , switchSubscribedOccurrence = occRef
@@ -1613,7 +1651,7 @@ getSwitchSubscribed s sub = do
1613 , switchSubscribedCurrentParent = subscriptionRef
1651 , switchSubscribedCurrentParent = subscriptionRef
1614 , switchSubscribedWeakSelf = weakSelf
1652 , switchSubscribedWeakSelf = weakSelf
1615#ifdef DEBUG_NODEIDS
1653#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1654+ , switchSubscribedNodeId = nid
1617#endif
1655#endif
1618 }
1656 }
1619 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1657 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1658,6 +1696,9 @@ getCoincidenceSubscribed c sub = do
1658 scheduleClear innerSubdRef
1696 scheduleClear innerSubdRef
1659 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1697 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1660 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1698 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1699+#ifdef DEBUG_NODEIDS
1700+ nid <- liftIO newNodeId
1701+#endif
1661 let subscribed = CoincidenceSubscribed
1702 let subscribed = CoincidenceSubscribed
1662 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1703 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1663 , coincidenceSubscribedOccurrence = occRef
1704 , coincidenceSubscribedOccurrence = occRef
@@ -1668,7 +1709,7 @@ getCoincidenceSubscribed c sub = do
1668 , coincidenceSubscribedInnerParent = innerSubdRef
1709 , coincidenceSubscribedInnerParent = innerSubdRef
1669 , coincidenceSubscribedWeakSelf = weakSelf
1710 , coincidenceSubscribedWeakSelf = weakSelf
1670#ifdef DEBUG_NODEIDS
1711#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1712+ , coincidenceSubscribedNodeId = nid
1672#endif
1713#endif
1673 }
1714 }
1674 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1715 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1685,26 +1726,34 @@ cleanupCoincidenceSubscribed subscribed = do
1685subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1726subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1686subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1727subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1687
1728
1688−{-# INLINE merge #-}
1729+{-# INLINE mergeG #-}
1689−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
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)
1690−merge d = cacheEvent (mergeCheap d)
1733+mergeG nt d = cacheEvent (mergeCheap nt d)
1691
1734
1692{-# INLINE mergeWithMove #-}
1735{-# INLINE mergeWithMove #-}
1693−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
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)
1694−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1739+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
1695
1740
1696{-# INLINE [1] mergeCheap #-}
1741{-# INLINE [1] mergeCheap #-}
1742+mergeCheap
1697−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
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)
1698−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1747+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
1699 where
1748 where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1749+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
1701 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1750 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1702 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1751 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1703 (mOldSubd, newPs) <- case me of
1752 (mOldSubd, newPs) <- case me of
1704 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1753 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1705 Just e -> do
1754 Just e -> do
1706 let s = subscriber $ return k
1755 let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1756+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
1708 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1757 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1709 let newParent = MergeSubscribedParent subscription
1758 let newParent = MergeSubscribedParent subscription
1710 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1759 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1714,28 +1763,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1714 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1763 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1715 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1764 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1716 foldM f ([], oldParents) $ DMap.toList p
1765 foldM f ([], oldParents) $ DMap.toList p
1766+
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1767+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
1718 getInitialSubscribers initialParents subscriber = do
1768 getInitialSubscribers initialParents subscriber = do
1719 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1769 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1720 let s = subscriber $ return k
1770 let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1771+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1722 height <- liftIO $ getEventSubscribedHeight parentSubd
1772 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)
1724 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1774 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1725 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1775 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1726 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1776 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1727 )
1777 )
1778+
1728 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1779 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1729 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1780 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1730
1781
1731{-# INLINE [1] mergeCheapWithMove #-}
1782{-# INLINE [1] mergeCheapWithMove #-}
1732−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
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)
1733−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1787+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
1734 where
1788 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)
1736 updateMe subscriber heightBagRef oldParents p = do
1790 updateMe subscriber heightBagRef oldParents p = do
1737 -- Prepare new parents for insertion
1791 -- 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)
1739 subscribeParent k e = do
1793 subscribeParent k e = do
1740 keyRef <- liftIO $ newIORef k
1794 keyRef <- liftIO $ newIORef k
1741 let s = subscriber $ liftIO $ readIORef keyRef
1795 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1744,9 +1798,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1744 newParentHeight <- getEventSubscribedHeight subd
1798 newParentHeight <- getEventSubscribedHeight subd
1745 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1799 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1746 return $ MergeSubscribedParentWithMove subscription keyRef
1800 return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1801+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
1748 -- Collect old parents for deletion and update the keys of moved parents
1802 -- 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
1750 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1804 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1751 Nothing -> do
1805 Nothing -> do
1752 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
1806 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1757,46 +1811,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1757 return Nothing
1811 return Nothing
1758 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1812 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1759 return (toDelete, applyAlways p' oldParents)
1813 return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1814+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
1761 getInitialSubscribers initialParents subscriber = do
1815 getInitialSubscribers initialParents subscriber = do
1762 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1816 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1763 keyRef <- liftIO $ newIORef k
1817 keyRef <- liftIO $ newIORef k
1764 let s = subscriber $ liftIO $ readIORef keyRef
1818 let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1819+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1766 height <- liftIO $ getEventSubscribedHeight parentSubd
1820 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)
1768 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1822 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1769 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1823 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1770 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1824 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1771 )
1825 )
1826+
1772 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1827 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1773 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1828 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1774
1829
1775−type MergeUpdateFunc k x p s
1830+type MergeUpdateFunc k v x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1831+ = (forall a. EventM x (k a) -> Subscriber x (v a))
1777 -> IORef HeightBag
1832 -> IORef HeightBag
1778 -> DMap k s
1833 -> DMap k s
1779 -> p
1834 -> p
1780 -> EventM x ([EventSubscription x], DMap k s)
1835 -> EventM x ([EventSubscription x], DMap k s)
1781
1836
1782−type MergeInitFunc k x s
1837+type MergeInitFunc k v q x s
1783− = DMap k (Event x)
1838+ = DMap k q
1784− -> (forall a. EventM x (k a) -> Subscriber x a)
1839+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1785− -> EventM x (DMap k Identity, [Height], DMap k s)
1840+ -> EventM x (DMap k v, [Height], DMap k s)
1786
1841
1787type MergeDestroyFunc k s
1842type MergeDestroyFunc k s
1788 = DMap k s
1843 = DMap k s
1789 -> IO ()
1844 -> IO ()
1790
1845
1791−data Merge x k s = Merge
1846+data Merge x k v s = Merge
1792 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1847 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1793 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1848 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1794 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1849 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1850+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1851+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
1797 }
1852 }
1798
1853
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1854+invalidateMergeHeight :: Merge x k v s -> IO ()
1800invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1855invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1801
1856
1802invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1857invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1806,8 +1861,7 @@ invalidateMergeHeight' heightRef sub = do
1806 writeIORef heightRef $! invalidHeight
1861 writeIORef heightRef $! invalidHeight
1807 subscriberInvalidateHeight sub oldHeight
1862 subscriberInvalidateHeight sub oldHeight
1808
1863
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1864+revalidateMergeHeight :: Merge x k v s -> IO ()
1811revalidateMergeHeight m = do
1865revalidateMergeHeight m = do
1812 currentHeight <- readIORef $ _merge_heightRef m
1866 currentHeight <- readIORef $ _merge_heightRef m
1813 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1867 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
1823 subscriberRecalculateHeight (_merge_sub m) height
1877 subscriberRecalculateHeight (_merge_sub m) height
1824 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1878 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1825
1879
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1880+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
1827scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1881scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1828 vals <- liftIO $ readIORef $ _merge_accumRef m
1882 vals <- liftIO $ readIORef $ _merge_accumRef m
1829 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
1883 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
1830 --TODO: Assert that m is not empty
1884 --TODO: Assert that m is not empty
1831 subscriberPropagate (_merge_sub m) vals
1885 subscriberPropagate (_merge_sub m) vals
1832
1886
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)
1834mergeSubscriber m getKey = Subscriber
1888mergeSubscriber m getKey = Subscriber
1835 { subscriberPropagate = \a -> do
1889 { subscriberPropagate = \a -> do
1836 oldM <- liftIO $ readIORef $ _merge_accumRef m
1890 oldM <- liftIO $ readIORef $ _merge_accumRef m
1837 k <- getKey
1891 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
1839 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1893 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1840 liftIO $ writeIORef (_merge_accumRef m) $! newM
1894 liftIO $ writeIORef (_merge_accumRef m) $! newM
1841 when (DMap.null oldM) $ do -- Only schedule the firing once
1895 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1850,7 +1904,7 @@ mergeSubscriber m getKey = Subscriber
1850 else liftIO $ do
1904 else liftIO $ do
1851#ifdef DEBUG_CYCLES
1905#ifdef DEBUG_CYCLES
1852 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1906 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1907+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
1854 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1908 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1855#else
1909#else
1856 let cycleInfo = ""
1910 let cycleInfo = ""
@@ -1866,7 +1920,7 @@ mergeSubscriber m getKey = Subscriber
1866 }
1920 }
1867
1921
1868--TODO: Be able to run as much of this as possible promptly
1922--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
1870updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1924updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1871 where updateMe = do
1925 where updateMe = do
1872 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1926 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1874,9 +1928,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
1874 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1928 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1875 return subscriptionsToKill
1929 return subscriptionsToKill
1876
1930
1877−{-# INLINE mergeCheap' #-}
1931+{-# INLINE mergeGCheap' #-}
1932+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
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)
1933+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1879−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1934+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1880 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1935 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1881 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1936 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1882 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1937 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1924,11 +1979,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1924 , occ
1979 , occ
1925 )
1980 )
1926
1981
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)
1928mergeInt = cacheEvent . mergeIntCheap
1983mergeInt = cacheEvent . mergeIntCheap
1929
1984
1930{-# INLINABLE mergeIntCheap #-}
1985{-# 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)
1932mergeIntCheap d = Event $ \sub -> do
1987mergeIntCheap d = Event $ \sub -> do
1933 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1988 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1934 accum <- liftIO $ FastMutableIntMap.newEmpty
1989 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2025,14 +2080,16 @@ mergeIntCheap d = Event $ \sub -> do
2025 )
2080 )
2026
2081
2027newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2082newtype 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) }
2028
2084
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2085+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2030−fan e =
2086+fanG e = unsafePerformIO $ do
2087+ ref <- newIORef Nothing
2031 let f = Fan
2088 let f = Fan
2032 { fanParent = e
2089 { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2090+ , fanSubscribed = ref
2034 }
2091 }
2035− in EventSelector $ \k -> eventFan k f
2092+ pure $ EventSelectorG $ \k -> eventFan k f
2036
2093
2037runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2094runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2038runHoldInits holdInitRef dynInitRef mergeInitRef = do
2095runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2086,18 +2143,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
2086-- | Run an event action outside of a frame
2143-- | Run an event action outside of a frame
2087runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2144runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2088runFrame a = SpiderHost $ do
2145runFrame a = SpiderHost $ do
2089− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2146+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
2090 let go = do
2147 let go = do
2091 result <- a
2148 result <- a
2092 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
2149 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
2093 return result
2150 return result
2094 result <- runEventM go
2151 result <- runEventM go
2095 toClear <- readIORef $ eventEnvClears env
2152 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
2097 toClearInt <- readIORef $ eventEnvIntClears env
2154 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
2099 toClearRoot <- readIORef $ eventEnvRootClears env
2156 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
2101 toAssign <- readIORef $ eventEnvAssignments env
2158 toAssign <- readIORef $ eventEnvAssignments env
2102 toReconnectRef <- newIORef []
2159 toReconnectRef <- newIORef []
2103 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
2160 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2297,7 +2354,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
2297 fail _ = error "Dynamic does not support 'fail'"
2354 fail _ = error "Dynamic does not support 'fail'"
2298
2355
2299{-# INLINABLE newJoinDyn #-}
2356{-# 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)
2301newJoinDyn d =
2358newJoinDyn d =
2302 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2359 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2303 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
2360 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2447,7 +2504,7 @@ unsafeNewSpiderTimelineEnv = do
2447#ifdef DEBUG
2504#ifdef DEBUG
2448 depthRef <- newIORef 0
2505 depthRef <- newIORef 0
2449#endif
2506#endif
2450− return $ SpiderTimelineEnv
2507+ return $ STE $ SpiderTimelineEnv
2451 { _spiderTimeline_lock = lock
2508 { _spiderTimeline_lock = lock
2452 , _spiderTimeline_eventEnv = env
2509 , _spiderTimeline_eventEnv = env
2453#ifdef DEBUG
2510#ifdef DEBUG
@@ -2457,19 +2514,19 @@ unsafeNewSpiderTimelineEnv = do
2457
2514
2458-- | Create a new SpiderTimelineEnv
2515-- | Create a new SpiderTimelineEnv
2459newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2516newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2460−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2517+newSpiderTimeline = withSpiderTimeline (pure . Some)
2461
2518
2462−data LocalSpiderTimeline x s
2519+data LocalSpiderTimeline (x :: Type) s
2463
2520
2464instance Reifies s (SpiderTimelineEnv x) =>
2521instance Reifies s (SpiderTimelineEnv x) =>
2465 HasSpiderTimeline (LocalSpiderTimeline x s) where
2522 HasSpiderTimeline (LocalSpiderTimeline x s) where
2466− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2523+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
2467
2524
2468localSpiderTimeline
2525localSpiderTimeline
2469− :: Proxy s
2526+ :: proxy s
2470 -> SpiderTimelineEnv x
2527 -> SpiderTimelineEnv x
2471 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2528 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2472−localSpiderTimeline _ = unsafeCoerce
2529+localSpiderTimeline _ = coerce
2473
2530
2474-- | Pass a new timeline to the given function.
2531-- | Pass a new timeline to the given function.
2475withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2532withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2477,18 +2534,18 @@ withSpiderTimeline k = do
2477 env <- unsafeNewSpiderTimelineEnv
2534 env <- unsafeNewSpiderTimelineEnv
2478 reify env $ \s -> k $ localSpiderTimeline s env
2535 reify env $ \s -> k $ localSpiderTimeline s env
2479
2536
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)
2481
2538
2482type ComputeM = EventM
2539type ComputeM = EventM
2483
2540
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)
2485
2542
2486instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2543instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2487 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2544 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2488 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2545 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2489 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2546 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)
2547+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2491− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2548+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
2492 type PullM (SpiderTimeline x) = SpiderPullM x
2549 type PullM (SpiderTimeline x) = SpiderPullM x
2493 type PushM (SpiderTimeline x) = SpiderPushM x
2550 type PushM (SpiderTimeline x) = SpiderPushM x
2494 {-# INLINABLE never #-}
2551 {-# INLINABLE never #-}
@@ -2501,10 +2558,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2501 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2558 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2502 {-# INLINABLE pull #-}
2559 {-# INLINABLE pull #-}
2503 pull = SpiderBehavior . pull . coerce
2560 pull = SpiderBehavior . pull . coerce
2504− {-# INLINABLE merge #-}
2561+ {-# INLINABLE fanG #-}
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))
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
2508 {-# INLINABLE switch #-}
2570 {-# INLINABLE switch #-}
2509 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2571 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2510 {-# INLINABLE coincidence #-}
2572 {-# INLINABLE coincidence #-}
@@ -2512,15 +2574,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2512 {-# INLINABLE current #-}
2574 {-# INLINABLE current #-}
2513 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2575 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2514 {-# INLINABLE updated #-}
2576 {-# INLINABLE updated #-}
2515− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2577+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
2516 {-# INLINABLE unsafeBuildDynamic #-}
2578 {-# INLINABLE unsafeBuildDynamic #-}
2517 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2579 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2518 {-# INLINABLE unsafeBuildIncremental #-}
2580 {-# INLINABLE unsafeBuildIncremental #-}
2519 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2581 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2520− {-# INLINABLE mergeIncremental #-}
2582+ {-# INLINABLE mergeIncrementalG #-}
2521− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2583+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2522− {-# INLINABLE mergeIncrementalWithMove #-}
2584+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2523− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2585+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
2524 {-# INLINABLE currentIncremental #-}
2586 {-# INLINABLE currentIncremental #-}
2525 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2587 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2526 {-# INLINABLE updatedIncremental #-}
2588 {-# INLINABLE updatedIncremental #-}
@@ -2531,9 +2593,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2531 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2593 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2532 eventCoercion Coercion = Coercion
2594 eventCoercion Coercion = Coercion
2533 behaviorCoercion Coercion = Coercion
2595 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
2535 {-# INLINABLE mergeIntIncremental #-}
2598 {-# 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
2537 {-# INLINABLE fanInt #-}
2600 {-# INLINABLE fanInt #-}
2538 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2601 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2539
2602
@@ -2558,7 +2621,7 @@ instance MonadAtomicRef (EventM x) where
2558 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2621 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2559
2622
2560-- | The monad for actions that manipulate a Spider timeline identified by @x@
2623-- | 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)
2562
2625
2563instance Monad (SpiderHost x) where
2626instance Monad (SpiderHost x) where
2564 {-# INLINABLE (>>=) #-}
2627 {-# INLINABLE (>>=) #-}
@@ -2580,7 +2643,7 @@ runSpiderHost (SpiderHost a) = a
2580runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2643runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2581runSpiderHostForTimeline (SpiderHost a) _ = a
2644runSpiderHostForTimeline (SpiderHost a) _ = a
2582
2645
2583−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2646+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2584 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2647 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2585
2648
2586instance Monad (SpiderHostFrame x) where
2649instance Monad (SpiderHostFrame x) where
src/Reflex/Time.hs+113−13
@@ -1,5 +1,7 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveGeneric #-}
4+{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE RecursiveDo #-}
7{-# LANGUAGE RecursiveDo #-}
@@ -9,6 +11,11 @@
9#ifdef USE_TEMPLATE_HASKELL
11#ifdef USE_TEMPLATE_HASKELL
10{-# LANGUAGE TemplateHaskell #-}
12{-# LANGUAGE TemplateHaskell #-}
11#endif
13#endif
14+-- |
15+-- Module:
16+-- Reflex.Time
17+-- Description:
18+-- Clocks, timers, and other time-related functions.
12module Reflex.Time where
19module Reflex.Time where
13
20
14import Reflex.Class
21import Reflex.Class
@@ -24,46 +31,50 @@ import Control.Monad
24import Control.Monad.Fix
31import Control.Monad.Fix
25import Control.Monad.IO.Class
32import Control.Monad.IO.Class
26import Data.Align
33import Data.Align
34+import Data.Data (Data)
27import Data.Fixed
35import Data.Fixed
36+import Data.Semigroup (Semigroup(..))
28import Data.Sequence (Seq, (|>))
37import Data.Sequence (Seq, (|>))
29import qualified Data.Sequence as Seq
38import qualified Data.Sequence as Seq
30import Data.These
39import Data.These
31import Data.Time.Clock
40import Data.Time.Clock
32import Data.Typeable
41import Data.Typeable
42+import GHC.Generics (Generic)
33import System.Random
43import System.Random
34
44
45+-- | Metadata associated with a timer "tick"
35data TickInfo
46data TickInfo
36 = TickInfo { _tickInfo_lastUTC :: UTCTime
47 = TickInfo { _tickInfo_lastUTC :: UTCTime
37 -- ^ UTC time immediately after the last tick.
48 -- ^ UTC time immediately after the last tick.
38 , _tickInfo_n :: Integer
49 , _tickInfo_n :: Integer
39− -- ^ Number of time periods since t0
50+ -- ^ Number of time periods or ticks since the start of the timer
40 , _tickInfo_alreadyElapsed :: NominalDiffTime
51 , _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.
42 }
53 }
43 deriving (Eq, Ord, Show, Typeable)
54 deriving (Eq, Ord, Show, Typeable)
44
55
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.
45−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
58+-- This is a special case of 'tickLossyFrom' that uses the post-build event to start the tick thread.
46−-- tick thread.
47tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
59tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
48tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
60tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
49
61
50−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
62+-- | Fires an 'Event' once every time provided interval elapses, approximately.
51−-- tick thread and the time of the post-build as the tick basis time.
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.
52tickLossyFromPostBuildTime :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> m (Event t TickInfo)
64tickLossyFromPostBuildTime :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> m (Event t TickInfo)
53tickLossyFromPostBuildTime dt = do
65tickLossyFromPostBuildTime dt = do
54 postBuild <- getPostBuild
66 postBuild <- getPostBuild
55 postBuildTime <- performEvent $ liftIO getCurrentTime <$ postBuild
67 postBuildTime <- performEvent $ liftIO getCurrentTime <$ postBuild
56 tickLossyFrom' $ (dt,) <$> postBuildTime
68 tickLossyFrom' $ (dt,) <$> postBuildTime
57
69
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
70+-- | Fires an 'Event' approximately each time the provided interval elapses. 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
71+-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the provided initial time.
61tickLossyFrom
72tickLossyFrom
62 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
73 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
63 => NominalDiffTime
74 => NominalDiffTime
64 -- ^ The length of a tick interval
75 -- ^ The length of a tick interval
65 -> UTCTime
76 -> 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.
67 -> Event t a
78 -> Event t a
68 -- ^ Event that starts a tick generation thread. Usually you want this to
79 -- ^ Event that starts a tick generation thread. Usually you want this to
69 -- be something like the result of getPostBuild that only fires once. But
80 -- be something like the result of getPostBuild that only fires once. But
@@ -71,12 +82,12 @@ tickLossyFrom
71 -> m (Event t TickInfo)
82 -> m (Event t TickInfo)
72tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e
83tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e
73
84
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'.
75tickLossyFrom'
86tickLossyFrom'
76 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
87 :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
77 => Event t (NominalDiffTime, UTCTime)
88 => Event t (NominalDiffTime, UTCTime)
78 -- ^ Event that starts a tick generation thread. Usually you want this to
89 -- ^ 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
80 -- there could be uses for starting multiple timer threads.
91 -- there could be uses for starting multiple timer threads.
81 -> m (Event t TickInfo)
92 -> m (Event t TickInfo)
82tickLossyFrom' e = do
93tickLossyFrom' e = do
@@ -87,12 +98,16 @@ tickLossyFrom' e = do
87 Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000
98 Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000
88 cb (tick, pair)
99 cb (tick, pair)
89
100
101+-- | Like 'tickLossy', but immediately calculates the first tick and provides a 'Dynamic' that is updated as ticks fire.
90clockLossy :: (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)
102clockLossy :: (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)
91clockLossy dt t0 = do
103clockLossy dt t0 = do
92 initial <- liftIO $ getCurrentTick dt t0
104 initial <- liftIO $ getCurrentTick dt t0
93 e <- tickLossy dt t0
105 e <- tickLossy dt t0
94 holdDyn initial e
106 holdDyn initial e
95
107
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.
96getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo
111getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo
97getCurrentTick dt t0 = do
112getCurrentTick dt t0 = do
98 t <- getCurrentTime
113 t <- getCurrentTime
@@ -266,6 +281,92 @@ throttle t e = do
266 delayed <- delay t outE
281 delayed <- delay t outE
267 return outE
282 return outE
268
283
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+
269#ifdef USE_TEMPLATE_HASKELL
370#ifdef USE_TEMPLATE_HASKELL
270makeLensesWith (lensRules & simpleLenses .~ True) ''TickInfo
371makeLensesWith (lensRules & simpleLenses .~ True) ''TickInfo
271#else
372#else
@@ -281,4 +382,3 @@ tickInfo_alreadyElapsed :: Lens' TickInfo NominalDiffTime
281tickInfo_alreadyElapsed f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 x2 y) <$> f x3
382tickInfo_alreadyElapsed f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 x2 y) <$> f x3
282{-# INLINE tickInfo_alreadyElapsed #-}
383{-# INLINE tickInfo_alreadyElapsed #-}
283#endif
384#endif
284−
src/Reflex/TriggerEvent/Class.hs−1
@@ -2,7 +2,6 @@
2-- new 'Event's that can be triggered from 'IO'.
2-- new 'Event's that can be triggered from 'IO'.
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
7module Reflex.TriggerEvent.Class
6module Reflex.TriggerEvent.Class
8 ( TriggerEvent (..)
7 ( TriggerEvent (..)
src/Reflex/Workflow.hs+13−1
@@ -1,7 +1,13 @@
1{-# LANGUAGE RecursiveDo #-}
1{-# LANGUAGE RecursiveDo #-}
2{-# LANGUAGE ScopedTypeVariables #-}
2{-# 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."
3module Reflex.Workflow (
10module Reflex.Workflow (
4− -- * Workflows
5 Workflow (..)
11 Workflow (..)
6 , workflow
12 , workflow
7 , workflowView
13 , workflowView
@@ -18,21 +24,27 @@ import Reflex.Network
18import Reflex.NotReady.Class
24import Reflex.NotReady.Class
19import Reflex.PostBuild.Class
25import Reflex.PostBuild.Class
20
26
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.
21newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
29newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
22
30
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).
23workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
32workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
24workflow w0 = do
33workflow w0 = do
25 rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
34 rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
26 return $ fmap fst eResult
35 return $ fmap fst eResult
27
36
37+-- | Similar to 'workflow', but outputs an 'Event' that fires whenever the current 'Workflow' is replaced by the next 'Workflow'.
28workflowView :: 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)
38workflowView :: 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)
29workflowView w0 = do
39workflowView w0 = do
30 rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
40 rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
31 eReplace <- fmap switch $ hold never $ fmap snd eResult
41 eReplace <- fmap switch $ hold never $ fmap snd eResult
32 return $ fmap fst eResult
42 return $ fmap fst eResult
33
43
44+-- | Map a function over a 'Workflow', possibly changing the return type.
34mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
45mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
35mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
46mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
36
47
48+-- | Map a "cheap" function over a 'Workflow'. Refer to the documentation for 'pushCheap' for more information and performance considerations.
37mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
49mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
38mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
50mapWorkflowCheap 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 #-}
1{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
3{-# LANGUAGE RecursiveDo #-}
5{-# LANGUAGE RecursiveDo #-}
@@ -12,6 +14,10 @@ import Data.Functor.Misc
12import qualified Data.Map as M
14import qualified Data.Map as M
13import Data.These
15import Data.These
14
16
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+
15import Reflex
21import Reflex
16import Reflex.EventWriter.Base
22import Reflex.EventWriter.Base
17import Test.Run
23import Test.Run
@@ -36,6 +42,9 @@ main = do
36 print os4
42 print os4
37 os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
43 os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
38 print os5
44 print os5
45+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
46+ print os6
47+ let ![[Nothing, Nothing]] = os6
39 return ()
48 return ()
40
49
41unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
50unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
@@ -44,16 +53,13 @@ unwrapApp x appIn = do
44 return e
53 return e
45
54
46testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
55testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
47−testOrdering pulse = do
48− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
49− return ()
50
57
51testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
58testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
52testSimultaneous pulse = do
59testSimultaneous pulse = do
53 let e0 = fmapMaybe (^? here) pulse
60 let e0 = fmapMaybe (^? here) pulse
54 e1 = fmapMaybe (^? there) pulse
61 e1 = fmapMaybe (^? there) pulse
55 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
62 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
56− return ()
57
63
58-- | Test that a widget telling and event which fires at the same time it has been replaced
64-- | Test that a widget telling and event which fires at the same time it has been replaced
59-- doesn't count along with the new widget.
65-- doesn't count along with the new widget.
@@ -112,3 +118,17 @@ testLiveTellEventDMap pulse = do
112 (mapToDMap $ M.singleton 1 ())
118 (mapToDMap $ M.singleton 1 ())
113 ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
119 ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
114 return ()
120 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
27
27
28import System.Exit
28import System.Exit
29import System.Mem
29import System.Mem
30+import Data.Coerce
30
31
31main :: IO ()
32main :: IO ()
32main = do
33main = do
@@ -46,7 +47,7 @@ hostPerf ref = S.runSpiderHost $ do
46 eventToPerform <- Host.runHostFrame $ do
47 eventToPerform <- Host.runHostFrame $ do
47 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
48 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
48 <- S.SpiderHostFrame
49 <- S.SpiderHostFrame
49− $ fmap ( S.merge
50+ $ fmap ( S.mergeG coerce
50 . S.dynamicHold)
51 . S.dynamicHold)
51 $ S.hold DMap.empty
52 $ S.hold DMap.empty
52 -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
53 -- 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
55 { S.subscriberPropagate = S.subscriberPropagate sub
56 { S.subscriberPropagate = S.subscriberPropagate sub
56 }
57 }
57 return (s, o))
58 return (s, o))
58− $ runIdentity <$> S.select
59+ $ runIdentity . runIdentity <$> S.selectG
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
60+ (S.fanG $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
60 (WrapArg Request)
61 (WrapArg Request)
61 return $ alignWith (mergeThese (<>))
62 return $ alignWith (mergeThese (<>))
62 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
63 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
test/QueryT.hs+4
@@ -19,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
19import Data.Semigroup
19import Data.Semigroup
20import Data.These
20import Data.These
21
21
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+
22import Reflex
26import Reflex
23import Reflex.Patch.MapWithMove
27import Reflex.Patch.MapWithMove
24import Test.Run
28import Test.Run
test/Reflex/Bench/Focused.hs+12
@@ -130,6 +130,16 @@ mapDynChain = iterM (return . fmap (+1))
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132
132
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+
133combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
143combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
144combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135
145
@@ -308,6 +318,8 @@ dynamics :: Word -> [(String, TestCase)]
308dynamics n =
318dynamics n =
309 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
319 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
320 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
321+ , testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d
322+ , testE "buildDynChain" $ fmap updated $ buildDynChain n =<< d
311 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
323 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
324 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
325 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
test/RequesterT.hs+116−9
@@ -1,15 +1,25 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
2{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
3{-# LANGUAGE RankNTypes #-}
5{-# LANGUAGE RankNTypes #-}
6+{-# LANGUAGE RecursiveDo #-}
4{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
5module Main where
8module Main where
6
9
7import Control.Lens
10import Control.Lens
8import Control.Monad
11import Control.Monad
12+import Control.Monad.Fix
9import qualified Data.Dependent.Map as DMap
13import qualified Data.Dependent.Map as DMap
10import Data.Dependent.Sum
14import Data.Dependent.Sum
15+import Data.Functor.Misc
16+import qualified Data.Map as M
11import Data.These
17import Data.These
12
18
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+
13import Reflex
23import Reflex
14import Reflex.Requester.Base
24import Reflex.Requester.Base
15import Reflex.Requester.Class
25import Reflex.Requester.Class
@@ -20,18 +30,31 @@ data RequestInt a where
20
30
21main :: IO ()
31main :: IO ()
22main = do
32main = do
23− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
33+ os1 <- runApp' (unwrapApp testOrdering) $
24 [ Just ()
34 [ Just ()
25 ]
35 ]
26 print os1
36 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 $
37+ os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
29− [ This ()
38+ [ This ()
30− , That ()
39+ , That ()
31− , This ()
40+ , This ()
32− , These () ()
41+ , These () ()
33− ]
42+ ]
34 print os2
43 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
35 return ()
58 return ()
36
59
37unwrapRequest :: DSum tag RequestInt -> Int
60unwrapRequest :: DSum tag RequestInt -> Int
@@ -43,7 +66,7 @@ unwrapApp :: ( Reflex t, Monad m )
43 -> m (Event t [Int])
66 -> m (Event t [Int])
44unwrapApp x appIn = do
67unwrapApp x appIn = do
45 ((), e) <- runRequesterT (x appIn) never
68 ((), e) <- runRequesterT (x appIn) never
46− return $ fmap (map unwrapRequest . DMap.toList) e
69+ return $ fmap (map unwrapRequest . requesterDataToList) e
47
70
48testOrdering :: ( Response m ~ Identity
71testOrdering :: ( Response m ~ Identity
49 , Request m ~ RequestInt
72 , Request m ~ RequestInt
@@ -65,3 +88,87 @@ testSimultaneous pulse = do
65 switchE = fmapMaybe (^? there) pulse
88 switchE = fmapMaybe (^? there) pulse
66 forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
89 forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
67 requestingIdentity (RequestInt (i+1) <$ tellE)
90 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
22 , "--ignore=Use unless"
22 , "--ignore=Use unless"
23 , "--ignore=Reduce duplication"
23 , "--ignore=Reduce duplication"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
25 ]
26 ]
26 recurseInto = and <$> sequence
27 recurseInto = and <$> sequence
27 [ fileType ==? Directory
28 [ fileType ==? Directory