on develop help sign in
Merge branch 'develop' into develop
5f1754fJohn Ericson
.travis.yml+165−10
@@ -1,16 +1,171 @@
1−# Cache Buster: 0
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+#
2−language: nix
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
3cache:
15cache:
4 directories:
16 directories:
5− - /nix
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
6matrix:
28matrix:
7 include:
29 include:
8− - env: COMPILER=ghc7103
30+ - compiler: ghcjs-8.4
9− - env: COMPILER=ghc821
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
10− - env: COMPILER=ghc802
32+ - compiler: ghc-8.6.5
11− - env: COMPILER=ghcjs
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
12script:
131script:
13− - set -e
132+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
14− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
133+ # Packaging...
15− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
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
16
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+63
@@ -0,0 +1,63 @@
1+# Revision history for reflex
2+
3+## Unreleased
4+
5+* Data.WeakBag.traverse and Data.FastWeakBag.traverse have been
6+ renamed to Data.WeakBag.traverse_ and Data.FastWeakBag.traverse_
7+ respectively.
8+
9+## 0.6.2.4
10+
11+* Update to monoidal-containers 0.6
12+
13+## 0.6.2.3
14+
15+* Add an upper-bound to witherable
16+
17+## 0.6.2.2
18+
19+* Support these >= 1. Add `split-these` flag to control whether to use new these/semialign combination or not.
20+* Update version bounds to fix some CI failures
21+* Add travis CI configuration
22+
23+## 0.6.2.1
24+
25+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
26+ functor:
27+ * `fan` to `fanG`
28+ * `EventSelectorG` for `fanG` result selector.
29+
30+* Reduce the amount of unsafeCoerce in coercing newtypes under Event/Dynamic/Behavior.
31+ * Add fused ReaderIO for the purpose of coercion (ReaderT's third argument has nominal role preventing automated coerce)
32+ * Add incrementalCoercion/coerceIncremental to go with dynamicCoercion/coerceDynamic
33+
34+* Generalize merging functions:
35+ `merge` to `mergeG`,
36+ `mergeIncremental` to `mergeIncrementalG`,
37+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
38+
39+* Generalize distribute function:
40+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
41+
42+## 0.6.2.0
43+
44+* Fix `holdDyn` so that it is lazy in its event argument
45+ These produce `DMap`s whose values needn't be `Identity`.
46+* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
47+* Fix `holdDyn` so that it is lazy in its event argument.
48+
49+## 0.6.1.0
50+
51+* Re-export all of `Data.Map.Monoidal`
52+* Fix `QueryT` and `RequesterT` tests
53+
54+## 0.6.0.0 -- 2019-03-20
55+
56+* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
57+* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
58+* Remove many deprecated functions.
59+* Add a `Num` instance for `Dynamic`.
60+* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
61+* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
62+* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
63+* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
Quickref.md+24−7
@@ -57,15 +57,15 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
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.
@@ -292,3 +292,20 @@ Th typeclasses and their associated annotations include:
292[P,T] delay :: NominalDiffTime -> Event t a -> m (Event t a)
292[P,T] delay :: NominalDiffTime -> Event t a -> m (Event t a)
293```
293```
294
294
295+## Networks
296+
297+```haskell
298+-- Functions from Reflex.Network used to deal with Dynamics/Events carrying (m a)
299+
300+-- Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
301+-- The returned Event of network results occurs when the Dynamic does. Note: Often, the type a is an Event,
302+-- in which case the return value is an Event-of-Events that would typically be flattened (via switchHold).
303+[P,A] networkView :: Dynamic (m a) -> m (Event a)
304+
305+-- Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the
306+-- Event fires. The returned Dynamic of network results occurs when the Event does. Note: Often, the type a is an
307+-- Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
308+[H,A] networkHold :: m a -> Event (m a) -> m (Dynamic a)
309+
310+-- Render a placeholder network to be shown while another network is not yet done building
311+[P,A] untilReady :: m a -> m b -> m (a, Event b)
README.md+13−7
@@ -1,19 +1,25 @@
1−## Reflex
1+## [Reflex](https://reflex-frp.org/)
2+
3+[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex)
4+
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+18−8
@@ -6,13 +6,19 @@
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+, splitThese ? (semialign != null), semialign ? null, these-lens ? 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.4";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
17+ "default.nix"
18+ "release.nix"
19+ ".git"
20+ "dist"
21+ ])) ./.;
16 libraryHaskellDepends = [
22 libraryHaskellDepends = [
17 base bifunctors containers dependent-map dependent-sum
23 base bifunctors containers dependent-map dependent-sum
18 exception-transformers lens
24 exception-transformers lens
@@ -21,18 +27,22 @@ mkDerivation {
21 transformers-compat prim-uniq
27 transformers-compat prim-uniq
22 base bifunctors containers deepseq dependent-map dependent-sum
28 base bifunctors containers deepseq dependent-map dependent-sum
23 mtl ref-tf split transformers data-default
29 mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
30+ random time unbounded-delays monoidal-containers witherable
31+ profunctors
25 ] ++ (if ghc.isGhcjs or false then [
32 ] ++ (if ghc.isGhcjs or false then [
26 ghcjs-base
33 ghcjs-base
27 ] else []) ++ (if !useTemplateHaskell then [] else [
34 ] else []) ++ (if !useTemplateHaskell then [] else [
28 haskell-src-exts haskell-src-meta
35 haskell-src-exts haskell-src-meta
29− ]);
36+ ]) ++ (if splitThese then [
37+ semialign
38+ these-lens
39+ ] else []);
30 testHaskellDepends = if ghc.isGhcjs or false then [] else [
40 testHaskellDepends = if ghc.isGhcjs or false then [] else [
31 hlint filepath directory filemanip
41 hlint filepath directory filemanip
32 ];
42 ];
33− configureFlags = if useTemplateHaskell then [] else [
43+ configureFlags =
34− "-f-use-template-haskell"
44+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
35− ];
45+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
36 homepage = "https://github.com/reflex-frp/reflex";
46 homepage = "https://github.com/reflex-frp/reflex";
37 description = "Higher-order Functional Reactive Programming";
47 description = "Higher-order Functional Reactive Programming";
38 license = stdenv.lib.licenses.bsd3;
48 license = stdenv.lib.licenses.bsd3;
dep/reflex-platform/default.nixnew file+7
@@ -0,0 +1,7 @@
1+# DO NOT HAND-EDIT THIS FILE
2+import ((import <nixpkgs> {}).fetchFromGitHub (
3+ let json = builtins.fromJSON (builtins.readFile ./github.json);
4+ in { inherit (json) owner repo rev sha256;
5+ private = json.private or false;
6+ }
7+))
dep/reflex-platform/github.jsonnew file+7
@@ -0,0 +1,7 @@
1+{
2+ "owner": "reflex-frp",
3+ "repo": "reflex-platform",
4+ "branch": "jailbreakTheselens",
5+ "rev": "4284ed527c96c373538dc7e31776d9a50ca6aa91",
6+ "sha256": "1m1nkxq7ng9wnqbd77xn8d81f2iz8g61y5m0r3gp0gi0q59jm8ay"
7+}
reflex.cabal+76−34
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.5
2+Version: 0.6.2.4
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
@@ -12,6 +12,14 @@ Build-type: Simple
12Cabal-version: >=1.9.2
12Cabal-version: >=1.9.2
13homepage: https://reflex-frp.org
13homepage: https://reflex-frp.org
14bug-reports: https://github.com/reflex-frp/reflex/issues
14bug-reports: https://github.com/reflex-frp/reflex/issues
15+extra-source-files:
16+ README.md
17+ Quickref.md
18+ ChangeLog.md
19+
20+tested-with:
21+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5,
22+ GHCJS ==8.4
15
23
16flag use-reflex-optimizer
24flag use-reflex-optimizer
17 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.
@@ -33,37 +41,51 @@ flag fast-weak
33 default: False
41 default: False
34 manual: True
42 manual: True
35
43
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
36library
49library
37 hs-source-dirs: src
50 hs-source-dirs: src
38 build-depends:
51 build-depends:
39 MemoTrie == 0.6.*,
52 MemoTrie == 0.6.*,
40− base >= 4.7 && < 4.13,
53+ base >= 4.9 && < 4.13,
41 bifunctors >= 5.2 && < 5.6,
54 bifunctors >= 5.2 && < 5.6,
42− comonad,
55+ comonad >= 5.0.4 && < 5.1,
56+ constraints-extras >= 0.3 && < 0.4,
43− containers >= 0.5 && < 0.7,
57+ containers >= 0.6 && < 0.7,
44 data-default >= 0.5 && < 0.8,
58 data-default >= 0.5 && < 0.8,
45− dependent-map >= 0.2.4 && < 0.3,
59+ dependent-map >= 0.3 && < 0.4,
46 exception-transformers == 0.4.*,
60 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
47 lens >= 4.7 && < 5,
62 lens >= 4.7 && < 5,
48 monad-control >= 1.0.1 && < 1.1,
63 monad-control >= 1.0.1 && < 1.1,
49− monoidal-containers == 0.5.*,
50 mtl >= 2.1 && < 2.3,
64 mtl >= 2.1 && < 2.3,
51 prim-uniq >= 0.1.0.1 && < 0.2,
65 prim-uniq >= 0.1.0.1 && < 0.2,
52− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
53 random == 1.1.*,
68 random == 1.1.*,
54 ref-tf == 0.4.*,
69 ref-tf == 0.4.*,
55 reflection == 2.1.*,
70 reflection == 2.1.*,
56 semigroupoids >= 4.0 && < 6,
71 semigroupoids >= 4.0 && < 6,
57− semigroups >= 0.16 && < 0.19,
58 stm >= 2.4 && < 2.6,
72 stm >= 2.4 && < 2.6,
59 syb >= 0.5 && < 0.8,
73 syb >= 0.5 && < 0.8,
60− these >= 0.4 && < 0.8,
61 time >= 1.4 && < 1.9,
74 time >= 1.4 && < 1.9,
62− transformers >= 0.2,
75+ transformers >= 0.5.6.0 && < 0.6,
63− transformers-compat >= 0.3,
64− unbounded-delays >= 0.1.0.0 && < 0.2
76+ unbounded-delays >= 0.1.0.0 && < 0.2,
77+ witherable >= 0.3 && < 0.3.2
78+
79+ if flag(split-these)
80+ build-depends: these >= 1 && <1.1,
81+ semialign >=1 && <1.2,
82+ monoidal-containers >= 0.6 && < 0.7
83+ else
84+ build-depends: these >= 0.4 && <0.9,
85+ monoidal-containers == 0.4.0.0
65
86
66 exposed-modules:
87 exposed-modules:
88+ Control.Monad.ReaderIO
67 Data.AppendMap,
89 Data.AppendMap,
68 Data.FastMutableIntMap,
90 Data.FastMutableIntMap,
69 Data.FastWeakBag,
91 Data.FastWeakBag,
@@ -73,6 +95,8 @@ library
73 Reflex,
95 Reflex,
74 Reflex.Class,
96 Reflex.Class,
75 Reflex.Adjustable.Class,
97 Reflex.Adjustable.Class,
98+ Reflex.BehaviorWriter.Base,
99+ Reflex.BehaviorWriter.Class,
76 Reflex.Collection,
100 Reflex.Collection,
77 Reflex.Dynamic,
101 Reflex.Dynamic,
78 Reflex.Dynamic.Uniq,
102 Reflex.Dynamic.Uniq,
@@ -126,8 +150,8 @@ library
126 if flag(use-template-haskell)
150 if flag(use-template-haskell)
127 cpp-options: -DUSE_TEMPLATE_HASKELL
151 cpp-options: -DUSE_TEMPLATE_HASKELL
128 build-depends:
152 build-depends:
129− dependent-sum >= 0.3 && < 0.5,
153+ dependent-sum >= 0.6 && < 0.7,
130− haskell-src-exts >= 1.16 && < 1.21,
154+ haskell-src-exts >= 1.16 && < 1.22,
131 haskell-src-meta >= 0.6 && < 0.9,
155 haskell-src-meta >= 0.6 && < 0.9,
132 template-haskell >= 2.9 && < 2.15
156 template-haskell >= 2.9 && < 2.15
133 exposed-modules:
157 exposed-modules:
@@ -135,7 +159,7 @@ library
135 other-extensions: TemplateHaskell
159 other-extensions: TemplateHaskell
136 else
160 else
137 build-depends:
161 build-depends:
138− dependent-sum == 0.4.*
162+ dependent-sum >= 0.6 && < 0.7
139
163
140 if flag(fast-weak) && impl(ghcjs)
164 if flag(fast-weak) && impl(ghcjs)
141 cpp-options: -DGHCJS_FAST_WEAK
165 cpp-options: -DGHCJS_FAST_WEAK
@@ -152,14 +176,14 @@ test-suite semantics
152 base,
176 base,
153 bifunctors,
177 bifunctors,
154 containers,
178 containers,
155− deepseq >= 1.3 && < 1.5,
179+ deepseq,
156 dependent-map,
180 dependent-map,
157 dependent-sum,
181 dependent-sum,
158 mtl,
182 mtl,
159 ref-tf,
183 ref-tf,
160 reflex,
184 reflex,
161 split,
185 split,
162− transformers >= 0.3
186+ transformers
163 other-modules:
187 other-modules:
164 Reflex.Bench.Focused
188 Reflex.Bench.Focused
165 Reflex.Plan.Pure
189 Reflex.Plan.Pure
@@ -178,7 +202,7 @@ test-suite CrossImpl
178 containers,
202 containers,
179 dependent-map,
203 dependent-map,
180 dependent-sum,
204 dependent-sum,
181− deepseq >= 1.3 && < 1.5,
205+ deepseq,
182 mtl,
206 mtl,
183 transformers,
207 transformers,
184 ref-tf,
208 ref-tf,
@@ -197,7 +221,7 @@ test-suite hlint
197 , directory
221 , directory
198 , filepath
222 , filepath
199 , filemanip
223 , filemanip
200− , hlint
224+ , hlint < 2.1 || >= 2.2.2
201 if impl(ghcjs)
225 if impl(ghcjs)
202 buildable: False
226 buildable: False
203
227
@@ -207,7 +231,8 @@ test-suite EventWriterT
207 hs-source-dirs: test
231 hs-source-dirs: test
208 build-depends: base
232 build-depends: base
209 , containers
233 , containers
210− , deepseq >= 1.3 && < 1.5
234+ , deepseq
235+ , dependent-map
211 , dependent-sum
236 , dependent-sum
212 , lens
237 , lens
213 , mtl
238 , mtl
@@ -215,6 +240,10 @@ test-suite EventWriterT
215 , transformers
240 , transformers
216 , reflex
241 , reflex
217 , ref-tf
242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
218 other-modules:
247 other-modules:
219 Reflex.Test
248 Reflex.Test
220 Reflex.TestPlan
249 Reflex.TestPlan
@@ -222,22 +251,30 @@ test-suite EventWriterT
222 Reflex.Plan.Pure
251 Reflex.Plan.Pure
223 Test.Run
252 Test.Run
224
253
254+
225test-suite RequesterT
255test-suite RequesterT
226 type: exitcode-stdio-1.0
256 type: exitcode-stdio-1.0
227 main-is: RequesterT.hs
257 main-is: RequesterT.hs
228 hs-source-dirs: test
258 hs-source-dirs: test
229 build-depends: base
259 build-depends: base
260+ , containers
261+ , deepseq
230 , dependent-sum
262 , dependent-sum
231 , dependent-map
263 , dependent-map
232 , lens
264 , lens
265+ , mtl
233 , these
266 , these
234 , transformers
267 , transformers
235 , reflex
268 , reflex
236 , ref-tf
269 , ref-tf
237− buildable: False
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
238 other-modules:
274 other-modules:
239 Reflex.TestPlan
275 Reflex.TestPlan
240 Reflex.Plan.Pure
276 Reflex.Plan.Pure
277+ Test.Run
241
278
242test-suite QueryT
279test-suite QueryT
243 type: exitcode-stdio-1.0
280 type: exitcode-stdio-1.0
@@ -247,15 +284,18 @@ test-suite QueryT
247 , containers
284 , containers
248 , dependent-map
285 , dependent-map
249 , dependent-sum
286 , dependent-sum
250− , deepseq >= 1.3 && < 1.5
287+ , deepseq
251 , lens
288 , lens
252 , monoidal-containers
289 , monoidal-containers
253 , mtl
290 , mtl
254 , ref-tf
291 , ref-tf
255 , reflex
292 , reflex
256− , semigroups
257 , these
293 , these
258 , transformers
294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
259 other-modules:
299 other-modules:
260 Test.Run
300 Test.Run
261 Reflex.TestPlan
301 Reflex.TestPlan
@@ -270,14 +310,16 @@ test-suite GC-Semantics
270 , containers
310 , containers
271 , dependent-sum
311 , dependent-sum
272 , dependent-map
312 , dependent-map
273− , deepseq >= 1.3 && < 1.5
313+ , deepseq
274 , mtl
314 , mtl
275 , these
315 , these
276 , transformers
316 , transformers
277 , reflex
317 , reflex
278 , ref-tf
318 , ref-tf
279− if impl(ghc < 8)
319+
320+ if flag(split-these)
280− build-depends: semigroups
321+ build-depends: semialign
322+
281 other-modules:
323 other-modules:
282 Reflex.Plan.Pure
324 Reflex.Plan.Pure
283 Reflex.Plan.Reflex
325 Reflex.Plan.Reflex
@@ -290,7 +332,7 @@ test-suite rootCleanup
290 hs-source-dirs: test
332 hs-source-dirs: test
291 build-depends: base
333 build-depends: base
292 , containers
334 , containers
293− , deepseq >= 1.3 && < 1.5
335+ , deepseq
294 , dependent-sum
336 , dependent-sum
295 , mtl
337 , mtl
296 , reflex
338 , reflex
@@ -309,8 +351,8 @@ benchmark spider-bench
309 build-depends:
351 build-depends:
310 base,
352 base,
311 containers,
353 containers,
312− criterion >= 1.1 && < 1.6,
354+ criterion,
313− deepseq >= 1.3 && < 1.5,
355+ deepseq,
314 dependent-map,
356 dependent-map,
315 dependent-sum,
357 dependent-sum,
316 ref-tf,
358 ref-tf,
@@ -319,7 +361,7 @@ benchmark spider-bench
319 reflex,
361 reflex,
320 split,
362 split,
321 stm,
363 stm,
322− transformers >= 0.3
364+ transformers
323 other-modules:
365 other-modules:
324 Reflex.TestPlan
366 Reflex.TestPlan
325 Reflex.Plan.Reflex
367 Reflex.Plan.Reflex
@@ -333,9 +375,9 @@ benchmark saulzar-bench
333 ghc-options: -Wall -O2 -rtsopts -threaded
375 ghc-options: -Wall -O2 -rtsopts -threaded
334 build-depends:
376 build-depends:
335 base,
377 base,
336− containers >= 0.5 && < 0.7,
378+ containers,
337− criterion >= 1.1 && < 1.6,
379+ criterion,
338− deepseq >= 1.3 && < 1.5,
380+ deepseq,
339 dependent-map,
381 dependent-map,
340 dependent-sum,
382 dependent-sum,
341 loch-th,
383 loch-th,
@@ -347,7 +389,7 @@ benchmark saulzar-bench
347 split,
389 split,
348 stm,
390 stm,
349 time,
391 time,
350− transformers >= 0.3
392+ transformers
351 other-modules:
393 other-modules:
352 Reflex.TestPlan
394 Reflex.TestPlan
353 Reflex.Plan.Reflex
395 Reflex.Plan.Reflex
release.nixnew file+38
@@ -0,0 +1,38 @@
1+{ reflex-platform-fun ? import ./dep/reflex-platform
2+}:
3+
4+let
5+ native-reflex-platform = reflex-platform-fun {};
6+ inherit (native-reflex-platform.nixpkgs) lib;
7+ systems = ["x86_64-linux" "x86_64-darwin"];
8+
9+ perPlatform = lib.genAttrs systems (system: let
10+ reflex-platform = reflex-platform-fun { inherit system; };
11+ compilers = [
12+ "ghc"
13+ "ghcjs"
14+ ] ++ lib.optionals (reflex-platform.androidSupport) [
15+ "ghcAndroidAarch64"
16+ "ghcAndroidAarch32"
17+ ] ++ lib.optionals (reflex-platform.iosSupport) [
18+ "ghcIosAarch64"
19+ ];
20+ hsPkgs = lib.genAttrs compilers (ghc: let
21+ ghc' = reflex-platform.${ghc}.override {
22+ overrides = self: super: {
23+ reflex-dontUseTemplateHaskell = self.callPackage ./. { useTemplateHaskell = false; };
24+ reflex = self.callPackage ./. { useTemplateHaskell = true; };
25+ };
26+ };
27+ in {
28+ inherit (ghc') reflex reflex-dontUseTemplateHaskell;
29+ });
30+ in hsPkgs // {
31+ cache = reflex-platform.pinBuildInputs "reflex-${system}"
32+ (lib.concatLists (map builtins.attrValues (builtins.attrValues hsPkgs)));
33+ });
34+
35+ metaCache = native-reflex-platform.pinBuildInputs "reflex-everywhere"
36+ (map (a: a.cache) (builtins.attrValues perPlatform));
37+
38+in perPlatform // { inherit metaCache; }
src/Control/Monad/ReaderIO.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# language RoleAnnotations #-}
2+{-# language MultiParamTypeClasses #-}
3+{-# language FlexibleInstances #-}
4+{-# language CPP #-}
5+module Control.Monad.ReaderIO
6+ (
7+ ReaderIO (..)
8+ )
9+ where
10+
11+import Control.Monad.Fix
12+#if MIN_VERSION_base(4,10,0)
13+import Control.Applicative
14+#endif
15+import Control.Monad
16+import Control.Monad.Reader.Class
17+import Control.Monad.IO.Class
18+
19+-- | An approximate clone of @RIO@ from the @rio@ package, but not based on
20+-- @ReaderT@. The trouble with @ReaderT@ is that its third type argument has a
21+-- @nominal@ role, so we can't coerce through it when it's wrapped in some
22+-- other @data@ type. Ugh.
23+newtype ReaderIO e a = ReaderIO { runReaderIO :: e -> IO a }
24+type role ReaderIO representational representational
25+
26+instance Functor (ReaderIO e) where
27+ fmap = liftM
28+ {-# INLINE fmap #-}
29+ a <$ m = m >> pure a
30+ {-# INLINE (<$) #-}
31+
32+instance Applicative (ReaderIO e) where
33+ pure a = ReaderIO $ \_ -> pure a
34+ {-# INLINE pure #-}
35+ (<*>) = ap
36+ {-# INLINE (<*>) #-}
37+#if MIN_VERSION_base(4,10,0)
38+ liftA2 = liftM2
39+ {-# INLINE liftA2 #-}
40+#endif
41+ (*>) = (>>)
42+ {-# INLINE (*>) #-}
43+
44+instance Monad (ReaderIO e) where
45+ ReaderIO q >>= f = ReaderIO $ \e -> q e >>= \a -> runReaderIO (f a) e
46+ {-# INLINE (>>=) #-}
47+
48+instance MonadFix (ReaderIO e) where
49+ mfix f = ReaderIO $ \e -> mfix $ \r -> runReaderIO (f r) e
50+ {-# INLINE mfix #-}
51+
52+instance MonadIO (ReaderIO e) where
53+ liftIO m = ReaderIO $ \_ -> m
54+ {-# INLINE liftIO #-}
55+
56+instance MonadReader e (ReaderIO e) where
57+ ask = ReaderIO pure
58+ {-# INLINE ask #-}
59+ local f (ReaderIO m) = ReaderIO (m . f)
60+ {-# INLINE local #-}
src/Data/AppendMap.hs+22−19
@@ -1,21 +1,20 @@
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,28 @@ 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 (..))
35+import qualified Data.Map.Monoidal as MonoidalMap
36−import Reflex.Patch (Additive, Group (..))
36+
37
37
38{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
38{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
39+-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
39type AppendMap = MonoidalMap
40type AppendMap = MonoidalMap
40
41
41{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
42{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
43+-- | A synonym for 'getMonoidalMap'
42_unAppendMap :: MonoidalMap k v -> Map k v
44_unAppendMap :: MonoidalMap k v -> Map k v
43_unAppendMap = getMonoidalMap
45_unAppendMap = getMonoidalMap
44
46
47+-- | Pattern synonym for 'MonoidalMap'
45pattern AppendMap :: Map k v -> MonoidalMap k v
48pattern AppendMap :: Map k v -> MonoidalMap k v
46pattern AppendMap m = MonoidalMap m
49pattern AppendMap m = MonoidalMap m
47
50
51+#if !MIN_VERSION_witherable(0,3,2)
48−instance FunctorMaybe (MonoidalMap k) where
52+instance W.Filterable (MonoidalMap k) where
49− fmapMaybe = mapMaybe
53+ mapMaybe = MonoidalMap.mapMaybe
54+#endif
50
55
51-- | Deletes a key, returning 'Nothing' if the result is empty.
56-- | Deletes a key, returning 'Nothing' if the result is empty.
52nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
57nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
@@ -56,24 +61,22 @@ nonEmptyDelete k vs =
56 then Nothing
61 then Nothing
57 else Just deleted
62 else Just deleted
58
63
64+-- | Like 'mapMaybe' but indicates whether the resulting container is empty
59mapMaybeNoNull :: (a -> Maybe b)
65mapMaybeNoNull :: (a -> Maybe b)
60 -> MonoidalMap token a
66 -> MonoidalMap token a
61 -> Maybe (MonoidalMap token b)
67 -> Maybe (MonoidalMap token b)
62mapMaybeNoNull f as =
68mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
69+ let bs = mapMaybe f as
64 in if null bs
70 in if null bs
65 then Nothing
71 then Nothing
66 else Just bs
72 else Just bs
67
73
68-- TODO: Move instances to `Reflex.Patch`
74-- TODO: Move instances to `Reflex.Patch`
69−instance (Ord k, Group q) => Group (MonoidalMap k q) where
75+-- | 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
76showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
75showTree = coerce (Map.showTree :: Map k a -> String)
77showTree = coerce (Map.showTree :: Map k a -> String)
76
78
79+-- | 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
80showTreeWith :: 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)
81showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
79
82
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+12−7
@@ -18,6 +18,7 @@ module Data.FastWeakBag
18 , isEmpty
18 , isEmpty
19 , insert
19 , insert
20 , traverse
20 , traverse
21+ , traverse_
21 , remove
22 , remove
22 -- * Internal functions
23 -- * Internal functions
23 -- These will not always be available.
24 -- These will not always be available.
@@ -42,15 +43,15 @@ import Data.IORef
42import System.Mem.Weak
43import System.Mem.Weak
43#endif
44#endif
44
45
45−-- | A @FastWeakBag a@ holds a set of values of type @a@, but does not retain them -
46+-- | A 'FastWeakBag' holds a set of values of type @/a/@, but does not retain them -
46−-- that is, they can still be garbage-collected. As long as the @a@s remain
47+-- that is, they can still be garbage-collected. As long as the @/a/@ values remain
47-- alive, the 'FastWeakBag' will continue to refer to them.
48-- alive, the 'FastWeakBag' will continue to refer to them.
48#ifdef GHCJS_FAST_WEAK
49#ifdef GHCJS_FAST_WEAK
49newtype FastWeakBag a = FastWeakBag JSVal
50newtype FastWeakBag a = FastWeakBag JSVal
50#else
51#else
51data FastWeakBag a = FastWeakBag
52data FastWeakBag a = FastWeakBag
52 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
54+ , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ Map of items contained by the 'FastWeakBag'
54 }
55 }
55#endif
56#endif
56
57
@@ -116,14 +117,14 @@ foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.len
116isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117#endif
118#endif
118
119
119−{-# INLINE traverse #-}
120+{-# INLINE traverse_ #-}
120-- | Visit every node in the given list. If new nodes are appended during the
121-- | Visit every node in the given list. If new nodes are appended during the
121-- traversal, they will not be visited. Every live node that was in the list
122-- traversal, they will not be visited. Every live node that was in the list
122-- when the traversal began will be visited exactly once; however, no guarantee
123-- when the traversal began will be visited exactly once; however, no guarantee
123-- is made about the order of the traversal.
124-- is made about the order of the traversal.
124−traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125+traverse_ :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125#ifdef GHCJS_FAST_WEAK
126#ifdef GHCJS_FAST_WEAK
126−traverse wb f = do
127+traverse_ wb f = do
127 let go cursor = when (not $ js_isNull cursor) $ do
128 let go cursor = when (not $ js_isNull cursor) $ do
128 val <- liftIO $ js_getTicketValue cursor
129 val <- liftIO $ js_getTicketValue cursor
129 f $ unsafeFromRawJSVal val
130 f $ unsafeFromRawJSVal val
@@ -134,13 +135,17 @@ foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO
134--TODO: Fix the race condition where if a cursor is deleted (presumably using 'remove', below) while we're holding it, it can't find its way back to the correct bag
135--TODO: Fix the race condition where if a cursor is deleted (presumably using 'remove', below) while we're holding it, it can't find its way back to the correct bag
135foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
136foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
136#else
137#else
137−traverse (FastWeakBag _ children) f = {-# SCC "traverse" #-} do
138+traverse_ (FastWeakBag _ children) f = {-# SCC "traverse_" #-} do
138 cs <- liftIO $ readIORef children
139 cs <- liftIO $ readIORef children
139 forM_ cs $ \c -> do
140 forM_ cs $ \c -> do
140 ma <- liftIO $ deRefWeak c
141 ma <- liftIO $ deRefWeak c
141 mapM_ f ma
142 mapM_ f ma
142#endif
143#endif
143
144
145+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
146+traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
147+traverse = traverse_
148+
144-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
149-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
145-- on the same 'FastWeakBagTicket'.
150-- on the same 'FastWeakBagTicket'.
146{-# INLINE remove #-}
151{-# INLINE remove #-}
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+26−10
@@ -1,4 +1,5 @@
1{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE LambdaCase #-}
2+-- | Additional functions for manipulating 'Map's.
2module Data.Map.Misc
3module Data.Map.Misc
3 (
4 (
4 -- * Working with Maps
5 -- * Working with Maps
@@ -10,7 +11,6 @@ module Data.Map.Misc
10 ) where
11 ) where
11
12
12import Data.Align
13import Data.Align
13−import Data.Either (isLeft)
14import Data.Map (Map)
14import Data.Map (Map)
15import qualified Data.Map as Map
15import qualified Data.Map as Map
16import Data.Maybe
16import Data.Maybe
@@ -18,12 +18,21 @@ import Data.Set (Set)
18import qualified Data.Set as Set
18import qualified Data.Set as Set
19import Data.These
19import Data.These
20
20
21+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new@ respectively. @Just@ represents an association present in @new@ and @Nothing@
22+-- represents an association only present in @old@ but no longer present in @new@.
23+--
24+-- Similar to 'diffMap' but doesn't require 'Eq' on the values, thus can't tell if a value has changed or not.
21diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
25diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
22diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
26diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
23 This _ -> Just Nothing
27 This _ -> Just Nothing
24 These _ new -> Just $ Just new
28 These _ new -> Just $ Just new
25 That new -> Just $ Just new
29 That new -> Just $ Just new
26
30
31+-- |Produce a @'Map' k (Maybe v)@ by comparing two @'Map' k v@s, @old@ and @new respectively. @Just@ represents an association present in @new@ and either not
32+-- present in @old@ or where the value has changed. @Nothing@ represents an association only present in @old@ but no longer present in @new@.
33+--
34+-- See also 'diffMapNoEq' for a similar but weaker version which does not require 'Eq' on the values but thus can't indicated a value not changing between
35+-- @old@ and @new@ with @Nothing@.
27diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
36diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
28diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
37diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
29 This _ -> Just Nothing
38 This _ -> Just Nothing
@@ -32,23 +41,30 @@ diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
32 | otherwise -> Just $ Just new
41 | otherwise -> Just $ Just new
33 That new -> Just $ Just new
42 That new -> Just $ Just new
34
43
44+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new map from the given input @'Map' k v@.
45+--
46+-- See also 'Reflex.Patch.Map' and 'Reflex.Patch.MapWithMove'.
35applyMap :: 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
36applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
48applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
37− where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
49+ where (deletions, insertions) = Map.mapEither maybeToEither patch
38 maybeToEither = \case
50 maybeToEither = \case
39 Nothing -> Left ()
51 Nothing -> Left ()
40 Just r -> Right r
52 Just r -> Right r
41
53
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" #-}
42mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
56mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
43−mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
57+mapPartitionEithers = Map.mapEither id
44− where (ls, rs) = Map.partition isLeft m
45− fromLeft (Left l) = l
46− fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
47− fromRight (Right r) = r
48− fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
49
58
50−-- | Apply a map patch to a set
59+-- |Given a @'Map' k (Maybe v)@ representing keys to insert/update (@Just@) or delete (@Nothing@), produce a new @'Set' k@ from the given input set.
60+--
61+-- Equivalent to:
62+--
63+-- @
51−-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
64+-- applyMapKeysSet patch ('Map.keysSet' m) == 'Map.keysSet' ('applyMap' patch m)
65+-- @
66+--
67+-- but avoids the intervening @Map@ and needs no values.
52applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
68applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
53applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
69applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
54 where (insertions, deletions) = Map.partition isJust patch
70 where (insertions, deletions) = Map.partition isJust patch
src/Data/WeakBag.hs+10−5
@@ -15,6 +15,7 @@ module Data.WeakBag
15 , singleton
15 , singleton
16 , insert
16 , insert
17 , traverse
17 , traverse
18+ , traverse_
18 , remove
19 , remove
19 -- * Internal functions
20 -- * Internal functions
20 -- These will not always be available.
21 -- These will not always be available.
@@ -31,8 +32,8 @@ import qualified Data.IntMap.Strict as IntMap
31import Data.IORef
32import Data.IORef
32import System.Mem.Weak
33import System.Mem.Weak
33
34
34−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
35+-- | A 'WeakBag' holds a set of values of type @/a/@, but does not retain them -
35−-- that is, they can still be garbage-collected. As long as the @a@s remain
36+-- that is, they can still be garbage-collected. As long as the @/a/@ values remain
36-- alive, the 'WeakBag' will continue to refer to them.
37-- alive, the 'WeakBag' will continue to refer to them.
37data WeakBag a = WeakBag
38data WeakBag a = WeakBag
38 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
39 { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
@@ -99,18 +100,22 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99 ticket <- insert a bag wbRef finalizer
100 ticket <- insert a bag wbRef finalizer
100 return (bag, ticket)
101 return (bag, ticket)
101
102
102−{-# INLINE traverse #-}
103+{-# INLINE traverse_ #-}
103-- | Visit every node in the given list. If new nodes are appended during the
104-- | Visit every node in the given list. If new nodes are appended during the
104-- traversal, they will not be visited. Every live node that was in the list
105-- traversal, they will not be visited. Every live node that was in the list
105-- when the traversal began will be visited exactly once; however, no guarantee
106-- when the traversal began will be visited exactly once; however, no guarantee
106-- is made about the order of the traversal.
107-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108+traverse_ :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
109+traverse_ (WeakBag _ children) f = {-# SCC "traverse" #-} do
109 cs <- liftIO $ readIORef children
110 cs <- liftIO $ readIORef children
110 forM_ cs $ \c -> do
111 forM_ cs $ \c -> do
111 ma <- liftIO $ deRefWeak c
112 ma <- liftIO $ deRefWeak c
112 mapM_ f ma
113 mapM_ f ma
113
114
115+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
116+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
117+traverse = traverse_
118+
114-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
119-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115-- on the same 'WeakBagTicket'.
120-- on the same 'WeakBagTicket'.
116{-# INLINE remove #-}
121{-# INLINE remove #-}
src/Reflex.hs+3
@@ -7,6 +7,8 @@ module Reflex
7
7
8import Reflex.Class as X
8import Reflex.Class as X
9import Reflex.Adjustable.Class as X
9import Reflex.Adjustable.Class as X
10+import Reflex.BehaviorWriter.Base as X
11+import Reflex.BehaviorWriter.Class as X
10import Reflex.Collection as X
12import Reflex.Collection as X
11import Reflex.Dynamic as X
13import Reflex.Dynamic as X
12import Reflex.EventWriter.Base as X
14import Reflex.EventWriter.Base as X
@@ -17,6 +19,7 @@ import Reflex.Dynamic.TH as X
17import Reflex.Dynamic.Uniq as X
19import Reflex.Dynamic.Uniq as X
18import Reflex.DynamicWriter.Base as X
20import Reflex.DynamicWriter.Base as X
19import Reflex.DynamicWriter.Class as X
21import Reflex.DynamicWriter.Class as X
22+import Reflex.NotReady.Class as X
20import Reflex.PerformEvent.Base as X
23import Reflex.PerformEvent.Base as X
21import Reflex.PerformEvent.Class as X
24import Reflex.PerformEvent.Class as X
22import 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/Base.hsnew file+202
@@ -0,0 +1,202 @@
1+{-|
2+Module: Reflex.BehaviorWriter.Base
3+Description: Implementation of MonadBehaviorWriter
4+-}
5+{-# LANGUAGE CPP #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE GADTs #-}
8+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE UndecidableInstances #-}
14+{-# LANGUAGE StandaloneDeriving #-}
15+#ifdef USE_REFLEX_OPTIMIZER
16+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
17+#endif
18+module Reflex.BehaviorWriter.Base
19+ ( BehaviorWriterT (..)
20+ , runBehaviorWriterT
21+ , withBehaviorWriterT
22+ ) where
23+
24+import Control.Monad.Exception
25+import Control.Monad.Identity
26+import Control.Monad.IO.Class
27+import Control.Monad.Reader
28+import Control.Monad.Ref
29+import Control.Monad.State.Strict
30+import Data.Dependent.Map (DMap)
31+import qualified Data.Dependent.Map as DMap
32+import Data.Functor.Misc
33+import Data.IntMap (IntMap)
34+import qualified Data.IntMap as IntMap
35+import Data.Map (Map)
36+import qualified Data.Map as Map
37+import Data.Some (Some)
38+
39+import Reflex.Class
40+import Reflex.Adjustable.Class
41+import Reflex.BehaviorWriter.Class
42+import Reflex.Host.Class
43+import Reflex.PerformEvent.Class
44+import Reflex.PostBuild.Class
45+import Reflex.Query.Class
46+import Reflex.Requester.Class
47+import Reflex.TriggerEvent.Class
48+
49+-- | A basic implementation of 'MonadBehaviorWriter'.
50+newtype BehaviorWriterT t w m a = BehaviorWriterT { unBehaviorWriterT :: StateT [Behavior t w] m a }
51+ deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
52+
53+-- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided
54+-- along with the result of the action.
55+runBehaviorWriterT :: (Monad m, Reflex t, Monoid w) => BehaviorWriterT t w m a -> m (a, Behavior t w)
56+runBehaviorWriterT (BehaviorWriterT a) = do
57+ (result, ws) <- runStateT a []
58+ return (result, mconcat $ reverse ws)
59+
60+-- | Map a function over the output of a 'BehaviorWriterT'.
61+withBehaviorWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m)
62+ => (w -> w')
63+ -> BehaviorWriterT t w m a
64+ -> BehaviorWriterT t w' m a
65+withBehaviorWriterT f dw = do
66+ (r, d) <- lift $ do
67+ (r, d) <- runBehaviorWriterT dw
68+ let d' = fmap f d
69+ return (r, d')
70+ tellBehavior d
71+ return r
72+
73+deriving instance MonadHold t m => MonadHold t (BehaviorWriterT t w m)
74+deriving instance MonadSample t m => MonadSample t (BehaviorWriterT t w m)
75+
76+instance MonadTrans (BehaviorWriterT t w) where
77+ lift = BehaviorWriterT . lift
78+
79+instance MonadRef m => MonadRef (BehaviorWriterT t w m) where
80+ type Ref (BehaviorWriterT t w m) = Ref m
81+ newRef = lift . newRef
82+ readRef = lift . readRef
83+ writeRef r = lift . writeRef r
84+
85+instance MonadAtomicRef m => MonadAtomicRef (BehaviorWriterT t w m) where
86+ atomicModifyRef r = lift . atomicModifyRef r
87+
88+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (BehaviorWriterT t w m) where
89+ newEventWithTrigger = lift . newEventWithTrigger
90+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
91+
92+instance (Monad m, Monoid w, Reflex t) => MonadBehaviorWriter t w (BehaviorWriterT t w m) where
93+ tellBehavior w = BehaviorWriterT $ modify (w :)
94+
95+instance MonadReader r m => MonadReader r (BehaviorWriterT t w m) where
96+ ask = lift ask
97+ local f (BehaviorWriterT a) = BehaviorWriterT $ mapStateT (local f) a
98+ reader = lift . reader
99+
100+instance PerformEvent t m => PerformEvent t (BehaviorWriterT t w m) where
101+ type Performable (BehaviorWriterT t w m) = Performable m
102+ performEvent_ = lift . performEvent_
103+ performEvent = lift . performEvent
104+
105+instance TriggerEvent t m => TriggerEvent t (BehaviorWriterT t w m) where
106+ newTriggerEvent = lift newTriggerEvent
107+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
108+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
109+
110+instance PostBuild t m => PostBuild t (BehaviorWriterT t w m) where
111+ getPostBuild = lift getPostBuild
112+
113+instance MonadState s m => MonadState s (BehaviorWriterT t w m) where
114+ get = lift get
115+ put = lift . put
116+
117+instance Requester t m => Requester t (BehaviorWriterT t w m) where
118+ type Request (BehaviorWriterT t w m) = Request m
119+ type Response (BehaviorWriterT t w m) = Response m
120+ requesting = lift . requesting
121+ requesting_ = lift . requesting_
122+
123+instance (MonadQuery t q m, Monad m) => MonadQuery t q (BehaviorWriterT t w m) where
124+ tellQueryIncremental = lift . tellQueryIncremental
125+ askQueryResult = lift askQueryResult
126+ queryIncremental = lift . queryIncremental
127+
128+instance (Adjustable t m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (BehaviorWriterT t w m) where
129+ runWithReplace a0 a' = do
130+ (result0, result') <- lift $ runWithReplace (runBehaviorWriterT a0) $ runBehaviorWriterT <$> a'
131+ tellBehavior . join =<< hold (snd result0) (snd <$> result')
132+ return (fst result0, fst <$> result')
133+ traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust
134+ traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith
135+
136+traverseIntMapWithKeyWithAdjustImpl
137+ :: forall t w v' p p' v m.
138+ ( PatchTarget (p' (Behavior t w)) ~ IntMap (Behavior t w)
139+ , Patch (p' (Behavior t w))
140+ , Monoid w
141+ , Reflex t
142+ , MonadHold t m
143+ , Functor p
144+ , p ~ p'
145+ )
146+ => ( (IntMap.Key -> v -> m (v', Behavior t w))
147+ -> IntMap v
148+ -> Event t (p v)
149+ -> m (IntMap (v', Behavior t w), Event t (p (v', Behavior t w)))
150+ )
151+ -> (IntMap.Key -> v -> BehaviorWriterT t w m v')
152+ -> IntMap v
153+ -> Event t (p v)
154+ -> BehaviorWriterT t w m (IntMap v', Event t (p v'))
155+traverseIntMapWithKeyWithAdjustImpl base f (dm0 :: IntMap v) dm' = do
156+ (result0, result') <- lift $ base (\k v -> runBehaviorWriterT $ f k v) dm0 dm'
157+ let liftedResult0 = fmap fst result0
158+ liftedResult' = fmap (fmap fst) result'
159+ liftedWritten0 :: IntMap (Behavior t w)
160+ liftedWritten0 = fmap snd result0
161+ liftedWritten' = fmap (fmap snd) result'
162+ i <- holdIncremental liftedWritten0 liftedWritten'
163+ tellBehavior $ pull $ do
164+ m <- sample $ currentIncremental i
165+ mconcat . IntMap.elems <$> traverse sample m
166+ return (liftedResult0, liftedResult')
167+
168+newtype BehaviorWriterTLoweredResult t w v a = BehaviorWriterTLoweredResult (v a, Behavior t w)
169+
170+traverseDMapWithKeyWithAdjustImpl
171+ :: forall t w k v' p p' v m.
172+ ( PatchTarget (p' (Some k) (Behavior t w)) ~ Map (Some k) (Behavior t w)
173+ , Patch (p' (Some k) (Behavior t w))
174+ , Monoid w
175+ , Reflex t
176+ , MonadHold t m
177+ )
178+ => ( (forall a. k a -> v a -> m (BehaviorWriterTLoweredResult t w v' a))
179+ -> DMap k v
180+ -> Event t (p k v)
181+ -> m (DMap k (BehaviorWriterTLoweredResult t w v'), Event t (p k (BehaviorWriterTLoweredResult t w v')))
182+ )
183+ -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> v' a) -> p k (BehaviorWriterTLoweredResult t w v') -> p k v')
184+ -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> Behavior t w) -> p k (BehaviorWriterTLoweredResult t w v') -> p' (Some k) (Behavior t w))
185+ -> (forall a. k a -> v a -> BehaviorWriterT t w m (v' a))
186+ -> DMap k v
187+ -> Event t (p k v)
188+ -> BehaviorWriterT t w m (DMap k v', Event t (p k v'))
189+traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith f (dm0 :: DMap k v) dm' = do
190+ (result0, result') <- lift $ base (\k v -> fmap BehaviorWriterTLoweredResult $ runBehaviorWriterT $ f k v) dm0 dm'
191+ let getValue (BehaviorWriterTLoweredResult (v, _)) = v
192+ getWritten (BehaviorWriterTLoweredResult (_, w)) = w
193+ liftedResult0 = DMap.map getValue result0
194+ liftedResult' = ffor result' $ mapPatch getValue
195+ liftedWritten0 :: Map (Some k) (Behavior t w)
196+ liftedWritten0 = weakenDMapWith getWritten result0
197+ liftedWritten' = ffor result' $ weakenPatchWith getWritten
198+ i <- holdIncremental liftedWritten0 liftedWritten'
199+ tellBehavior $ pull $ do
200+ m <- sample $ currentIncremental i
201+ mconcat . Map.elems <$> traverse sample m
202+ return (liftedResult0, liftedResult')
src/Reflex/BehaviorWriter/Class.hsnew file+25
@@ -0,0 +1,25 @@
1+{-|
2+Module: Reflex.BehaviorWriter.Class
3+Description: This module defines the 'MonadBehaviorWriter' class
4+-}
5+{-# LANGUAGE CPP #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE UndecidableInstances #-}
9+#ifdef USE_REFLEX_OPTIMIZER
10+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11+#endif
12+module Reflex.BehaviorWriter.Class
13+ ( MonadBehaviorWriter (..)
14+ ) where
15+
16+import Control.Monad.Reader (ReaderT, lift)
17+import Reflex.Class (Behavior)
18+
19+-- | 'MonadBehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
20+-- and combines them monoidally to provide a 'Behavior' result.
21+class (Monad m, Monoid w) => MonadBehaviorWriter t w m | m -> t w where
22+ tellBehavior :: Behavior t w -> m ()
23+
24+instance MonadBehaviorWriter t w m => MonadBehaviorWriter t w (ReaderT r m) where
25+ tellBehavior = lift . tellBehavior
src/Reflex/Class.hs+506−132
@@ -7,7 +7,6 @@
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,27 +36,36 @@ 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− -- ** 'Incremental'-related types
41 -- * Convenience functions
46 -- * Convenience functions
42 , constDyn
47 , constDyn
43 , pushAlways
48 , pushAlways
44 -- ** Combining 'Event's
49 -- ** Combining 'Event's
45 , leftmost
50 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
46 , mergeMap
54 , mergeMap
55+ , mergeIntMap
47 , mergeMapIncremental
56 , mergeMapIncremental
48 , mergeMapIncrementalWithMove
57 , mergeMapIncrementalWithMove
49 , mergeIntMapIncremental
58 , mergeIntMapIncremental
59+ , coincidencePatchMap
60+ , coincidencePatchMapWithMove
61+ , coincidencePatchIntMap
50 , mergeList
62 , mergeList
51 , mergeWith
63 , mergeWith
52 , difference
64 , difference
53 , alignEventWithMaybe
65 , alignEventWithMaybe
54 -- ** Breaking up 'Event's
66 -- ** Breaking up 'Event's
55 , splitE
67 , splitE
68+ , fan
56 , fanEither
69 , fanEither
57 , fanThese
70 , fanThese
58 , fanMap
71 , fanMap
@@ -67,6 +80,7 @@ module Reflex.Class
67 , switchHold
80 , switchHold
68 , switchHoldPromptly
81 , switchHoldPromptly
69 , switchHoldPromptOnly
82 , switchHoldPromptOnly
83+ , switchHoldPromptOnlyIncremental
70 -- ** Using 'Event's to sample 'Behavior's
84 -- ** Using 'Event's to sample 'Behavior's
71 , tag
85 , tag
72 , tagMaybe
86 , tagMaybe
@@ -77,6 +91,7 @@ module Reflex.Class
77 , gate
91 , gate
78 -- ** Combining 'Dynamic's
92 -- ** Combining 'Dynamic's
79 , distributeDMapOverDynPure
93 , distributeDMapOverDynPure
94+ , distributeDMapOverDynPureG
80 , distributeListOverDyn
95 , distributeListOverDyn
81 , distributeListOverDynWith
96 , distributeListOverDynWith
82 , zipDyn
97 , zipDyn
@@ -130,8 +145,10 @@ module Reflex.Class
130 -- * Unsafe functions
145 -- * Unsafe functions
131 , unsafeDynamic
146 , unsafeDynamic
132 , unsafeMapIncremental
147 , unsafeMapIncremental
133− -- * 'FunctorMaybe'
148+ -- * 'Filterable' convenience functions
134− , FunctorMaybe (..)
149+ , FunctorMaybe -- fmapMaybe is purposely not exported from deprecated 'FunctorMaybe' and the new alias is exported instead
150+ , mapMaybe -- Re-exported for convenience
151+ , fmapMaybe
135 , fforMaybe
152 , fforMaybe
136 , ffilter
153 , ffilter
137 , filterLeft
154 , filterLeft
@@ -141,10 +158,11 @@ module Reflex.Class
141 , ffor2
158 , ffor2
142 , ffor3
159 , ffor3
143 -- * Deprecated functions
160 -- * Deprecated functions
144− , appendEvents
161+ , switchPromptly
145− , onceE
162+ , switchPromptOnly
146− , sequenceThese
163+ -- * "Cheap" functions
147 , fmapMaybeCheap
164 , fmapMaybeCheap
165+ , mapMaybeCheap
148 , fmapCheap
166 , fmapCheap
149 , fforCheap
167 , fforCheap
150 , fforMaybeCheap
168 , fforMaybeCheap
@@ -152,12 +170,21 @@ module Reflex.Class
152 , tagCheap
170 , tagCheap
153 , mergeWithCheap
171 , mergeWithCheap
154 , mergeWithCheap'
172 , mergeWithCheap'
155− , switchPromptly
156− , switchPromptOnly
157 -- * Slow, but general, implementations
173 -- * Slow, but general, implementations
158 , slowHeadE
174 , slowHeadE
159 ) where
175 ) where
160
176
177+#ifdef MIN_VERSION_semialign
178+import Prelude hiding (zip, zipWith)
179+
180+#if MIN_VERSION_these(0,8,0)
181+import Data.These.Combinators (justThese)
182+#endif
183+#if MIN_VERSION_semialign(1,1,0)
184+import Data.Zip (Zip (..))
185+#endif
186+#endif
187+
161import Control.Applicative
188import Control.Applicative
162import Control.Monad.Identity
189import Control.Monad.Identity
163import Control.Monad.Reader
190import Control.Monad.Reader
@@ -185,18 +212,21 @@ import qualified Data.IntMap.Strict as IntMap
185import Data.List.NonEmpty (NonEmpty (..))
212import Data.List.NonEmpty (NonEmpty (..))
186import Data.Map (Map)
213import Data.Map (Map)
187import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
214import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
188−import Data.Some (Some)
215+import Data.Some (Some(Some))
189−import qualified Data.Some as Some
190import Data.String
216import Data.String
191import Data.These
217import Data.These
192import Data.Type.Coercion
218import Data.Type.Coercion
219+import Data.Witherable (Filterable(..))
220+import qualified Data.Witherable as W
221+import Reflex.FunctorMaybe (FunctorMaybe)
193−import Reflex.FunctorMaybe
222+import qualified Reflex.FunctorMaybe
194import Reflex.Patch
223import Reflex.Patch
224+import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
195
225
196import Debug.Trace (trace)
226import Debug.Trace (trace)
197
227
198-- | The 'Reflex' class contains all the primitive functionality needed for
228-- | The 'Reflex' class contains all the primitive functionality needed for
199−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
229+-- Functional Reactive Programming (FRP). The @/t/@ type parameter indicates
200-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
230-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
201-- and the type of the timeline determines the FRP engine to be used. For most
231-- and the type of the timeline determines the FRP engine to be used. For most
202-- purposes, the 'Reflex.Spider' implementation is recommended.
232-- purposes, the 'Reflex.Spider' implementation is recommended.
@@ -244,16 +274,20 @@ class ( MonadHold t (PushM t)
244 -- | Merge a collection of events; the resulting 'Event' will only occur if at
274 -- | Merge a collection of events; the resulting 'Event' will only occur if at
245 -- least one input event is occurring, and will contain all of the input keys
275 -- least one input event is occurring, and will contain all of the input keys
246 -- that are occurring simultaneously
276 -- that are occurring simultaneously
277+
247− merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
278+ --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
279+ mergeG :: GCompare k => (forall a. q a -> Event t (v a))
280+ -> DMap k q -> Event t (DMap k v)
281+
248− -- | Efficiently fan-out an event to many destinations. This function should
282+ -- | Efficiently fan-out an event to many destinations. You should save the
249− -- be partially applied, and then the result applied repeatedly to create
283+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
250− -- child events
284+ -- create child events
251− fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
285+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
286+
252 -- | Create an 'Event' that will occur whenever the currently-selected input
287 -- | Create an 'Event' that will occur whenever the currently-selected input
253 -- 'Event' occurs
288 -- 'Event' occurs
254 switch :: Behavior t (Event t a) -> Event t a
289 switch :: Behavior t (Event t a) -> Event t a
255− -- | Create an 'Event' that will occur whenever the input event is occurring
290+ -- | Create an 'Event' that will occur whenever the input event is occurring -- and its occurrence value, another 'Event', is also occurring
256− -- and its occurrence value, another 'Event', is also occurring
257 coincidence :: Event t (Event t a) -> Event t a
291 coincidence :: Event t (Event t a) -> Event t a
258 -- | Extract the 'Behavior' of a 'Dynamic'.
292 -- | Extract the 'Behavior' of a 'Dynamic'.
259 current :: Dynamic t a -> Behavior t a
293 current :: Dynamic t a -> Behavior t a
@@ -262,14 +296,19 @@ class ( MonadHold t (PushM t)
262 -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
296 -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
263 -- recent firing of the given 'Event', if any.
297 -- recent firing of the given 'Event', if any.
264 unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
298 unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
265− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
299+ -- | Create a new 'Incremental'. The given "PullM"'s value must always change
266 -- in the same way that the accumulated application of patches would change
300 -- in the same way that the accumulated application of patches would change
267 -- that value.
301 -- that value.
268 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
302 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
269 -- | Create a merge whose parents can change over time
303 -- | Create a merge whose parents can change over time
304+ mergeIncrementalG :: GCompare k
305+ => (forall a. q a -> Event t (v a))
270− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
306+ -> Incremental t (PatchDMap k q)
307+ -> Event t (DMap k v)
271 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
308 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
309+ mergeIncrementalWithMoveG :: GCompare k
310+ => (forall a. q a -> Event t (v a))
272− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
311+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
273 -- | Extract the 'Behavior' component of an 'Incremental'
312 -- | Extract the 'Behavior' component of an 'Incremental'
274 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
313 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
275 -- | Extract the 'Event' component of an 'Incremental'
314 -- | Extract the 'Event' component of an 'Incremental'
@@ -285,10 +324,28 @@ class ( MonadHold t (PushM t)
285 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
324 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
286 -- occurrence type
325 -- occurrence type
287 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
326 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
327+ -- | Construct a 'Coercion' for an 'Incremental' given 'Coercion's for its
328+ -- patch target and patch types.
329+ incrementalCoercion
330+ :: Coercion (PatchTarget a) (PatchTarget b) -> Coercion a b -> Coercion (Incremental t a) (Incremental t b)
288 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
331 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
289 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
332 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
290
333
334+-- | Efficiently fan-out an event to many destinations. You should save the
335+-- result in a @let@-binding, and then repeatedly 'select' on the result to
336+-- create child events
337+fan :: forall t k. (Reflex t, GCompare k)
338+ => Event t (DMap k Identity) -> EventSelector t k
339+ --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
340+fan e = EventSelector (fixup (selectG (fanG e) :: k a -> Event t (Identity a)) :: forall a. k a -> Event t a)
341+ where
342+ fixup :: forall a. (k a -> Event t (Identity a)) -> k a -> Event t a
343+ fixup = case eventCoercion Coercion :: Coercion (Event t (Identity a)) (Event t a) of
344+ Coercion -> coerce
345+
291--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
346--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
347+-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
348+-- keys simultaneously.
292mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
349mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
293mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
350mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
294
351
@@ -304,8 +361,14 @@ coerceEvent = coerceWith $ eventCoercion Coercion
304coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
361coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
305coerceDynamic = coerceWith $ dynamicCoercion Coercion
362coerceDynamic = coerceWith $ dynamicCoercion Coercion
306
363
364+-- | Coerce an 'Incremental' between representationally-equivalent value types
365+coerceIncremental
366+ :: (Reflex t, Coercible a b, Coercible (PatchTarget a) (PatchTarget b))
367+ => Incremental t a -> Incremental t b
368+coerceIncremental = coerceWith $ incrementalCoercion Coercion Coercion
369+
307-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
370-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
308−-- _must_ change when and only when the 'Event' fires, such that the
371+-- __must__ change when and only when the 'Event' fires, such that the
309-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
372-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
310-- if this is not the case, the resulting 'Dynamic' will behave
373-- if this is not the case, the resulting 'Dynamic' will behave
311-- nondeterministically.
374-- nondeterministically.
@@ -333,7 +396,7 @@ class MonadSample t m => MonadHold t m where
333 -- value and will be updated whenever the given 'Event' occurs. The update
396 -- value and will be updated whenever the given 'Event' occurs. The update
334 -- takes effect immediately after the 'Event' occurs; if the occurrence that
397 -- takes effect immediately after the 'Event' occurs; if the occurrence that
335 -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
398 -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
336− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
399+ -- the 'Behavior', it will see the __old__ value of the 'Behavior', not the new
337 -- one.
400 -- one.
338 hold :: a -> Event t a -> m (Behavior t a)
401 hold :: a -> Event t a -> m (Behavior t a)
339 default hold :: (m ~ f m', MonadTrans f, MonadHold t m') => a -> Event t a -> m (Behavior t a)
402 default hold :: (m ~ f m', MonadTrans f, MonadHold t m') => a -> Event t a -> m (Behavior t a)
@@ -357,26 +420,91 @@ class MonadSample t m => MonadHold t m where
357 -- the supplied 'Event'.
420 -- the supplied 'Event'.
358 headE :: Event t a -> m (Event t a)
421 headE :: Event t a -> m (Event t a)
359
422
423+-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
424+-- using the combining function to produce a patch.
425+accumIncremental
360−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
426+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
427+ => (PatchTarget p -> b -> p)
428+ -> PatchTarget p
429+ -> Event t b
430+ -> m (Incremental t p)
361accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
431accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
432+
433+-- | Similar to 'accumIncremental' but the combining function runs in 'PushM'
434+accumMIncremental
362−accumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t p) -> PatchTarget p -> Event t b -> m (Incremental t p)
435+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
436+ => (PatchTarget p -> b -> PushM t p)
437+ -> PatchTarget p
438+ -> Event t b
439+ -> m (Incremental t p)
363accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
440accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
441+
442+-- | Similar to 'accumIncremental' but allows filtering of updates (by dropping updates when the
443+-- combining function produces @Nothing@)
444+accumMaybeIncremental
364−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
445+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
446+ => (PatchTarget p -> b -> Maybe p)
447+ -> PatchTarget p
448+ -> Event t b
449+ -> m (Incremental t p)
365accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
450accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
451+
452+-- | Similar to 'accumMaybeMIncremental' but the combining function runs in 'PushM'
453+accumMaybeMIncremental
366−accumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p)) -> PatchTarget p -> Event t b -> m (Incremental t p)
454+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
455+ => (PatchTarget p -> b -> PushM t (Maybe p))
456+ -> PatchTarget p
457+ -> Event t b
458+ -> m (Incremental t p)
367accumMaybeMIncremental f z e = do
459accumMaybeMIncremental f z e = do
368 rec let e' = flip push e $ \o -> do
460 rec let e' = flip push e $ \o -> do
369 v <- sample $ currentIncremental d'
461 v <- sample $ currentIncremental d'
370 f v o
462 f v o
371 d' <- holdIncremental z e'
463 d' <- holdIncremental z e'
372 return d'
464 return d'
465+
466+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event'
467+-- with a function that both accumulates and produces a value to fire
468+-- as an 'Event'. Returns both the accumulated value and the constructed
469+-- 'Event'.
470+mapAccumIncremental
373−mapAccumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
471+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
472+ => (PatchTarget p -> b -> (p, c))
473+ -> PatchTarget p
474+ -> Event t b
475+ -> m (Incremental t p, Event t c)
374mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
476mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
477+
478+-- | Like 'mapAccumIncremental' but the combining function runs in 'PushM'
479+mapAccumMIncremental
375−mapAccumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
480+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
481+ => (PatchTarget p -> b -> PushM t (p, c))
482+ -> PatchTarget p
483+ -> Event t b
484+ -> m (Incremental t p, Event t c)
376mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
485mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
486+
487+-- | Accumulate an 'Incremental' by folding occurrences of an 'Event' with
488+-- a function that both optionally accumulates and optionally produces
489+-- a value to fire as a separate output 'Event'.
490+-- Note that because 'Nothing's are discarded in both cases, the output
491+-- 'Event' may fire even though the output 'Incremental' has not changed, and
492+-- the output 'Incremental' may update even when the output 'Event' is not firing.
493+mapAccumMaybeIncremental
377−mapAccumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
494+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
495+ => (PatchTarget p -> b -> (Maybe p, Maybe c))
496+ -> PatchTarget p
497+ -> Event t b
498+ -> m (Incremental t p, Event t c)
378mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
499mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
500+
501+-- | Like 'mapAccumMaybeIncremental' but the combining function is a 'PushM' action
502+mapAccumMaybeMIncremental
379−mapAccumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
503+ :: (Reflex t, Patch p, MonadHold t m, MonadFix m)
504+ => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c))
505+ -> PatchTarget p
506+ -> Event t b
507+ -> m (Incremental t p, Event t c)
380mapAccumMaybeMIncremental f z e = do
508mapAccumMaybeMIncremental f z e = do
381 rec let e' = flip push e $ \o -> do
509 rec let e' = flip push e $ \o -> do
382 v <- sample $ currentIncremental d'
510 v <- sample $ currentIncremental d'
@@ -384,9 +512,10 @@ mapAccumMaybeMIncremental f z e = do
384 return $ case result of
512 return $ case result of
385 (Nothing, Nothing) -> Nothing
513 (Nothing, Nothing) -> Nothing
386 _ -> Just result
514 _ -> Just result
387− d' <- holdIncremental z $ fmapMaybe fst e'
515+ d' <- holdIncremental z $ mapMaybe fst e'
388− return (d', fmapMaybe snd e')
516+ return (d', mapMaybe snd e')
389
517
518+-- | A somewhat slow implementation of 'headE'
390slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
519slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
391slowHeadE e = do
520slowHeadE e = do
392 rec be <- hold e $ fmapCheap (const never) e'
521 rec be <- hold e $ fmapCheap (const never) e'
@@ -395,18 +524,31 @@ slowHeadE e = do
395
524
396-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
525-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
397-- key. This is much more efficient than filtering for each key with
526-- key. This is much more efficient than filtering for each key with
398−-- 'fmapMaybe'.
527+-- 'mapMaybe'.
399newtype EventSelector t k = EventSelector
528newtype EventSelector t k = EventSelector
400 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
529 { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
401 -- determined by the type of the key, so this can be used to fan-out
530 -- determined by the type of the key, so this can be used to fan-out
402 -- 'Event's whose sub-'Event's have different types.
531 -- 'Event's whose sub-'Event's have different types.
403 --
532 --
404 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
533 -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
405− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
534+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
406 -- occurrences of an 'Event'.
535 -- occurrences of an 'Event'.
407 select :: forall a. k a -> Event t a
536 select :: forall a. k a -> Event t a
408 }
537 }
409
538
539+newtype EventSelectorG t k v = EventSelectorG
540+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
541+ -- determined by the type of the key, so this can be used to fan-out
542+ -- 'Event's whose sub-'Event's have different types.
543+ --
544+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
545+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
546+ -- occurrences of an 'Event'.
547+ selectG :: forall a. k a -> Event t (v a)
548+ }
549+
550+-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
551+-- filtering by key.
410newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
552newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
411
553
412--------------------------------------------------------------------------------
554--------------------------------------------------------------------------------
@@ -537,40 +679,51 @@ instance (Reflex t, Num a) => Num (Behavior t a) where
537 negate = fmap negate
679 negate = fmap negate
538 signum = fmap signum
680 signum = fmap signum
539
681
682+instance (Num a, Reflex t) => Num (Dynamic t a) where
683+ (+) = liftA2 (+)
684+ (*) = liftA2 (*)
685+ abs = fmap abs
686+ signum = fmap signum
687+ fromInteger = pure . fromInteger
688+ negate = fmap negate
689+ (-) = liftA2 (-)
690+
540instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
691instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
541 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
692 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
542 sconcat = pull . fmap sconcat . mapM sample
693 sconcat = pull . fmap sconcat . mapM sample
543−#if MIN_VERSION_semigroups(0,17,0)
544 stimes n = fmap $ stimes n
694 stimes n = fmap $ stimes n
545−#else
546− times1p n = fmap $ times1p n
547−#endif
548
695
696+-- | Alias for 'mapMaybe'
697+fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
698+fmapMaybe = mapMaybe
699+
549−-- | Flipped version of 'fmapMaybe'.
700+-- | Flipped version of 'mapMaybe'.
550−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
701+fforMaybe :: Filterable f => f a -> (a -> Maybe b) -> f b
551−fforMaybe = flip fmapMaybe
702+fforMaybe = flip mapMaybe
552
703
553-- | Filter 'f a' using the provided predicate.
704-- | Filter 'f a' using the provided predicate.
554−-- Relies on 'fforMaybe'.
555−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
705+ffilter :: Filterable f => (a -> Bool) -> f a -> f a
556−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
706+ffilter = W.filter
557
707
558-- | Filter 'Left's from 'f (Either a b)' into 'a'.
708-- | Filter 'Left's from 'f (Either a b)' into 'a'.
559−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
709+filterLeft :: Filterable f => f (Either a b) -> f a
560−filterLeft = fmapMaybe (either Just (const Nothing))
710+filterLeft = mapMaybe (either Just (const Nothing))
561
711
562-- | Filter 'Right's from 'f (Either a b)' into 'b'.
712-- | Filter 'Right's from 'f (Either a b)' into 'b'.
563−filterRight :: FunctorMaybe f => f (Either a b) -> f b
713+filterRight :: Filterable f => f (Either a b) -> f b
564−filterRight = fmapMaybe (either (const Nothing) Just)
714+filterRight = mapMaybe (either (const Nothing) Just)
565
715
566-- | Left-biased event union (prefers left event on simultaneous
716-- | Left-biased event union (prefers left event on simultaneous
567-- occurrence).
717-- occurrence).
568instance Reflex t => Alt (Event t) where
718instance Reflex t => Alt (Event t) where
569 ev1 <!> ev2 = leftmost [ev1, ev2]
719 ev1 <!> ev2 = leftmost [ev1, ev2]
570
720
571−-- | 'Event' intersection (convenient interface to 'coincidence').
721+-- | 'Event' intersection. Only occurs when both events are co-incident.
572instance Reflex t => Apply (Event t) where
722instance Reflex t => Apply (Event t) where
573− evf <.> evx = coincidence (fmap (<$> evx) evf)
723+ evf <.> evx = mapMaybe f (align evf evx) where
724+ f (These g a) = Just (g a)
725+ f _ = Nothing
726+
574
727
575-- | 'Event' intersection (convenient interface to 'coincidence').
728-- | 'Event' intersection (convenient interface to 'coincidence').
576instance Reflex t => Bind (Event t) where
729instance Reflex t => Bind (Event t) where
@@ -579,13 +732,18 @@ instance Reflex t => Bind (Event t) where
579
732
580instance Reflex t => Functor (Event t) where
733instance Reflex t => Functor (Event t) where
581 {-# INLINE fmap #-}
734 {-# INLINE fmap #-}
582− fmap f = fmapMaybe $ Just . f
735+ fmap f = mapMaybe $ Just . f
583 {-# INLINE (<$) #-}
736 {-# INLINE (<$) #-}
584 x <$ e = fmapCheap (const x) e
737 x <$ e = fmapCheap (const x) e
585
738
739+-- TODO Remove this instance
586instance Reflex t => FunctorMaybe (Event t) where
740instance Reflex t => FunctorMaybe (Event t) where
587 {-# INLINE fmapMaybe #-}
741 {-# INLINE fmapMaybe #-}
742+ fmapMaybe = mapMaybe
743+
744+instance Reflex t => Filterable (Event t) where
745+ {-# INLINE mapMaybe #-}
588− fmapMaybe f = push $ return . f
746+ mapMaybe f = push $ return . f
589
747
590-- | Never: @'zero' = 'never'@.
748-- | Never: @'zero' = 'never'@.
591instance Reflex t => Plus (Event t) where
749instance Reflex t => Plus (Event t) where
@@ -636,7 +794,7 @@ headTailE e = do
636-- event returns 'True'.
794-- event returns 'True'.
637--
795--
638-- Starting at the current time, fire all the occurrences of the 'Event' for
796-- Starting at the current time, fire all the occurrences of the 'Event' for
639−-- which the given predicate returns 'True'. When 'False' first is returned,
797+-- which the given predicate returns 'True'. When first 'False' is returned,
640-- do not fire, and permanently stop firing, even if 'True' values would have
798-- do not fire, and permanently stop firing, even if 'True' values would have
641-- been encountered later.
799-- been encountered later.
642takeWhileE
800takeWhileE
@@ -651,7 +809,7 @@ takeWhileE f = takeWhileJustE $ \v -> guard (f v) $> v
651-- event returns 'Just b'.
809-- event returns 'Just b'.
652--
810--
653-- Starting at the current time, fire all the occurrences of the 'Event' for
811-- Starting at the current time, fire all the occurrences of the 'Event' for
654−-- which the given predicate returns 'Just b'. When 'Nothing' first is returned,
812+-- which the given predicate returns 'Just b'. When first 'Nothing' is returned,
655-- do not fire, and permanently stop firing, even if 'Just b' values would have
813-- do not fire, and permanently stop firing, even if 'Just b' values would have
656-- been encountered later.
814-- been encountered later.
657takeWhileJustE
815takeWhileJustE
@@ -734,11 +892,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
734instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
892instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
735 (<>) = alignWith (mergeThese (<>))
893 (<>) = alignWith (mergeThese (<>))
736 sconcat = fmap sconcat . mergeList . toList
894 sconcat = fmap sconcat . mergeList . toList
737−#if MIN_VERSION_semigroups(0,17,0)
738 stimes n = fmap $ stimes n
895 stimes n = fmap $ stimes n
739−#else
740− times1p n = fmap $ times1p n
741−#endif
742
896
743instance (Semigroup a, Reflex t) => Monoid (Event t a) where
897instance (Semigroup a, Reflex t) => Monoid (Event t a) where
744 mempty = never
898 mempty = never
@@ -773,7 +927,12 @@ mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
773mergeList [] = never
927mergeList [] = never
774mergeList es = mergeWithFoldCheap' id es
928mergeList es = mergeWithFoldCheap' id es
775
929
930+unsafeMapIncremental
931+ :: (Reflex t, Patch p, Patch p')
776−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
932+ => (PatchTarget p -> PatchTarget p')
933+ -> (p -> p')
934+ -> Incremental t p
935+ -> Incremental t p'
777unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
936unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
778
937
779-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
938-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
@@ -782,6 +941,10 @@ unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIn
782mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
941mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
783mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
942mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
784
943
944+-- | Like 'mergeMap' but for 'IntMap'.
945+mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
946+mergeIntMap = fmap dmapToIntMap . merge . intMapWithFunctorToDMap
947+
785-- | Create a merge whose parents can change over time
948-- | Create a merge whose parents can change over time
786mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
949mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
787mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
950mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
@@ -799,7 +962,7 @@ fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
799fanEither e =
962fanEither e =
800 let justLeft = either Just (const Nothing)
963 let justLeft = either Just (const Nothing)
801 justRight = either (const Nothing) Just
964 justRight = either (const Nothing) Just
802− in (fmapMaybe justLeft e, fmapMaybe justRight e)
965+ in (mapMaybe justLeft e, mapMaybe justRight e)
803
966
804-- | Split the event into separate events for 'This' and 'That' values,
967-- | Split the event into separate events for 'This' and 'That' values,
805-- allowing them to fire simultaneously when the input value is 'These'.
968-- allowing them to fire simultaneously when the input value is 'These'.
@@ -811,7 +974,7 @@ fanThese e =
811 that (That y) = Just y
974 that (That y) = Just y
812 that (These _ y) = Just y
975 that (These _ y) = Just y
813 that _ = Nothing
976 that _ = Nothing
814− in (fmapMaybe this e, fmapMaybe that e)
977+ in (mapMaybe this e, mapMaybe that e)
815
978
816-- | Split the event into an 'EventSelector' that allows efficient selection of
979-- | Split the event into an 'EventSelector' that allows efficient selection of
817-- the individual 'Event's.
980-- the individual 'Event's.
@@ -824,7 +987,7 @@ eventJoin = switchHold never
824
987
825-- | Switches to the new event whenever it receives one. Only the old event is
988-- | Switches to the new event whenever it receives one. Only the old event is
826-- considered the moment a new one is switched in; the output event will fire at
989-- considered the moment a new one is switched in; the output event will fire at
827−-- that moment if only if the old event does.
990+-- that moment only if the old event does.
828--
991--
829-- Because the simultaneous firing case is irrelevant, this function imposes
992-- Because the simultaneous firing case is irrelevant, this function imposes
830-- laxer "timing requirements" on the overall circuit, avoiding many potential
993-- laxer "timing requirements" on the overall circuit, avoiding many potential
@@ -864,17 +1027,79 @@ switchHoldPromptOnly e0 e' = do
864 eLag <- switch <$> hold e0 e'
1027 eLag <- switch <$> hold e0 e'
865 return $ coincidence $ leftmost [e', eLag <$ eLag]
1028 return $ coincidence $ leftmost [e', eLag <$ eLag]
866
1029
1030+-- | When the given outer event fires, condense the inner events into the contained patch. Non-firing inner events will be replaced with deletions.
1031+coincidencePatchMap :: (Reflex t, Ord k) => Event t (PatchMap k (Event t v)) -> Event t (PatchMap k v)
1032+coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ ffor e $ \(PatchMap m) -> mergeMap $ ffor m $ \case
1033+ Nothing -> fmapCheap (const Nothing) e
1034+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1035+
1036+-- | See 'coincidencePatchMap'
1037+coincidencePatchIntMap :: Reflex t => Event t (PatchIntMap (Event t v)) -> Event t (PatchIntMap v)
1038+coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ ffor e $ \(PatchIntMap m) -> mergeIntMap $ ffor m $ \case
1039+ Nothing -> fmapCheap (const Nothing) e
1040+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
1041+
1042+-- | See 'coincidencePatchMap'
1043+coincidencePatchMapWithMove :: (Reflex t, Ord k) => Event t (PatchMapWithMove k (Event t v)) -> Event t (PatchMapWithMove k v)
1044+coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ ffor e $ \p -> mergeMap $ ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1045+ PatchMapWithMove.From_Delete -> fforCheap e $ \_ ->
1046+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1047+ PatchMapWithMove.From_Move k -> fforCheap e $ \_ ->
1048+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1049+ PatchMapWithMove.From_Insert ev -> leftmost
1050+ [ fforCheap ev $ \v ->
1051+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1052+ , fforCheap e $ \_ ->
1053+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1054+ ]
1055+
1056+-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
1057+-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
1058+-- fires with the patched value.
1059+switchHoldPromptOnlyIncremental
1060+ :: forall t m p pt w
1061+ . ( Reflex t
1062+ , MonadHold t m
1063+ , Patch (p (Event t w))
1064+ , PatchTarget (p (Event t w)) ~ pt (Event t w)
1065+ , Patch (p w)
1066+ , PatchTarget (p w) ~ pt w
1067+ , Monoid (pt w)
1068+ )
1069+ => (Incremental t (p (Event t w)) -> Event t (pt w))
1070+ -> (Event t (p (Event t w)) -> Event t (p w))
1071+ -> pt (Event t w)
1072+ -> Event t (p (Event t w))
1073+ -> m (Event t (pt w))
1074+switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = do
1075+ lag <- mergePatchIncremental <$> holdIncremental e0 e'
1076+ pure $ ffor (align lag (coincidencePatch e')) $ \case
1077+ This old -> old
1078+ That new -> new `applyAlways` mempty
1079+ These old new -> new `applyAlways` old
1080+
867instance Reflex t => Align (Event t) where
1081instance Reflex t => Align (Event t) where
868 nil = never
1082 nil = never
1083+#if MIN_VERSION_these(0, 8, 0)
1084+instance Reflex t => Semialign (Event t) where
1085+#endif
869 align = alignEventWithMaybe Just
1086 align = alignEventWithMaybe Just
870
1087
1088+#ifdef MIN_VERSION_semialign
1089+#if MIN_VERSION_semialign(1,1,0)
1090+instance Reflex t => Zip (Event t) where
1091+#endif
1092+ zip x y = mapMaybe justThese $ align x y
1093+#endif
1094+
1095+
871-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
1096-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
872-- the 'Behavior' is true at the time of occurrence.
1097-- the 'Behavior' is true at the time of occurrence.
873gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
1098gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
874gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
1099gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
875
1100
876−-- | Create a new behavior given a starting behavior and switch to a the behvior
1101+-- | Create a new behavior given a starting behavior and switch to the behavior
877−-- carried by the event when it fires.
1102+-- carried by the event when it fires.
878switcher :: (Reflex t, MonadHold t m)
1103switcher :: (Reflex t, MonadHold t m)
879 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
1104 => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
880switcher b eb = pull . (sample <=< sample) <$> hold b eb
1105switcher b eb = pull . (sample <=< sample) <$> hold b eb
@@ -889,7 +1114,7 @@ zipDyn = zipDynWith (,)
889
1114
890-- | Combine two 'Dynamic's with a combining function. The result will change
1115-- | Combine two 'Dynamic's with a combining function. The result will change
891-- whenever either (or both) input 'Dynamic' changes.
1116-- whenever either (or both) input 'Dynamic' changes.
892−-- More efficient than liftA2.
1117+-- More efficient than 'liftA2'.
893zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
1118zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
894zipDynWith f da db =
1119zipDynWith f da db =
895 let eab = align (updated da) (updated db)
1120 let eab = align (updated da) (updated db)
@@ -907,11 +1132,7 @@ zipDynWith f da db =
907
1132
908instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
1133instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
909 (<>) = zipDynWith (<>)
1134 (<>) = zipDynWith (<>)
910−#if MIN_VERSION_semigroups(0,17,0)
911 stimes n = fmap $ stimes n
1135 stimes n = fmap $ stimes n
912−#else
913− times1p n = fmap $ times1p n
914−#endif
915
1136
916instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
1137instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
917 mconcat = distributeListOverDynWith mconcat
1138 mconcat = distributeListOverDynWith mconcat
@@ -920,14 +1141,23 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
920
1141
921-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1142-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
922-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1143-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
923−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
1144+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
924distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1145distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
925−distributeDMapOverDynPure dm = case DMap.toList dm of
1146+distributeDMapOverDynPure = distributeDMapOverDynPureG coerceDynamic
1147+
1148+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1149+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1150+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1151+distributeDMapOverDynPureG
1152+ :: forall t k q v. (Reflex t, GCompare k)
1153+ => (forall a. q a -> Dynamic t (v a))
1154+ -> DMap k q -> Dynamic t (DMap k v)
1155+distributeDMapOverDynPureG nt dm = case DMap.toList dm of
926 [] -> constDyn DMap.empty
1156 [] -> constDyn DMap.empty
927− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1157+ [k :=> v] -> DMap.singleton k <$> nt v
928 _ ->
1158 _ ->
929− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1159+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
930− edmPre = merge $ DMap.map updated dm
1160+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
931 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
1161 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
932 olds <- sample $ current result
1162 olds <- sample $ current result
933 return $ DMap.unionWithKey (\_ _ new -> new) olds news
1163 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -939,21 +1169,32 @@ distributeListOverDyn = distributeListOverDynWith id
939
1169
940-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
1170-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
941distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
1171distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
942−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
1172+distributeListOverDynWith f =
1173+ fmap (f . map fromDSum . DMap.toAscList) .
1174+ distributeDMapOverDynPure .
1175+ DMap.fromDistinctAscList .
1176+ zipWith toDSum [0..]
1177+ where
1178+ toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
1179+ toDSum k v = Const2 k :=> v
1180+ fromDSum :: DSum (Const2 Int a) Identity -> a
1181+ fromDSum (Const2 _ :=> Identity v) = v
943
1182
944-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
1183-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
945-- unless the second supplied 'Event' occurs simultaneously.
1184-- unless the second supplied 'Event' occurs simultaneously.
946difference :: Reflex t => Event t a -> Event t b -> Event t a
1185difference :: Reflex t => Event t a -> Event t b -> Event t a
947difference = alignEventWithMaybe $ \case
1186difference = alignEventWithMaybe $ \case
948 This a -> Just a
1187 This a -> Just a
949− _ -> Nothing
1188+ _ -> Nothing
950
1189
1190+-- | Zips two values by taking the union of their shapes and combining with the provided function.
1191+-- 'Nothing' values are dropped.
951alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1192alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
952−alignEventWithMaybe f ea eb =
1193+alignEventWithMaybe f ea eb = mapMaybe (f <=< dmapToThese) $
953− fmapMaybe (f <=< dmapToThese)
954− $ merge
955− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1194+ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
956
1195
1196+-- | Produces an 'Event' that fires only when the input event fires with a 'DSum' key that
1197+-- matches the provided key.
957filterEventKey
1198filterEventKey
958 :: forall t m k v a.
1199 :: forall t m k v a.
959 ( Reflex t
1200 ( Reflex t
@@ -971,7 +1212,10 @@ filterEventKey k kv' = do
971 Nothing -> Nothing
1212 Nothing -> Nothing
972 takeWhileJustE f kv'
1213 takeWhileJustE f kv'
973
1214
974−
1215+-- | "Factor" the input 'DSum' 'Event' to produce an 'Event' which
1216+-- fires when the 'DSum' key changes and contains both the value of the
1217+-- 'DSum' at switchover and an 'Event' of values produced by subsequent
1218+-- firings of the input 'Event' that do not change the 'DSum' key.
975factorEvent
1219factorEvent
976 :: forall t m k v a.
1220 :: forall t m k v a.
977 ( Reflex t
1221 ( Reflex t
@@ -983,9 +1227,9 @@ factorEvent
983 -> Event t (DSum k v)
1227 -> Event t (DSum k v)
984 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
1228 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
985factorEvent k0 kv' = do
1229factorEvent k0 kv' = do
986− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1230+ key :: Behavior t (Some k) <- hold (Some k0) $ fmapCheap (\(k :=> _) -> Some k) kv'
987 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1231 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
988− Some.This oldKey -> case newKey `geq` oldKey of
1232+ Some oldKey -> case newKey `geq` oldKey of
989 Just Refl -> return Nothing
1233 Just Refl -> return Nothing
990 Nothing -> do
1234 Nothing -> do
991 newInner <- filterEventKey newKey kv'
1235 newInner <- filterEventKey newKey kv'
@@ -1021,26 +1265,94 @@ class Reflex t => Accumulator t f | f -> t where
1021 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
1265 mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
1022 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1266 mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1023
1267
1268+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1269+-- with the provided function. See 'foldDyn'.
1270+accumDyn
1024−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1271+ :: (Reflex t, MonadHold t m, MonadFix m)
1272+ => (a -> b -> a)
1273+ -> a
1274+ -> Event t b
1275+ -> m (Dynamic t a)
1025accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1276accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1277+
1278+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1279+-- with the provided 'PushM' action.
1280+accumMDyn
1026−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
1281+ :: (Reflex t, MonadHold t m, MonadFix m)
1282+ => (a -> b -> PushM t a)
1283+ -> a
1284+ -> Event t b
1285+ -> m (Dynamic t a)
1027accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1286accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1287+
1288+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1289+-- with the provided function, discarding 'Nothing' results.
1290+accumMaybeDyn
1028−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1291+ :: (Reflex t, MonadHold t m, MonadFix m)
1292+ => (a -> b -> Maybe a)
1293+ -> a
1294+ -> Event t b
1295+ -> m (Dynamic t a)
1029accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1296accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1297+
1298+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1299+-- with the provided 'PushM' action, discarding 'Nothing' results.
1300+accumMaybeMDyn
1030−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
1301+ :: (Reflex t, MonadHold t m, MonadFix m)
1302+ => (a -> b -> PushM t (Maybe a))
1303+ -> a
1304+ -> Event t b
1305+ -> m (Dynamic t a)
1031accumMaybeMDyn f z e = do
1306accumMaybeMDyn f z e = do
1032 rec let e' = flip push e $ \o -> do
1307 rec let e' = flip push e $ \o -> do
1033 v <- sample $ current d'
1308 v <- sample $ current d'
1034 f v o
1309 f v o
1035 d' <- holdDyn z e'
1310 d' <- holdDyn z e'
1036 return d'
1311 return d'
1312+
1313+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event'
1314+-- with a function that both accumulates and produces a value to fire
1315+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
1316+mapAccumDyn
1037−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1317+ :: (Reflex t, MonadHold t m, MonadFix m)
1318+ => (a -> b -> (a, c))
1319+ -> a
1320+ -> Event t b
1321+ -> m (Dynamic t a, Event t c)
1038mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1322mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1323+
1324+-- | Similar to 'mapAccumDyn' except that the combining function is a
1325+-- 'PushM' action.
1326+mapAccumMDyn
1039−mapAccumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1327+ :: (Reflex t, MonadHold t m, MonadFix m)
1328+ => (a -> b -> PushM t (a, c))
1329+ -> a
1330+ -> Event t b
1331+ -> m (Dynamic t a, Event t c)
1040mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1332mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1333+
1334+-- | Accumulate a 'Dynamic' by folding occurrences of an 'Event' with
1335+-- a function that both optionally accumulates and optionally produces
1336+-- a value to fire as a separate output 'Event'.
1337+-- Note that because 'Nothing's are discarded in both cases, the output
1338+-- 'Event' may fire even though the output 'Dynamic' has not changed, and
1339+-- the output 'Dynamic' may update even when the output 'Event' is not firing.
1340+mapAccumMaybeDyn
1041−mapAccumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1341+ :: (Reflex t, MonadHold t m, MonadFix m)
1342+ => (a -> b -> (Maybe a, Maybe c))
1343+ -> a
1344+ -> Event t b
1345+ -> m (Dynamic t a, Event t c)
1042mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1346mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1347+
1348+-- | Like 'mapAccumMaybeDyn' except that the combining function is a
1349+-- 'PushM' action.
1350+mapAccumMaybeMDyn
1043−mapAccumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1351+ :: (Reflex t, MonadHold t m, MonadFix m)
1352+ => (a -> b -> PushM t (Maybe a, Maybe c))
1353+ -> a
1354+ -> Event t b
1355+ -> m (Dynamic t a, Event t c)
1044mapAccumMaybeMDyn f z e = do
1356mapAccumMaybeMDyn f z e = do
1045 rec let e' = flip push e $ \o -> do
1357 rec let e' = flip push e $ \o -> do
1046 v <- sample $ current d'
1358 v <- sample $ current d'
@@ -1048,18 +1360,42 @@ mapAccumMaybeMDyn f z e = do
1048 return $ case result of
1360 return $ case result of
1049 (Nothing, Nothing) -> Nothing
1361 (Nothing, Nothing) -> Nothing
1050 _ -> Just result
1362 _ -> Just result
1051− d' <- holdDyn z $ fmapMaybe fst e'
1363+ d' <- holdDyn z $ mapMaybe fst e'
1052− return (d', fmapMaybe snd e')
1364+ return (d', mapMaybe snd e')
1053
1365
1366+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1367+-- with the provided function.
1054{-# INLINE accumB #-}
1368{-# INLINE accumB #-}
1369+accumB
1055−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1370+ :: (Reflex t, MonadHold t m, MonadFix m)
1371+ => (a -> b -> a)
1372+ -> a
1373+ -> Event t b
1374+ -> m (Behavior t a)
1056accumB f = accumMaybeB $ \v o -> Just $ f v o
1375accumB f = accumMaybeB $ \v o -> Just $ f v o
1376+
1377+-- | Like 'accumB' except that the combining function is a 'PushM' action.
1057{-# INLINE accumMB #-}
1378{-# INLINE accumMB #-}
1379+accumMB
1058−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
1380+ :: (Reflex t, MonadHold t m, MonadFix m)
1381+ => (a -> b -> PushM t a)
1382+ -> a
1383+ -> Event t b
1384+ -> m (Behavior t a)
1059accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1385accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1386+
1387+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1388+-- with the provided function, discarding 'Nothing' results.
1060{-# INLINE accumMaybeB #-}
1389{-# INLINE accumMaybeB #-}
1390+accumMaybeB
1061−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1391+ :: (Reflex t, MonadHold t m, MonadFix m)
1392+ => (a -> b -> Maybe a)
1393+ -> a
1394+ -> Event t b
1395+ -> m (Behavior t a)
1062accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1396accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1397+
1398+-- | Like 'accumMaybeB' except that the combining function is a 'PushM' action.
1063{-# INLINE accumMaybeMB #-}
1399{-# INLINE accumMaybeMB #-}
1064accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
1400accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
1065accumMaybeMB f z e = do
1401accumMaybeMB f z e = do
@@ -1068,16 +1404,42 @@ accumMaybeMB f z e = do
1068 f v o
1404 f v o
1069 d' <- hold z e'
1405 d' <- hold z e'
1070 return d'
1406 return d'
1407+
1408+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
1409+-- with a function that both accumulates and produces a value to fire
1410+-- as an 'Event'. Returns both the accumulated value and an 'Event'.
1071{-# INLINE mapAccumB #-}
1411{-# INLINE mapAccumB #-}
1412+mapAccumB
1072−mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1413+ :: (Reflex t, MonadHold t m, MonadFix m)
1414+ => (a -> b -> (a, c))
1415+ -> a
1416+ -> Event t b
1417+ -> m (Behavior t a, Event t c)
1073mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1418mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1419+
1420+-- | Like 'mapAccumB' except that the combining function is a 'PushM' action.
1074{-# INLINE mapAccumMB #-}
1421{-# INLINE mapAccumMB #-}
1422+mapAccumMB
1075−mapAccumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1423+ :: (Reflex t, MonadHold t m, MonadFix m)
1424+ => (a -> b -> PushM t (a, c))
1425+ -> a
1426+ -> Event t b
1427+ -> m (Behavior t a, Event t c)
1076mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1428mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1429+
1430+-- | Accumulate a 'Behavior' by folding occurrences of an 'Event' with
1431+-- a function that both optionally accumulates and optionally produces
1432+-- a value to fire as a separate output 'Event'. 'Nothing's are discarded.
1077{-# INLINE mapAccumMaybeB #-}
1433{-# INLINE mapAccumMaybeB #-}
1434+mapAccumMaybeB
1078−mapAccumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1435+ :: (Reflex t, MonadHold t m, MonadFix m)
1436+ => (a -> b -> (Maybe a, Maybe c))
1437+ -> a
1438+ -> Event t b
1439+ -> m (Behavior t a, Event t c)
1079mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
1440mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
1080
1441
1442+-- | Like 'mapAccumMaybeB' except that the combining function is a 'PushM' action.
1081{-# INLINE mapAccumMaybeMB #-}
1443{-# INLINE mapAccumMaybeMB #-}
1082mapAccumMaybeMB :: (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)
1444mapAccumMaybeMB :: (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)
1083mapAccumMaybeMB f z e = do
1445mapAccumMaybeMB f z e = do
@@ -1087,8 +1449,8 @@ mapAccumMaybeMB f z e = do
1087 return $ case result of
1449 return $ case result of
1088 (Nothing, Nothing) -> Nothing
1450 (Nothing, Nothing) -> Nothing
1089 _ -> Just result
1451 _ -> Just result
1090− d' <- hold z $ fmapMaybe fst e'
1452+ d' <- hold z $ mapMaybe fst e'
1091− return (d', fmapMaybe snd e')
1453+ return (d', mapMaybe snd e')
1092
1454
1093-- | Accumulate occurrences of an 'Event', producing an output occurrence each
1455-- | Accumulate occurrences of an 'Event', producing an output occurrence each
1094-- time. Discard the underlying 'Accumulator'.
1456-- time. Discard the underlying 'Accumulator'.
@@ -1232,38 +1594,70 @@ infixl 4 <@
1232-- Cheap Functions
1594-- Cheap Functions
1233------------------
1595------------------
1234
1596
1597+-- | A "cheap" version of 'pushAlways'. See the performance note on 'pushCheap'.
1235{-# INLINE pushAlwaysCheap #-}
1598{-# INLINE pushAlwaysCheap #-}
1236pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1599pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1237pushAlwaysCheap f = pushCheap (fmap Just . f)
1600pushAlwaysCheap f = pushCheap (fmap Just . f)
1238
1601
1602+-- | A "cheap" version of 'mapMaybe'. See the performance note on 'pushCheap'.
1603+{-# INLINE mapMaybeCheap #-}
1604+mapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1605+mapMaybeCheap f = pushCheap $ return . f
1606+
1607+-- | An alias for 'mapMaybeCheap'
1239{-# INLINE fmapMaybeCheap #-}
1608{-# INLINE fmapMaybeCheap #-}
1240fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1609fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1241−fmapMaybeCheap f = pushCheap $ return . f
1610+fmapMaybeCheap = mapMaybeCheap
1611+
1242
1612
1613+-- | A "cheap" version of 'fforMaybe'. See the performance note on 'pushCheap'.
1243{-# INLINE fforMaybeCheap #-}
1614{-# INLINE fforMaybeCheap #-}
1244fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1615fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1245−fforMaybeCheap = flip fmapMaybeCheap
1616+fforMaybeCheap = flip mapMaybeCheap
1246
1617
1618+-- | A "cheap" version of 'ffor'. See the performance note on 'pushCheap'.
1247{-# INLINE fforCheap #-}
1619{-# INLINE fforCheap #-}
1248fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1620fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1249fforCheap = flip fmapCheap
1621fforCheap = flip fmapCheap
1250
1622
1623+-- | A "cheap" version of 'fmap'. See the performance note on 'pushCheap'.
1251{-# INLINE fmapCheap #-}
1624{-# INLINE fmapCheap #-}
1252fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1625fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1253fmapCheap f = pushCheap $ return . Just . f
1626fmapCheap f = pushCheap $ return . Just . f
1254
1627
1628+-- | A "cheap" version of 'tag'. See the performance note on 'pushCheap'.
1255{-# INLINE tagCheap #-}
1629{-# INLINE tagCheap #-}
1256tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1630tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1257tagCheap b = pushAlwaysCheap $ \_ -> sample b
1631tagCheap b = pushAlwaysCheap $ \_ -> sample b
1258
1632
1633+-- | Merge a collection of events; the resulting 'Event' will only occur if at
1634+-- least one input event is occurring, and will contain all of the input keys
1635+-- that are occurring simultaneously
1636+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
1637+merge = mergeG coerceEvent
1638+{-# INLINE merge #-}
1639+
1640+-- | Create a merge whose parents can change over time
1641+mergeIncremental :: (Reflex t, GCompare k)
1642+ => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
1643+mergeIncremental = mergeIncrementalG coerceEvent
1644+
1645+-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
1646+mergeIncrementalWithMove :: (Reflex t, GCompare k)
1647+ => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
1648+mergeIncrementalWithMove = mergeIncrementalWithMoveG coerceEvent
1649+
1650+-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
1259{-# INLINE mergeWithCheap #-}
1651{-# INLINE mergeWithCheap #-}
1260mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1652mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1261mergeWithCheap = mergeWithCheap' id
1653mergeWithCheap = mergeWithCheap' id
1262
1654
1655+-- | A "cheap" version of 'mergeWithCheap''. See the performance note on 'pushCheap'.
1263{-# INLINE mergeWithCheap' #-}
1656{-# INLINE mergeWithCheap' #-}
1264mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1657mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1265mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1658mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1266
1659
1660+-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
1267{-# INLINE mergeWithFoldCheap' #-}
1661{-# INLINE mergeWithFoldCheap' #-}
1268mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1662mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1269mergeWithFoldCheap' f es =
1663mergeWithFoldCheap' f es =
@@ -1276,31 +1670,11 @@ mergeWithFoldCheap' f es =
1276-- Deprecated functions
1670-- Deprecated functions
1277--------------------------------------------------------------------------------
1671--------------------------------------------------------------------------------
1278
1672
1279−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1280−-- occurs. If both occur at the same time they are combined using 'mappend'.
1281−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1282−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1283−appendEvents = alignWith $ mergeThese mappend
1284−
1285−-- | Alias for 'headE'
1286−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1287−onceE :: MonadHold t m => Event t a -> m (Event t a)
1288−onceE = headE
1289−
1290−-- | Run both sides of a 'These' monadically, combining the results.
1291−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1292−#ifdef USE_TEMPLATE_HASKELL
1293−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1294−#endif
1295−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1296−sequenceThese t = case t of
1297− This ma -> fmap This ma
1298− These ma mb -> liftM2 These ma mb
1299− That mb -> fmap That mb
1300−
1301{-# 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'. " #-}
1673{-# DEPRECATED switchPromptly "Use 'switchHoldPromptly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1674+-- | See 'switchHoldPromptly'
1302switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1675switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1303switchPromptly = switchHoldPromptly
1676switchPromptly = switchHoldPromptly
1304{-# 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'. " #-}
1677{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1678+-- | See 'switchHoldPromptOnly'
1305switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1679switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1306switchPromptOnly = switchHoldPromptOnly
1680switchPromptOnly = switchHoldPromptOnly
src/Reflex/Collection.hs+157−53
@@ -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,13 @@ module Reflex.Collection
25 , simpleList
27 , simpleList
26 ) where
28 ) where
27
29
30+#ifdef MIN_VERSION_semialign
31+import Prelude hiding (zip, zipWith)
32+#if MIN_VERSION_semialign(1,1,0)
33+import Data.Zip (Zip (..))
34+#endif
35+#endif
36+
28import Control.Monad.Identity
37import Control.Monad.Identity
29import Data.Align
38import Data.Align
30import Data.Functor.Misc
39import Data.Functor.Misc
@@ -38,84 +47,179 @@ import Reflex.Adjustable.Class
38import Reflex.Dynamic
47import Reflex.Dynamic
39import Reflex.PostBuild.Class
48import Reflex.PostBuild.Class
40
49
50+-- | Create a set of widgets based on the provided 'Map'. When the
51+-- input 'Event' fires, remove widgets for keys with the value 'Nothing'
52+-- and add/replace widgets for keys with 'Just' values.
53+listHoldWithKey
54+ :: forall t m k v a
41−listHoldWithKey :: forall t m k v a. (Ord k, Adjustable t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
55+ . (Ord k, Adjustable t m, MonadHold t m)
56+ => Map k v
57+ -> Event t (Map k (Maybe v))
58+ -> (k -> v -> m a)
59+ -> m (Dynamic t (Map k a))
42listHoldWithKey m0 m' f = do
60listHoldWithKey m0 m' f = do
43 let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
61 let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
62+ dm' = fmap
44− dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
63+ (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey
64+ (\k v -> ComposeMaybe $ fmap (f k) v)
65+ )
66+ m'
45 (a0, a') <- sequenceDMapWithAdjust dm0 dm'
67 (a0, a') <- sequenceDMapWithAdjust dm0 dm'
46− fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
47
68
69+ --TODO: Move the dmapToMap to the righthand side so it doesn't get
70+ --fully redone every time
71+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a'
72+
48−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
73+--TODO: Something better than Dynamic t (Map k v) - we want something
74+--where the Events carry diffs, not the whole value
75+listWithKey
76+ :: forall t k v m a
49−listWithKey :: forall t k v m a. (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
77+ . (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m)
78+ => Dynamic t (Map k v)
79+ -> (k -> Dynamic t v -> m a)
80+ -> m (Dynamic t (Map k a))
50listWithKey vals mkChild = do
81listWithKey vals mkChild = do
51 postBuild <- getPostBuild
82 postBuild <- getPostBuild
52 let childValChangedSelector = fanMap $ updated vals
83 let childValChangedSelector = fanMap $ updated vals
84+
53− -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey
85+ -- We keep track of changes to children values in the mkChild
86+ -- function we pass to listHoldWithKey The other changes we need
54− -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges
87+ -- to keep track of are child insertions and
88+ -- deletions. diffOnlyKeyChanges keeps track of insertions and
55− -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for.
89+ -- deletions but ignores value changes, since they're already
90+ -- accounted for.
91+ diffOnlyKeyChanges olds news =
56− diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case
92+ flip Map.mapMaybe (align olds news) $ \case
57− This _ -> Just Nothing
93+ This _ -> Just Nothing
58− These _ _ -> Nothing
94+ These _ _ -> Nothing
59− That new -> Just $ Just new
95+ That new -> Just $ Just new
60 rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
96 rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
61 let changeVals :: Event t (Map k (Maybe v))
97 let changeVals :: Event t (Map k (Maybe v))
98+ changeVals =
62− changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
99+ attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
63− [ updated vals
100+ [ updated vals
101+
102+ -- TODO: This should probably be added to the
103+ -- attachWith, not to the updated; if we were using
64− , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
104+ -- diffMap instead of diffMapNoEq, I think it might not
105+ -- work
106+ , tag (current vals) postBuild
65− ]
107+ ]
66 listHoldWithKey Map.empty changeVals $ \k v ->
108 listHoldWithKey Map.empty changeVals $ \k v ->
67 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
109 mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
68
110
69−{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
111+-- | Display the given map of items (in key order) using the builder
70−listWithKey' :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
112+-- function provided, and update it with the given event. 'Nothing'
71−listWithKey' = listWithKeyShallowDiff
113+-- update entries will delete the corresponding children, and 'Just'
72−
114+-- entries will create them if they do not exist or send an update
73−-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
115+-- event to them if they do.
116+listWithKeyShallowDiff
74−listWithKeyShallowDiff :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
117+ :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m)
118+ => Map k v
119+ -> Event t (Map k (Maybe v))
120+ -> (k -> v -> Event t v -> m a)
121+ -> m (Dynamic t (Map k a))
75listWithKeyShallowDiff initialVals valsChanged mkChild = do
122listWithKeyShallowDiff initialVals valsChanged mkChild = do
76 let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
123 let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
77 sentVals <- foldDyn applyMap (void initialVals) $ fmap (fmap void) valsChanged
124 sentVals <- foldDyn applyMap (void initialVals) $ fmap (fmap void) valsChanged
78 let relevantPatch patch _ = case patch of
125 let relevantPatch patch _ = case patch of
126+
79− Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
127+ -- Even if we let a Nothing through when the element doesn't
128+ -- already exist, this doesn't cause a problem because it is
129+ -- ignored
130+ Nothing -> Just Nothing
131+
80− Just _ -> Nothing -- We don't want to let spurious re-creations of items through
132+ -- We don't want to let spurious re-creations of items through
133+ Just _ -> Nothing
134+ listHoldWithKey
135+ initialVals
81− listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
136+ (attachWith (flip (Map.differenceWith relevantPatch))
137+ (current sentVals)
138+ valsChanged
139+ )
82− mkChild k v $ select childValChangedSelector $ Const2 k
140+ $ \k v -> mkChild k v $ select childValChangedSelector $ Const2 k
83−
141+
84−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
142+--TODO: Something better than Dynamic t (Map k v) - we want something
143+--where the Events carry diffs, not the whole value
85−-- | Create a dynamically-changing set of Event-valued widgets.
144+-- | Create a dynamically-changing set of Event-valued widgets. This
86−-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
145+-- is like 'listWithKey', specialized for widgets returning @/Event t a/@.
146+-- 'listWithKey' would return @/Dynamic t (Map k (Event t a))/@ in
147+-- this scenario, but 'listViewWithKey' flattens this to
148+-- @/Event t (Map k a)/@ via 'switch'.
149+listViewWithKey
87−listViewWithKey :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
150+ :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m)
151+ => Dynamic t (Map k v)
152+ -> (k -> Dynamic t v -> m (Event t a))
153+ -> m (Event t (Map k a))
154+listViewWithKey vals mkChild =
88−listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
155+ switch . fmap mergeMap <$> listViewWithKey' vals mkChild
89−
156+
157+listViewWithKey'
90−listViewWithKey' :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
158+ :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m)
159+ => Dynamic t (Map k v)
160+ -> (k -> Dynamic t v -> m a)
161+ -> m (Behavior t (Map k a))
91listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
162listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
92
163
93−-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
164+-- | Create a dynamically-changing set of widgets, one of which is
165+-- selected at any time.
166+selectViewListWithKey
167+ :: forall t m k v a
94−selectViewListWithKey :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
168+ . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
169+ => Dynamic t k
95− => Dynamic t k -- ^ Current selection key
170+ -- ^ Current selection key
171+ -> Dynamic t (Map k v)
96− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
172+ -- ^ Dynamic key/value map
173+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a))
174+ -- ^ Function to create a widget for a given key from Dynamic value
97− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
175+ -- and Dynamic Bool indicating if this widget is currently selected
176+ -> m (Event t (k, a))
98− -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
177+ -- ^ Event that fires when any child's return Event fires. Contains
178+ -- key of an arbitrary firing widget.
99selectViewListWithKey selection vals mkChild = do
179selectViewListWithKey selection vals mkChild = do
100− let selectionDemux = demux selection -- For good performance, this value must be shared across all children
180+ -- For good performance, this value must be shared across all children
181+ let selectionDemux = demux selection
101 selectChild <- listWithKey vals $ \k v -> do
182 selectChild <- listWithKey vals $ \k v -> do
102 let selected = demuxed selectionDemux k
183 let selected = demuxed selectionDemux k
103 selectSelf <- mkChild k v selected
184 selectSelf <- mkChild k v selected
104 return $ fmap ((,) k) selectSelf
185 return $ fmap ((,) k) selectSelf
105 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
186 return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
106
187
188+-- | Like 'selectViewListWithKey' but discards the value of the list
189+-- item widget's output 'Event'.
190+selectViewListWithKey_
191+ :: forall t m k v a
107−selectViewListWithKey_ :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
192+ . (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
193+ => Dynamic t k
108− => Dynamic t k -- ^ Current selection key
194+ -- ^ Current selection key
195+ -> Dynamic t (Map k v)
109− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
196+ -- ^ Dynamic key/value map
197+ -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a))
198+ -- ^ Function to create a widget for a given key from Dynamic value
110− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
199+ -- and Dynamic Bool indicating if this widget is currently selected
200+ -> m (Event t k)
111− -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
201+ -- ^ Event that fires when any child's return Event fires. Contains
202+ -- key of an arbitrary firing widget.
203+selectViewListWithKey_ selection vals mkChild =
112−selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
204+ fmap fst <$> selectViewListWithKey selection vals mkChild
113
205
114−-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
206+-- | Create a dynamically-changing set of widgets from a Dynamic
207+-- key/value map. Unlike the 'withKey' variants, the child widgets
115−-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
208+-- are insensitive to which key they're associated with.
209+list
116−list :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
210+ :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m)
211+ => Dynamic t (Map k v)
212+ -> (Dynamic t v -> m a)
213+ -> m (Dynamic t (Map k a))
117list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
214list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
118
215
119-- | Create a dynamically-changing set of widgets from a Dynamic list.
216-- | Create a dynamically-changing set of widgets from a Dynamic list.
217+simpleList
120−simpleList :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
218+ :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m)
219+ => Dynamic t [v]
220+ -> (Dynamic t v -> m a)
221+ -> m (Dynamic t [a])
222+simpleList xs mkChild =
121−simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
223+ fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap
224+ (Map.fromList . zip [(1 :: Int) ..])
225+ xs
src/Reflex/Dynamic.hs+35−185
@@ -17,8 +17,12 @@
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
@@ -58,6 +62,7 @@ module Reflex.Dynamic
58 , Demux
62 , Demux
59 , demux
63 , demux
60 , demuxed
64 , demuxed
65+ -- * Miscellaneous
61 -- Things that probably aren't very useful:
66 -- Things that probably aren't very useful:
62 , HList (..)
67 , HList (..)
63 , FHList (..)
68 , FHList (..)
@@ -67,34 +72,14 @@ module Reflex.Dynamic
67 , AllAreFunctors (..)
72 , AllAreFunctors (..)
68 , HListPtr (..)
73 , HListPtr (..)
69 , distributeFHListOverDynPure
74 , distributeFHListOverDynPure
70− -- Unsafe
75+ -- * Unsafe
71 , unsafeDynamic
76 , unsafeDynamic
72− -- * Deprecated functions
73− , apDyn
74− , attachDyn
75− , attachDynWith
76− , attachDynWithMaybe
77− , collectDyn
78− , combineDyn
79− , distributeDMapOverDyn
80− , distributeFHListOverDyn
81− , forDyn
82− , getDemuxed
83− , joinDyn
84− , mapDyn
85− , mconcatDyn
86− , nubDyn
87− , splitDyn
88− , tagDyn
89− , uniqDyn
90− , uniqDynBy
91 ) where
77 ) where
92
78
93import Data.Functor.Compose
79import Data.Functor.Compose
94import Data.Functor.Misc
80import Data.Functor.Misc
95import Reflex.Class
81import Reflex.Class
96
82
97−import Control.Applicative ((<*>))
98import Control.Monad
83import Control.Monad
99import Control.Monad.Fix
84import Control.Monad.Fix
100import Control.Monad.Identity
85import Control.Monad.Identity
@@ -102,11 +87,10 @@ import Data.Align
102import Data.Dependent.Map (DMap)
87import Data.Dependent.Map (DMap)
103import qualified Data.Dependent.Map as DMap
88import qualified Data.Dependent.Map as DMap
104import Data.Dependent.Sum (DSum (..))
89import Data.Dependent.Sum (DSum (..))
105−import Data.Functor.Product
106import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
90import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
107import Data.Map (Map)
91import Data.Map (Map)
108import Data.Maybe
92import Data.Maybe
109−import Data.Monoid hiding (Product)
93+import Data.Monoid ((<>))
110import Data.These
94import Data.These
111
95
112import Debug.Trace
96import Debug.Trace
@@ -130,7 +114,7 @@ holdUniqDyn = holdUniqDynBy (==)
130holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
114holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
131holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
115holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
132
116
133−-- | Dynamic Maybe that can only update from Nothing to Just or Just to Just (i.e., cannot revert to Nothing)
117+-- | @/Dynamic Maybe/@ that can only update from @/Nothing/@ to @/Just/@ or @/Just/@ to @/Just/@ (i.e., cannot revert to @/Nothing/@)
134improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
118improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
135improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
119improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
136
120
@@ -195,9 +179,9 @@ switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
195switchDyn d = switch (current d)
179switchDyn d = switch (current d)
196
180
197-- | Switches to the new 'Event' whenever it receives one. Switching occurs
181-- | Switches to the new 'Event' whenever it receives one. Switching occurs
198−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
182+-- __before__ the inner 'Event' fires - so if the 'Dynamic' changes and both the
199-- old and new inner Events fire simultaneously, the output will fire with the
183-- old and new inner Events fire simultaneously, the output will fire with the
200−-- value of the *new* 'Event'.
184+-- value of the __new__ 'Event'.
201--
185--
202-- Prefer 'switchDyn' to this where possible. The timing requirements that
186-- Prefer 'switchDyn' to this where possible. The timing requirements that
203-- switching before imposes are likely to bring down your app unless you are
187-- switching before imposes are likely to bring down your app unless you are
@@ -219,26 +203,18 @@ distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithF
219
203
220-- | 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
221-- 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" #-}
222distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
207distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
223−distributeListOverDynPure =
208+distributeListOverDynPure = distributeListOverDyn
224− fmap (map fromDSum . DMap.toAscList) .
225− distributeDMapOverDynPure .
226− DMap.fromDistinctAscList .
227− zipWith toDSum [0..]
228− where
229− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
230− toDSum k v = Const2 k :=> v
231− fromDSum :: DSum (Const2 Int a) Identity -> a
232− fromDSum (Const2 _ :=> Identity v) = v
233
209
234--TODO: Generalize this to functors other than Maps
210--TODO: Generalize this to functors other than Maps
235-- | 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'
236-- with the current values of the 'Dynamic's in a map.
212-- with the current values of the 'Dynamic's in a map.
237joinDynThroughMap :: 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)
238−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
214+joinDynThroughMap = (distributeMapOverDynPure =<<)
239
215
240-- | 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
241−-- subsequent change that is observed (as traceEvent), prefixed with the
217+-- subsequent change that is observed (as 'traceEvent'), prefixed with the
242-- provided string. This should /only/ be used for debugging.
218-- provided string. This should /only/ be used for debugging.
243--
219--
244-- Note: Just like Debug.Trace.trace, the value will only be shown if something
220-- Note: Just like Debug.Trace.trace, the value will only be shown if something
@@ -248,7 +224,7 @@ traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
248
224
249-- | Print the result of applying the provided function to the value
225-- | Print the result of applying the provided function to the value
250-- of the 'Dynamic' when it is first read and on each subsequent change
226-- of the 'Dynamic' when it is first read and on each subsequent change
251−-- that is observed (as traceEvent). This should /only/ be used for
227+-- that is observed (as 'traceEvent'). This should /only/ be used for
252-- debugging.
228-- debugging.
253--
229--
254-- Note: Just like Debug.Trace.trace, the value will only be shown if something
230-- Note: Just like Debug.Trace.trace, the value will only be shown if something
@@ -264,27 +240,27 @@ traceDynWith f d =
264-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
240-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
265-- each time the 'Event' occurs.
241-- each time the 'Event' occurs.
266--
242--
267−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
243+-- Note: @/tagPromptlyDyn d e/@ differs from @/tag (current d) e/@ in the case that @/e/@ is firing
268−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
244+-- at the same time that @/d/@ is changing. With @/tagPromptlyDyn d e/@, the __new__ value of @/d/@
269−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
245+-- will replace the value of @/e/@, whereas with @/tag (current d) e/@, the __old__ value
270-- will be used, since the 'Behavior' won't be updated until the end of the frame.
246-- will be used, since the 'Behavior' won't be updated until the end of the frame.
271-- Additionally, this means that the output 'Event' may not be used to directly change
247-- Additionally, this means that the output 'Event' may not be used to directly change
272−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
248+-- the input 'Dynamic', because that would mean its value depends on itself. __When creating__
273−-- cyclic data flows, generally `tag (current d) e` is preferred.
249+-- __cyclic data flows, generally @/tag (current d) e/@ is preferred.__
274tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
250tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
275tagPromptlyDyn = attachPromptlyDynWith const
251tagPromptlyDyn = attachPromptlyDynWith const
276
252
277-- | Attach the current value of the 'Dynamic' to the value of the
253-- | Attach the current value of the 'Dynamic' to the value of the
278-- 'Event' each time it occurs.
254-- 'Event' each time it occurs.
279--
255--
280−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
256+-- Note: @/attachPromptlyDyn d/@ is not the same as @/attach (current d)/@. See 'tagPromptlyDyn' for details.
281attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
257attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
282attachPromptlyDyn = attachPromptlyDynWith (,)
258attachPromptlyDyn = attachPromptlyDynWith (,)
283
259
284-- | Combine the current value of the 'Dynamic' with the value of the
260-- | Combine the current value of the 'Dynamic' with the value of the
285-- 'Event' each time it occurs.
261-- 'Event' each time it occurs.
286--
262--
287−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
263+-- Note: @/attachPromptlyDynWith f d/@ is not the same as @/attachWith f (current d)/@. See 'tagPromptlyDyn' for details.
288attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
264attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
289attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
265attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
290
266
@@ -292,8 +268,7 @@ attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
292-- current value of the 'Dynamic' value and possibly filtering if the combining
268-- current value of the 'Dynamic' value and possibly filtering if the combining
293-- function returns 'Nothing'.
269-- function returns 'Nothing'.
294--
270--
295−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
271+-- Note: @/attachPromptlyDynWithMaybe f d/@ is not the same as @/attachWithMaybe f (current d)/@. See 'tagPromptlyDyn' for details.
296−-- (current d)`. See 'tagPromptlyDyn' for details.
297attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
272attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
298attachPromptlyDynWithMaybe f d e =
273attachPromptlyDynWithMaybe f d e =
299 let e' = attach (current d) e
274 let e' = attach (current d) e
@@ -302,8 +277,8 @@ attachPromptlyDynWithMaybe f d e =
302 These (_, b) a -> f a b -- Both events are firing, so use the newer value
277 These (_, b) a -> f a b -- Both events are firing, so use the newer value
303 That _ -> Nothing -- The tagging event isn't firing, so don't fire
278 That _ -> Nothing -- The tagging event isn't firing, so don't fire
304
279
305−-- | Factor a @Dynamic t (Maybe a)@ into a @Dynamic t (Maybe (Dynamic t a))@,
280+-- | Factor a @/Dynamic t (Maybe a)/@ into a @/Dynamic t (Maybe (Dynamic t a))/@,
306−-- such that the outer 'Dynamic' is updated only when the 'Maybe''s constructor
281+-- such that the outer 'Dynamic' is updated only when the "Maybe"'s constructor
307-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
282-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
308-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
283-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
309-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
284-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', 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+13−4
@@ -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,13 +35,14 @@ 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
41−import Reflex.Class
42import Reflex.Adjustable.Class
42import Reflex.Adjustable.Class
43+import Reflex.Class
43import Reflex.DynamicWriter.Class
44import Reflex.DynamicWriter.Class
45+import Reflex.EventWriter.Class (EventWriter, tellEvent)
44import Reflex.Host.Class
46import Reflex.Host.Class
45import qualified Reflex.Patch.MapWithMove as MapWithMove
47import qualified Reflex.Patch.MapWithMove as MapWithMove
46import Reflex.PerformEvent.Class
48import Reflex.PerformEvent.Class
@@ -86,7 +88,7 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
86 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
88 noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
87 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?
88
90
89−-- | A basic implementation of 'MonadDynamicWriter'.
91+-- | A basic implementation of 'DynamicWriter'.
90newtype 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 }
91 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
92
94
@@ -114,7 +116,7 @@ runDynamicWriterT (DynamicWriterT a) = do
114 (result, ws) <- runStateT a []
116 (result, ws) <- runStateT a []
115 return (result, mconcat $ reverse ws)
117 return (result, mconcat $ reverse ws)
116
118
117−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
119+instance (Monad m, Monoid w, Reflex t) => DynamicWriter t w (DynamicWriterT t w m) where
118 tellDyn w = DynamicWriterT $ modify (w :)
120 tellDyn w = DynamicWriterT $ modify (w :)
119
121
120instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
122instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
@@ -139,6 +141,10 @@ instance MonadState s m => MonadState s (DynamicWriterT t w m) where
139 get = lift get
141 get = lift get
140 put = lift . put
142 put = lift . put
141
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+
142newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
148newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
143
149
144-- | When the execution of a 'DynamicWriterT' action is adjusted using
150-- | When the execution of a 'DynamicWriterT' action is adjusted using
@@ -226,3 +232,6 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (DynamicWriterT t w m) wh
226 tellQueryIncremental = lift . tellQueryIncremental
232 tellQueryIncremental = lift . tellQueryIncremental
227 askQueryResult = lift askQueryResult
233 askQueryResult = lift askQueryResult
228 queryIncremental = lift . queryIncremental
234 queryIncremental = lift . queryIncremental
235+
236+instance EventWriter t w m => EventWriter t w (DynamicWriterT t v m) where
237+ tellEvent = lift . tellEvent
src/Reflex/DynamicWriter/Class.hs+10−5
@@ -1,24 +1,29 @@
1−-- | This module defines the 'MonadDynamicWriter' class.
1+-- | This module defines the 'DynamicWriter' class.
2+{-# LANGUAGE ConstraintKinds #-}
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+68−54
@@ -22,9 +22,10 @@ module Reflex.EventWriter.Base
22 , withEventWriterT
22 , withEventWriterT
23 ) where
23 ) where
24
24
25−import Reflex.Class
26import Reflex.Adjustable.Class
25import Reflex.Adjustable.Class
26+import Reflex.Class
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
27import Reflex.EventWriter.Class (EventWriter, tellEvent)
28+import Reflex.DynamicWriter.Class (DynamicWriter, tellDyn)
28import Reflex.Host.Class
29import Reflex.Host.Class
29import Reflex.PerformEvent.Class
30import Reflex.PerformEvent.Class
30import Reflex.PostBuild.Class
31import Reflex.PostBuild.Class
@@ -40,7 +41,6 @@ import Control.Monad.Ref
40import Control.Monad.State.Strict
41import Control.Monad.State.Strict
41import Data.Dependent.Map (DMap, DSum (..))
42import Data.Dependent.Map (DMap, DSum (..))
42import qualified Data.Dependent.Map as DMap
43import qualified Data.Dependent.Map as DMap
43−import Data.Foldable
44import Data.Functor.Compose
44import Data.Functor.Compose
45import Data.Functor.Misc
45import Data.Functor.Misc
46import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
46import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
@@ -48,6 +48,7 @@ import Data.IntMap.Strict (IntMap)
48import qualified Data.IntMap.Strict as IntMap
48import qualified Data.IntMap.Strict as IntMap
49import Data.List.NonEmpty (NonEmpty (..))
49import Data.List.NonEmpty (NonEmpty (..))
50import Data.Map (Map)
50import Data.Map (Map)
51+import qualified Data.Map as Map
51import Data.Semigroup
52import Data.Semigroup
52import Data.Some (Some)
53import Data.Some (Some)
53import Data.Tuple
54import Data.Tuple
@@ -57,7 +58,7 @@ import Unsafe.Coerce
57
58
58{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
59{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
59newtype TellId w x
60newtype TellId w x
60− = TellId Int -- ^ WARNING: Do not construct this directly; use 'TellId' instead
61+ = TellId Int -- ^ WARNING: Do not construct this directly; use 'tellId' instead
61 deriving (Show, Eq, Ord, Enum)
62 deriving (Show, Eq, Ord, Enum)
62
63
63tellId :: Int -> TellId w w
64tellId :: Int -> TellId w w
@@ -135,9 +136,9 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
135
136
136instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
137instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
137 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
138 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
138− traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElements mergeIntIncremental
139+ traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') mergeIntIncremental coincidencePatchIntMap
139− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
140+ traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith mergeMapIncremental coincidencePatchMap
140− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
141+ traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeMapIncrementalWithMove coincidencePatchMapWithMove
141
142
142instance Requester t m => Requester t (EventWriterT t w m) where
143instance Requester t m => Requester t (EventWriterT t w m) where
143 type Request (EventWriterT t w m) = Request m
144 type Request (EventWriterT t w m) = Request m
@@ -160,19 +161,29 @@ runWithReplaceEventWriterTWith f a0 a' = do
160 return (fst result0, fmapCheap fst result')
161 return (fst result0, fmapCheap fst result')
161
162
162-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
163-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
163−sequenceIntMapWithAdjustEventWriterTWith :: forall t m p w v v'. (Reflex t, MonadHold t m, Semigroup w, Functor p, Patch (p (Event t w)), PatchTarget (p (Event t w)) ~ IntMap (Event t w))
164+sequenceIntMapWithAdjustEventWriterTWith
165+ :: forall t m p w v v'
166+ . ( Reflex t
167+ , MonadHold t m
168+ , Semigroup w
169+ , Functor p
170+ , Patch (p (Event t w))
171+ , PatchTarget (p (Event t w)) ~ IntMap (Event t w)
172+ , Patch (p w)
173+ , PatchTarget (p w) ~ IntMap w
174+ )
164− => ( (IntMap.Key -> v -> m (Event t w, v'))
175+ => ( (IntMap.Key -> v -> m (Event t w, v'))
165− -> IntMap v
176+ -> IntMap v
166− -> Event t (p v)
177+ -> Event t (p v)
167− -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
178+ -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
168− )
179+ )
169− -> (p (Event t w) -> [Event t w])
170− -> (Incremental t (p (Event t w)) -> Event t (IntMap w))
180+ -> (Incremental t (p (Event t w)) -> Event t (PatchTarget (p w)))
181+ -> (Event t (p (Event t w)) -> Event t (p w))
171− -> (IntMap.Key -> v -> EventWriterT t w m v')
182+ -> (IntMap.Key -> v -> EventWriterT t w m v')
172− -> IntMap v
183+ -> IntMap v
173− -> Event t (p v)
184+ -> Event t (p v)
174− -> EventWriterT t w m (IntMap v', Event t (p v'))
185+ -> EventWriterT t w m (IntMap v', Event t (p v'))
175−sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
186+sequenceIntMapWithAdjustEventWriterTWith base mergePatchIncremental coincidencePatch f dm0 dm' = do
176 let f' :: IntMap.Key -> v -> m (Event t w, v')
187 let f' :: IntMap.Key -> v -> m (Event t w, v')
177 f' k v = swap <$> runEventWriterT (f k v)
188 f' k v = swap <$> runEventWriterT (f k v)
178 (children0, children') <- base f' dm0 dm'
189 (children0, children') <- base f' dm0 dm'
@@ -182,26 +193,39 @@ sequenceIntMapWithAdjustEventWriterTWith base patchNewElements mergePatchIncreme
182 requests0 = fmap fst children0
193 requests0 = fmap fst children0
183 requests' :: Event t (p (Event t w))
194 requests' :: Event t (p (Event t w))
184 requests' = fmapCheap (fmap fst) children'
195 requests' = fmapCheap (fmap fst) children'
185− childRequestMap :: Incremental t (p (Event t w)) <- holdIncremental requests0 requests'
196+ e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
186− tellEventsPromptly requests' patchNewElements $ mergePatchIncremental childRequestMap
197+ tellEvent $ fforMaybeCheap e $ \m ->
198+ case IntMap.elems m of
199+ [] -> Nothing
200+ h : t -> Just $ sconcat $ h :| t
187 return (result0, result')
201 return (result0, result')
188
202
189-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
203-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
204+sequenceDMapWithAdjustEventWriterTWith
205+ :: forall t m p p' w k v v'
206+ . ( Reflex t
207+ , MonadHold t m
208+ , Semigroup w
209+ , Patch (p' (Some k) (Event t w))
190−sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
210+ , PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w)
211+ , GCompare k
212+ , Patch (p' (Some k) w)
213+ , PatchTarget (p' (Some k) w) ~ Map (Some k) w
214+ )
191− => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
215+ => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
192− -> DMap k v
216+ -> DMap k v
193− -> Event t (p k v)
217+ -> Event t (p k v)
194− -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
218+ -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
195− )
219+ )
196− -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
220+ -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
197− -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
221+ -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
198− -> (p' (Some k) (Event t w) -> [Event t w])
199− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
222+ -> (Incremental t (p' (Some k) (Event t w)) -> Event t (PatchTarget (p' (Some k) w)))
223+ -> (Event t (p' (Some k) (Event t w)) -> Event t (p' (Some k) w))
200− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
224+ -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
201− -> DMap k v
225+ -> DMap k v
202− -> Event t (p k v)
226+ -> Event t (p k v)
203− -> EventWriterT t w m (DMap k v', Event t (p k v'))
227+ -> EventWriterT t w m (DMap k v', Event t (p k v'))
204−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
228+sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith mergePatchIncremental coincidencePatch f dm0 dm' = do
205 let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
229 let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
206 f' k v = Compose . swap <$> runEventWriterT (f k v)
230 f' k v = Compose . swap <$> runEventWriterT (f k v)
207 (children0, children') <- base f' dm0 dm'
231 (children0, children') <- base f' dm0 dm'
@@ -211,26 +235,13 @@ sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewEle
211 requests0 = weakenDMapWith (fst . getCompose) children0
235 requests0 = weakenDMapWith (fst . getCompose) children0
212 requests' :: Event t (p' (Some k) (Event t w))
236 requests' :: Event t (p' (Some k) (Event t w))
213 requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
237 requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
214− childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
238+ e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
215− tellEventsPromptly requests' patchNewElements $ mergePatchIncremental childRequestMap
239+ tellEvent $ fforMaybeCheap e $ \m ->
240+ case Map.elems m of
241+ [] -> Nothing
242+ h : t -> Just $ sconcat $ h :| t
216 return (result0, result')
243 return (result0, result')
217
244
218−tellEventsPromptly
219− :: ( Foldable f
220− , Reflex t
221− , MonadHold t m
222− , EventWriter t w m
223− )
224− => Event t a
225− -> (a -> [Event t w])
226− -> Event t (f w)
227− -> m ()
228−tellEventsPromptly requests' patchNewElements mergedChildRequestMap = do
229− let patch0 = fforMaybeCheap mergedChildRequestMap $ \m -> case toList m of
230− [] -> Nothing
231− h : t -> Just $ sconcat $ h :| t
232− tellEvent =<< switchHoldPromptOnly patch0 (fmapCheap (mconcat . patchNewElements) requests')
233−
234instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
245instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
235 type Performable (EventWriterT t w m) = Performable m
246 type Performable (EventWriterT t w m) = Performable m
236 performEvent_ = lift . performEvent_
247 performEvent_ = lift . performEvent_
@@ -267,6 +278,9 @@ instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) wher
267 askQueryResult = lift askQueryResult
278 askQueryResult = lift askQueryResult
268 queryIncremental = lift . queryIncremental
279 queryIncremental = lift . queryIncremental
269
280
281+instance DynamicWriter t w m => DynamicWriter t w (EventWriterT t v m) where
282+ tellDyn = lift . tellDyn
283+
270instance PrimMonad m => PrimMonad (EventWriterT t w m) where
284instance PrimMonad m => PrimMonad (EventWriterT t w m) where
271 type PrimState (EventWriterT t w m) = PrimState m
285 type PrimState (EventWriterT t w m) = PrimState m
272 primitive = lift . primitive
286 primitive = lift . primitive
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+59−5
@@ -5,6 +5,19 @@
5{-# LANGUAGE ForeignFunctionInterface #-}
5{-# LANGUAGE ForeignFunctionInterface #-}
6{-# LANGUAGE JavaScriptFFI #-}
6{-# LANGUAGE JavaScriptFFI #-}
7#endif
7#endif
8+
9+-- |
10+-- Module:
11+-- Reflex.FastWeak
12+-- Description:
13+-- 'FastWeak' is a weak pointer to some value, and 'FastWeakTicket' ensures the value
14+-- referred to by a 'FastWeak' stays live while the ticket is held (live).
15+--
16+-- On GHC or GHCJS when not built with the @fast-weak@ cabal flag, 'FastWeak' is a wrapper
17+-- around the simple version of 'System.Mem.Weak.Weak' where the key and value are the same.
18+--
19+-- On GHCJS when built with the @fast-weak@ cabal flag, 'FastWeak' is implemented directly
20+-- in JS using @h$FastWeak@ and @h$FastWeakTicket@ which are a nonstandard part of the GHCJS RTS.
8module Reflex.FastWeak
21module Reflex.FastWeak
9 ( FastWeakTicket
22 ( FastWeakTicket
10 , FastWeak
23 , FastWeak
@@ -35,29 +48,57 @@ import System.Mem.Weak
35
48
36
49
37#ifdef GHCJS_FAST_WEAK
50#ifdef GHCJS_FAST_WEAK
51+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
52+-- can be used to get the referred to value without fear of @Nothing,
53+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
54+--
55+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeakTicket@.
38newtype FastWeakTicket a = FastWeakTicket JSVal
56newtype FastWeakTicket a = FastWeakTicket JSVal
39
57
58+-- | A reference to some value which can be garbage collected if there are only
59+-- weak references to the value left.
60+--
61+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
62+--
63+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using
64+-- 'getFastWeakTicket' if the value hasn't been collected yet.
65+--
66+-- Implemented by way of special support in the GHCJS RTS, @h$FastWeak@.
40newtype FastWeak a = FastWeak JSVal
67newtype FastWeak a = FastWeak JSVal
41
68
42-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
69-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
43data Val a = Val { unVal :: a }
70data Val a = Val { unVal :: a }
44
71
45−-- | Coerce a JSVal that represents the heap object of a value of type 'a' into a value of type 'a'
72+-- | Coerce a JSVal that represents the heap object of a value of type @a@ into a value of type @a@
46unsafeFromRawJSVal :: JSVal -> a
73unsafeFromRawJSVal :: JSVal -> a
47unsafeFromRawJSVal v = unVal (unsafeCoerce v)
74unsafeFromRawJSVal v = unVal (unsafeCoerce v)
48
75
76+-- | Coerce a heap object of type @a@ into a 'JSVal' which represents that object.
49unsafeToRawJSVal :: a -> JSVal
77unsafeToRawJSVal :: a -> JSVal
50unsafeToRawJSVal v = unsafeCoerce (Val v)
78unsafeToRawJSVal v = unsafeCoerce (Val v)
51#else
79#else
80+-- | A 'FastWeak' which has been promoted to a strong reference. 'getFastWeakTicketValue'
81+-- can be used to get the referred to value without fear of @Nothing@,
82+-- and 'getFastWeakTicketWeak' can be used to get the weak version.
52data FastWeakTicket a = FastWeakTicket
83data FastWeakTicket a = FastWeakTicket
53 { _fastWeakTicket_val :: !a
84 { _fastWeakTicket_val :: !a
54 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
85 , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
55 }
86 }
56
87
88+-- | A reference to some value which can be garbage collected if there are only weak references to the value left.
89+--
90+-- 'getFastWeakValue' can be used to try and obtain a strong reference to the value.
91+--
92+-- The value in a @FastWeak@ can also be kept alive by obtaining a 'FastWeakTicket' using 'getFastWeakTicket'
93+-- if the value hasn't been collected yet.
94+--
95+-- Synonymous with 'Weak'.
57type FastWeak a = Weak a
96type FastWeak a = Weak a
58#endif
97#endif
59
98
99+-- | Return the @a@ kept alive by the given 'FastWeakTicket'.
100+--
60−-- This needs to be in IO so we know that we've relinquished the ticket
101+-- This needs to be in IO so we know that we've relinquished the ticket.
61getFastWeakTicketValue :: FastWeakTicket a -> IO a
102getFastWeakTicketValue :: FastWeakTicket a -> IO a
62#ifdef GHCJS_FAST_WEAK
103#ifdef GHCJS_FAST_WEAK
63getFastWeakTicketValue t = do
104getFastWeakTicketValue t = do
@@ -69,6 +110,8 @@ foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a
69getFastWeakTicketValue = return . _fastWeakTicket_val
110getFastWeakTicketValue = return . _fastWeakTicket_val
70#endif
111#endif
71
112
113+-- | Get the value referred to by a 'FastWeak' if it hasn't yet been collected,
114+-- or @Nothing@ if it has been collected.
72getFastWeakValue :: FastWeak a -> IO (Maybe a)
115getFastWeakValue :: FastWeak a -> IO (Maybe a)
73#ifdef GHCJS_FAST_WEAK
116#ifdef GHCJS_FAST_WEAK
74getFastWeakValue w = do
117getFastWeakValue w = do
@@ -84,6 +127,9 @@ foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.v
84getFastWeakValue = deRefWeak
127getFastWeakValue = deRefWeak
85#endif
128#endif
86
129
130+-- | Try to create a 'FastWeakTicket' for the given 'FastWeak' which will ensure the value referred
131+-- remains alive. Returns @Just@ if the value hasn't been collected
132+-- and a ticket can therefore be obtained, @Nothing@ if it's been collected.
87getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
133getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88#ifdef GHCJS_FAST_WEAK
134#ifdef GHCJS_FAST_WEAK
89getFastWeakTicket w = do
135getFastWeakTicket w = do
@@ -103,8 +149,11 @@ getFastWeakTicket w = do
103 }
149 }
104#endif
150#endif
105
151
106−-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the associated FastWeak to actually be weak
152+-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
153+-- which can be obtained with 'getFastWeakTicketValue'.
107mkFastWeakTicket :: a -> IO (FastWeakTicket a)
154mkFastWeakTicket :: a -> IO (FastWeakTicket a)
155+-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
156+-- associated FastWeak to actually be weak
108#ifdef GHCJS_FAST_WEAK
157#ifdef GHCJS_FAST_WEAK
109mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
158mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
110
159
@@ -119,11 +168,16 @@ mkFastWeakTicket v = do
119 }
168 }
120#endif
169#endif
121
170
171+-- | Demote a 'FastWeakTicket'; which ensures the value is alive, to a 'FastWeak' which doesn't.
172+-- Note that unless the ticket for the same 'FastWeak' is held in some other way
173+-- the value might be collected immediately.
174+getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
122-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
175-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
123#ifdef GHCJS_FAST_WEAK
176#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
177+foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak' :: FastWeakTicket a -> IO (FastWeak a)
178+{-# INLINE getFastWeakTicketWeak #-}
179+getFastWeakTicketWeak = getFastWeakTicketWeak'
125#else
180#else
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127getFastWeakTicketWeak = return . _fastWeakTicket_weak
181getFastWeakTicketWeak = return . _fastWeakTicket_weak
128#endif
182#endif
129
183
src/Reflex/FunctorMaybe.hs+31−17
@@ -1,33 +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
7−import Data.Maybe
17+import Data.IntMap (IntMap)
18+import Data.Map (Map)
19+#if MIN_VERSION_base(4,9,0)
20+import Data.Semigroup (Option(..))
21+#endif
22+import Data.Witherable
8
23
9--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
10
25
11-- | A class for values that combines filtering and mapping using 'Maybe'.
26-- | A class for values that combines filtering and mapping using 'Maybe'.
12−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
27+-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@.
13−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
28+{-# DEPRECATED FunctorMaybe "Use 'Filterable' from Data.Witherable instead" #-}
14−-- and e.g. 'Event' is instance of neither.
15−--
16−-- A definition of 'fmapMaybe' must satisfy the following laws:
17−--
18−-- [/identity/]
19−-- @'fmapMaybe' 'Just' ≡ 'id'@
20−--
21−-- [/composition/]
22−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
23class FunctorMaybe f where
29class FunctorMaybe f where
24 -- | Combined mapping and filtering function.
30 -- | Combined mapping and filtering function.
25 fmapMaybe :: (a -> Maybe b) -> f a -> f b
31 fmapMaybe :: (a -> Maybe b) -> f a -> f b
26
32
27−-- | @fmapMaybe = (=<<)
28instance FunctorMaybe Maybe where
33instance FunctorMaybe Maybe where
29− fmapMaybe = (=<<)
34+ fmapMaybe = mapMaybe
35+
36+#if MIN_VERSION_base(4,9,0)
37+deriving instance FunctorMaybe Option
38+#endif
30
39
31−-- | @fmapMaybe = mapMaybe@
32instance FunctorMaybe [] where
40instance FunctorMaybe [] where
33 fmapMaybe = mapMaybe
41 fmapMaybe = mapMaybe
42+
43+instance FunctorMaybe (Map k) where
44+ fmapMaybe = mapMaybe
45+
46+instance FunctorMaybe IntMap where
47+ fmapMaybe = mapMaybe
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
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+13−4
@@ -1,10 +1,14 @@
1−-- | This module provides a GHC plugin designed to improve code that uses
2−-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
3−-- definition that doesn't have an explicit inlining pragma. In the future,
4−-- additional optimizations are likely to be added.
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
@@ -16,6 +20,11 @@ import Control.Arrow
16import CoreMonad
20import CoreMonad
17import Data.String
21import Data.String
18import GhcPlugins
22import GhcPlugins
23+
24+#if MIN_VERSION_base(4,9,0)
25+import Prelude hiding ((<>))
26+#endif
27+
19#endif
28#endif
20
29
21#ifdef ghcjs_HOST_OS
30#ifdef ghcjs_HOST_OS
src/Reflex/Patch.hs+12−5
@@ -1,6 +1,10 @@
1−-- | This module defines the 'Patch' class, which is used by Reflex to manage
2−-- changes to 'Reflex.Class.Incremental' values.
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/DMap.hs+22−8
@@ -5,6 +5,7 @@
5{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE StandaloneDeriving #-}
6{-# LANGUAGE StandaloneDeriving #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
8module Reflex.Patch.DMap where
9module Reflex.Patch.DMap where
9
10
10import Reflex.Patch.Class
11import Reflex.Patch.Class
@@ -20,14 +21,16 @@ import qualified Data.Map as Map
20import Data.Semigroup (Semigroup (..))
21import Data.Semigroup (Semigroup (..))
21import Data.Some (Some)
22import Data.Some (Some)
22
23
23−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
24+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
24−-- deleted.
25+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
26+-- while deletions are represented as @'ComposeMaybe' Nothing@.
25newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
27newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
26
28
27deriving instance GCompare k => Semigroup (PatchDMap k v)
29deriving instance GCompare k => Semigroup (PatchDMap k v)
28
30
29deriving instance GCompare k => Monoid (PatchDMap k v)
31deriving instance GCompare k => Monoid (PatchDMap k v)
30
32
33+-- | Apply the insertions or deletions to a given 'DMap'.
31instance GCompare k => Patch (PatchDMap k v) where
34instance GCompare k => Patch (PatchDMap k v) where
32 type PatchTarget (PatchDMap k v) = DMap k v
35 type PatchTarget (PatchDMap k v) = DMap k v
33 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
36 apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
@@ -37,32 +40,43 @@ instance GCompare k => Patch (PatchDMap k v) where
37 Nothing -> Just $ Constant ()
40 Nothing -> Just $ Constant ()
38 Just _ -> Nothing
41 Just _ -> Nothing
39
42
43+-- | Map a function @v a -> v' a@ over any inserts/updates in the given
44+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
40mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
45mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
41mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
46mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
42
47
48+-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
49+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
43−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
50+traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
44traversePatchDMap f = traversePatchDMapWithKey $ const f
51traversePatchDMap f = traversePatchDMapWithKey $ const f
45
52
53+-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
54+-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
46traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
55traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
47traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
56traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
48
57
58+-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
59+-- @v a -> v'@ to weaken each value contained in the patch.
49weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
60weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
50weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
61weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
51
62
63+-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
64+-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
52−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
65+patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
53patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
66patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
54
67
68+-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
55−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
69+const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
56const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
70const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
57− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
71+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
58 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
72 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
59
73
74+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
60const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
75const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
61const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
76const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
62 where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
77 where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
63 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
78 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
64
79
65−-- | Get the values that will be deleted if the given patch is applied to the
80+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
66−-- given 'DMap'. Includes values that will be replaced.
67getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
81getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
68getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
82getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hs+9−9
@@ -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
@@ -104,8 +103,9 @@ validationErrorsForPatchDMapWithMove m =
104 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
103 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
105 unbalancedMove _ = Nothing
104 unbalancedMove _ = Nothing
106
105
106+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
107−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
108− PatchDMapWithMove a == PatchDMapWithMove b = a == b
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
109
109
110-- |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
111data Pair1 f g a = Pair1 (f a) (g a)
111data Pair1 f g a = Pair1 (f a) (g a)
@@ -310,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
311 From_Insert v -> MapWithMove.From_Insert $ f v
311 From_Insert v -> MapWithMove.From_Insert $ f v
312 From_Delete -> MapWithMove.From_Delete
312 From_Delete -> MapWithMove.From_Delete
313− From_Move k -> MapWithMove.From_Move $ Some.This k
313+ From_Move k -> MapWithMove.From_Move $ Some k
314− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
315 }
315 }
316
316
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
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
@@ -341,6 +341,7 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
341 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
341 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
342 }
342 }
343
343
344+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
344instance GCompare k => Patch (PatchDMapWithMove k v) where
345instance GCompare k => Patch (PatchDMapWithMove k v) where
345 type PatchTarget (PatchDMapWithMove k v) = DMap k v
346 type PatchTarget (PatchDMapWithMove k v) = DMap k v
346 apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
347 apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
@@ -356,8 +357,7 @@ instance GCompare k => Patch (PatchDMapWithMove k v) where
356 From_Delete -> Just $ Constant ()
357 From_Delete -> Just $ Constant ()
357 _ -> Nothing
358 _ -> Nothing
358
359
359−-- | Get the values that will be deleted or moved if the given patch is applied
360+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
360−-- to the given 'DMap'.
361getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
361getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
362getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
362getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
363 where f _ v ni = Pair v $ _nodeInfo_to ni
363 where f _ v ni = Pair v $ _nodeInfo_to ni
src/Reflex/Patch/IntMap.hs+20−10
@@ -1,9 +1,10 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveFoldable #-}
1{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE DeriveTraversable #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6{-# LANGUAGE TypeFamilies #-}
5{-# LANGUAGE TypeFamilies #-}
6+-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
7+-- insert/update or delete of associations.
7module Reflex.Patch.IntMap where
8module Reflex.Patch.IntMap where
8
9
9import Data.IntMap.Strict (IntMap)
10import Data.IntMap.Strict (IntMap)
@@ -12,8 +13,12 @@ import Data.Maybe
12import Data.Semigroup
13import Data.Semigroup
13import Reflex.Patch.Class
14import Reflex.Patch.Class
14
15
16+-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
17+-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
18+-- and @Nothing@ means delete.
15newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
19newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
16
20
21+-- | Apply the insertions or deletions to a given 'IntMap'.
17instance Patch (PatchIntMap a) where
22instance Patch (PatchIntMap a) where
18 type PatchTarget (PatchIntMap a) = IntMap a
23 type PatchTarget (PatchIntMap a) = IntMap a
19 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
24 apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
@@ -27,23 +32,28 @@ instance Patch (PatchIntMap a) where
27instance Semigroup (PatchIntMap v) where
32instance Semigroup (PatchIntMap v) where
28 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
29 -- PatchMap is idempotent, so stimes n is id for every n
34 -- PatchMap is idempotent, so stimes n is id for every n
30−#if MIN_VERSION_semigroups(0,17,0)
31 stimes = stimesIdempotentMonoid
35 stimes = stimesIdempotentMonoid
32−#else
36+
33− times1p n x = case compare n 0 of
37+-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
34− LT -> error "stimesIdempotentMonoid: negative multiplier"
38+-- (that is, all inserts/updates), producing a @PatchIntMap b@.
35− EQ -> mempty
39+mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
36− GT -> x
40+mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
37−#endif
41+
38−
42+-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
43+-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
39−traverseIntMapPatchWithKey :: Applicative t => (Int -> a -> t b) -> PatchIntMap a -> t (PatchIntMap b)
44+traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
40traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
45traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
41
46
47+-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
42patchIntMapNewElements :: PatchIntMap a -> [a]
48patchIntMapNewElements :: PatchIntMap a -> [a]
43patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
49patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
44
50
51+-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
52+-- the inserts/updates in the given patch.
45patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
53patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
46patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
54patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
47
55
56+-- | Subset the given @'IntMap' a@ to contain only the keys that would be
57+-- deleted by the given @'PatchIntMap' a@.
48getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
58getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
49getDeletions (PatchIntMap m) v = IntMap.intersection v m
59getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Patch/Map.hs+1−10
@@ -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)
@@ -18,8 +17,7 @@ import Data.Semigroup
18newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
17newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
19 deriving (Show, Read, Eq, Ord)
18 deriving (Show, Read, Eq, Ord)
20
19
21−-- | Applying a 'PatchMap' will update the 'Map' by performing the insertions
20+-- | Apply the insertions or deletions to a given 'Map'.
22−-- and deletions specified
23instance Ord k => Patch (PatchMap k v) where
21instance Ord k => Patch (PatchMap k v) where
24 type PatchTarget (PatchMap k v) = Map k v
22 type PatchTarget (PatchMap k v) = Map k v
25 {-# INLINABLE apply #-}
23 {-# INLINABLE apply #-}
@@ -36,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
36instance Ord k => Semigroup (PatchMap k v) where
34instance Ord k => Semigroup (PatchMap k v) where
37 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
38 -- PatchMap is idempotent, so stimes n is id for every n
36 -- PatchMap is idempotent, so stimes n is id for every n
39−#if MIN_VERSION_semigroups(0,17,0)
40 stimes = stimesIdempotentMonoid
37 stimes = stimesIdempotentMonoid
41−#else
42− times1p n x = case compare n 0 of
43− LT -> error "stimesIdempotentMonoid: negative multiplier"
44− EQ -> mempty
45− GT -> x
46−#endif
47
38
48-- | The empty 'PatchMap' contains no insertions or deletions
39-- | The empty 'PatchMap' contains no insertions or deletions
49instance Ord k => Monoid (PatchMap k v) where
40instance Ord k => Monoid (PatchMap k v) where
src/Reflex/Patch/MapWithMove.hs+59−17
@@ -20,9 +20,9 @@ import Data.List
20import Data.Map (Map)
20import Data.Map (Map)
21import qualified Data.Map as Map
21import qualified Data.Map as Map
22import Data.Maybe
22import Data.Maybe
23−import Data.Semigroup
23+import 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@
@@ -41,6 +41,18 @@ data NodeInfo k v = NodeInfo
41 }
41 }
42 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
42 deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
43
43
44+-- | Describe how a key's new value should be produced
45+data From k v
46+ = From_Insert v -- ^ Insert the given value here
47+ | From_Delete -- ^ Delete the existing value, if any, from here
48+ | From_Move !k -- ^ Move the value here from the given key
49+ deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
50+
51+-- | Describe where a key's old value will go. If this is 'Just', that means
52+-- the key's old value will be moved to the given other key; if it is 'Nothing',
53+-- that means it will be deleted.
54+type To = Maybe
55+
44-- | Create a 'PatchMapWithMove', validating it
56-- | Create a 'PatchMapWithMove', validating it
45patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
57patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
46patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
58patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
@@ -62,22 +74,51 @@ patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
62unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
74unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
63unPatchMapWithMove (PatchMapWithMove p) = p
75unPatchMapWithMove (PatchMapWithMove p) = p
64
76
65−-- | Warning: when using this function, you must ensure that the invariants of
77+-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
66−-- 'PatchMapWithMove' are preserved; they will not be checked.
78+insertMapKey :: k -> v -> PatchMapWithMove k v
67−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
79+insertMapKey k v = PatchMapWithMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
68−unsafePatchMapWithMove = PatchMapWithMove
69
80
70−-- | Describe how a key's new value should be produced
81+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
71−data From k v
82+--
72− = From_Insert v -- ^ Insert the given value here
83+-- @
73− | From_Delete -- ^ Delete the existing value, if any, from here
84+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
74− | From_Move !k -- ^ Move the value here from the given key
85+-- @
75− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
86+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
87+moveMapKey src dst
88+ | src == dst = mempty
89+ | otherwise =
90+ PatchMapWithMove $ Map.fromList
91+ [ (dst, NodeInfo (From_Move src) Nothing)
92+ , (src, NodeInfo From_Delete (Just dst))
93+ ]
76
94
77−-- | Describe where a key's old value will go. If this is 'Just', that means
95+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
78−-- the key's old value will be moved to the given other key; if it is 'Nothing',
96+--
79−-- that means it will be deleted.
97+-- @
80−type To = Maybe
98+-- let aMay = Map.lookup a map
99+-- bMay = Map.lookup b map
100+-- in maybe id (Map.insert a) (bMay `mplus` aMay)
101+-- . maybe id (Map.insert b) (aMay `mplus` bMay)
102+-- . Map.delete a . Map.delete b $ map
103+-- @
104+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
105+swapMapKey src dst
106+ | src == dst = mempty
107+ | otherwise =
108+ PatchMapWithMove $ Map.fromList
109+ [ (dst, NodeInfo (From_Move src) (Just src))
110+ , (src, NodeInfo (From_Move dst) (Just dst))
111+ ]
112+
113+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
114+deleteMapKey :: k -> PatchMapWithMove k v
115+deleteMapKey k = PatchMapWithMove . Map.singleton k $ NodeInfo From_Delete Nothing
116+
117+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
118+--
119+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
120+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
121+unsafePatchMapWithMove = PatchMapWithMove
81
122
82-- | Apply the insertions, deletions, and moves to a given 'Map'
123-- | Apply the insertions, deletions, and moves to a given 'Map'
83instance Ord k => Patch (PatchMapWithMove k v) where
124instance Ord k => Patch (PatchMapWithMove k v) where
@@ -91,10 +132,11 @@ instance Ord k => Patch (PatchMapWithMove k v) where
91 From_Delete -> Just ()
132 From_Delete -> Just ()
92 _ -> Nothing
133 _ -> Nothing
93
134
94−-- | Returns all the new elements that will be added to the 'Map'
135+-- | Returns all the new elements that will be added to the 'Map'.
95patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
136patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
96patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
137patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
97
138
139+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
98patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
140patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
99patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
141patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
100 where f ni = case _nodeInfo_from ni of
142 where f ni = case _nodeInfo_from ni of
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+32−26
@@ -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,14 +41,15 @@ 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
51−-- exclusively to memoize functions of t, not for any of its other capabilities.
52+-- exclusively to memoize functions of @/t/@, not for any of its other capabilities.
52instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
53instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
53
54
54 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
55 newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
@@ -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+33−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 #-}
@@ -22,6 +21,7 @@ module Reflex.Query.Base
22import Control.Applicative (liftA2)
21import Control.Applicative (liftA2)
23import Control.Monad.Exception
22import Control.Monad.Exception
24import Control.Monad.Fix
23import Control.Monad.Fix
24+import Control.Monad.Primitive
25import Control.Monad.Reader
25import Control.Monad.Reader
26import Control.Monad.Ref
26import Control.Monad.Ref
27import Control.Monad.State.Strict
27import Control.Monad.State.Strict
@@ -37,8 +37,7 @@ import Data.Map (Map)
37import qualified Data.Map as Map
37import qualified Data.Map as Map
38import Data.Monoid ((<>))
38import Data.Monoid ((<>))
39import qualified Data.Semigroup as S
39import qualified Data.Semigroup as S
40−import Data.Some (Some)
40+import Data.Some (Some(Some))
41−import qualified Data.Some as Some
42import Data.These
41import Data.These
43
42
44import Reflex.Class
43import Reflex.Class
@@ -73,26 +72,19 @@ getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
73getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
72getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
74getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
73getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
75
74
76−{-
75+maskMempty :: (Eq a, Monoid a) => a -> Maybe a
77−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
76+maskMempty x = if x == mempty then Nothing else Just x
78− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
79− bs' = fmapCheap snd $ r'
80− patches = unsafeBuildIncremental (sampleBs bs0) $
81− flip pushCheap bs' $ \bs -> do
82− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
83− return (Just (AdditivePatch p))
84−-}
85
77
86−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
78+instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
87 runWithReplace (QueryT a0) a' = do
79 runWithReplace (QueryT a0) a' = do
88 ((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'
89 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
90 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
82 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
91 bs' = fmapCheap snd $ r'
83 bs' = fmapCheap snd $ r'
92 bbs <- hold bs0 bs'
84 bbs <- hold bs0 bs'
93− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
85+ let patches = flip pushCheap bs' $ \newBs -> do
94 oldBs <- sample bbs
86 oldBs <- sample bbs
95− (~~) <$> sampleBs newBs <*> sampleBs oldBs
87+ maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
96 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
88 QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
97 QueryT $ lift $ tellEvent patches
89 QueryT $ lift $ tellEvent patches
98 return (r0, fmapCheap fst r')
90 return (r0, fmapCheap fst r')
@@ -151,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
151 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
152 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
153 liftedBs0 :: Map (Some k) [Behavior t q]
145 liftedBs0 :: Map (Some k) [Behavior t q]
154− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
146+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
155 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
156 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
148 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
157− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
149+ Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
158 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
159 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
160 accumBehaviors :: forall m'. MonadHold t m'
152 accumBehaviors :: forall m'. MonadHold t m'
@@ -170,19 +162,19 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
170 let p k bs = case Map.lookup k bs0 of
162 let p k bs = case Map.lookup k bs0 of
171 Nothing -> case bs of
163 Nothing -> case bs of
172 -- 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.
173− Nothing -> return mempty
165+ Nothing -> return Nothing
174 -- 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.
175− Just newBs -> sampleBs newBs
167+ Just newBs -> maskMempty <$> sampleBs newBs
176 Just oldBs -> case bs of
168 Just oldBs -> case bs of
177 -- 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
178− Nothing -> negateG <$> sampleBs oldBs
170+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
179 -- 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
180 -- composed with the sampling the child's new state.
172 -- composed with the sampling the child's new state.
181− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
173+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
182 -- 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
183 -- child patches and wrap them in AdditivePatch.
175 -- child patches and wrap them in AdditivePatch.
184− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
176+ patch <- fold <$> Map.traverseWithKey p bs'
185− return (apply pbs bs0, Just patch)
177+ return (apply pbs bs0, AdditivePatch <$> patch)
186 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
178 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
187 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
179 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
188 return (liftedResult0, liftedResult')
180 return (liftedResult0, liftedResult')
@@ -195,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
195 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
196 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
197 liftedBs0 :: Map (Some k) [Behavior t q]
189 liftedBs0 :: Map (Some k) [Behavior t q]
198− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
190+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
199 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
200 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
192 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
201− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
193+ Map.fromDistinctAscList $ (\(k :=> mr) -> (Some k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
202 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
203 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
204 accumBehaviors' :: forall m'. MonadHold t m'
196 accumBehaviors' :: forall m'. MonadHold t m'
@@ -215,28 +207,28 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
215 p k bs = case Map.lookup k bs0 of
207 p k bs = case Map.lookup k bs0 of
216 Nothing -> case MapWithMove._nodeInfo_from bs of
208 Nothing -> case MapWithMove._nodeInfo_from bs of
217 -- 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.
218− MapWithMove.From_Delete -> return mempty
210+ MapWithMove.From_Delete -> return Nothing
219 -- 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.
220− MapWithMove.From_Insert newBs -> sampleBs newBs
212+ MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs
221 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
213 MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
222− Nothing -> return mempty
214+ Nothing -> return Nothing
223− Just newBs -> sampleBs newBs
215+ Just newBs -> maskMempty <$> sampleBs newBs
224 Just oldBs -> case MapWithMove._nodeInfo_from bs of
216 Just oldBs -> case MapWithMove._nodeInfo_from bs of
225 -- 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
226− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
218+ MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs
227 -- 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
228 -- composed with the sampling the child's new state.
220 -- composed with the sampling the child's new state.
229− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
221+ MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
230 MapWithMove.From_Move k'
222 MapWithMove.From_Move k'
231− | k' == k -> return mempty
223+ | k' == k -> return Nothing
232 | otherwise -> case Map.lookup k' bs0 of
224 | otherwise -> case Map.lookup k' bs0 of
233 -- 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
234− Nothing -> negateG <$> sampleBs oldBs
226+ Nothing -> maskMempty . negateG <$> sampleBs oldBs
235− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
227+ Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
236 -- 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
237 -- child patches and wrap them in AdditivePatch.
229 -- child patches and wrap them in AdditivePatch.
238− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
230+ patch <- fold <$> Map.traverseWithKey p bs'
239− return (apply pbs bs0, Just patch)
231+ return (apply pbs bs0, AdditivePatch <$> patch)
240 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
232 (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
241 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
233 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
242 return (liftedResult0, liftedResult')
234 return (liftedResult0, liftedResult')
@@ -244,6 +236,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adj
244instance MonadTrans (QueryT t q) where
236instance MonadTrans (QueryT t q) where
245 lift = QueryT . lift . lift . lift
237 lift = QueryT . lift . lift . lift
246
238
239+instance PrimMonad m => PrimMonad (QueryT t q m) where
240+ type PrimState (QueryT t q m) = PrimState m
241+ primitive = lift . primitive
242+
247instance PostBuild t m => PostBuild t (QueryT t q m) where
243instance PostBuild t m => PostBuild t (QueryT t q m) where
248 getPostBuild = lift getPostBuild
244 getPostBuild = lift getPostBuild
249
245
@@ -335,5 +331,5 @@ instance Requester t m => Requester t (QueryT t q m) where
335instance 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
336 tellEvent = lift . tellEvent
332 tellEvent = lift . tellEvent
337
333
338−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (QueryT t q m) where
334+instance DynamicWriter t w m => DynamicWriter t w (QueryT t q m) where
339 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+113−5
@@ -21,14 +21,20 @@
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
27 , RequesterData
28 , RequesterData
28 , RequesterDataKey
29 , RequesterDataKey
29 , traverseRequesterData
30 , traverseRequesterData
31+ , forRequesterData
30 , requesterDataToList
32 , requesterDataToList
31 , singletonRequesterData
33 , singletonRequesterData
34+ , matchResponsesWithRequests
35+ , multiEntry
36+ , unMultiEntry
37+ , requesting'
32 ) where
38 ) where
33
39
34import Reflex.Class
40import Reflex.Class
@@ -43,6 +49,7 @@ import Reflex.TriggerEvent.Class
43import Control.Applicative (liftA2)
49import Control.Applicative (liftA2)
44import Control.Monad.Exception
50import Control.Monad.Exception
45import Control.Monad.Identity
51import Control.Monad.Identity
52+import Control.Monad.Primitive
46import Control.Monad.Reader
53import Control.Monad.Reader
47import Control.Monad.Ref
54import Control.Monad.Ref
48import Control.Monad.State.Strict
55import Control.Monad.State.Strict
@@ -59,8 +66,7 @@ import qualified Data.Map as Map
59import Data.Monoid ((<>))
66import Data.Monoid ((<>))
60import Data.Proxy
67import Data.Proxy
61import qualified Data.Semigroup as S
68import qualified Data.Semigroup as S
62−import Data.Some (Some)
69+import Data.Some (Some(Some))
63−import qualified Data.Some as Some
64import Data.Type.Equality
70import Data.Type.Equality
65import Data.Unique.Tag
71import Data.Unique.Tag
66
72
@@ -130,6 +136,10 @@ traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWith
130 MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
136 MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
131 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
137 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
132
138
139+-- | 'traverseRequesterData' with its arguments flipped
140+forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response)
141+forRequesterData r f = traverseRequesterData f r
142+
133data MyTagType :: * -> * where
143data MyTagType :: * -> * where
134 MyTagType_Single :: MyTagType (Single a)
144 MyTagType_Single :: MyTagType (Single a)
135 MyTagType_Multi :: MyTagType Multi
145 MyTagType_Multi :: MyTagType Multi
@@ -239,6 +249,10 @@ deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
239deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
249deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
240deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
250deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
241
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+
242-- 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.
243instance (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
244 mempty = pure mempty
258 mempty = pure mempty
@@ -252,7 +266,6 @@ instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response
252-- 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'.
253-- 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
254-- requests were issued.
268-- requests were issued.
255−
256runRequesterT :: (Reflex t, Monad m)
269runRequesterT :: (Reflex t, Monad m)
257 => RequesterT t request response m a
270 => RequesterT t request response m a
258 -> 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
@@ -262,6 +275,20 @@ runRequesterT (RequesterT a) responses = do
262 coerceEvent responses
275 coerceEvent responses
263 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
276 return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
264
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+
265instance (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
266 type Request (RequesterT t request response m) = request
293 type Request (RequesterT t request response m) = request
267 type Response (RequesterT t request response m) = response
294 type Response (RequesterT t request response m) = response
@@ -365,7 +392,7 @@ traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIn
365 pack = Entry
392 pack = Entry
366 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')
367 f' k (n, v) = do
394 f' k (n, v) = do
368− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
395+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing mapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
369 return (fmapCheap (IntMap.singleton n) myRequests, result)
396 return (fmapCheap (IntMap.singleton n) myRequests, result)
370 ndm' <- numberOccurrencesFrom 1 dm'
397 ndm' <- numberOccurrencesFrom 1 dm'
371 (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
@@ -413,7 +440,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
413 pack = Entry
440 pack = Entry
414 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)
415 f' k (Compose (n, v)) = do
442 f' k (Compose (n, v)) = do
416− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
443+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
417 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
418 ndm' <- numberOccurrencesFrom 1 dm'
445 ndm' <- numberOccurrencesFrom 1 dm'
419 (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'
@@ -427,3 +454,84 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
427 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'
428 requests <- holdIncremental requests0 requests'
455 requests <- holdIncremental requests0 requests'
429 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+291−218
@@ -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,30 @@ 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+#ifdef MIN_VERSION_semialign
76+#if MIN_VERSION_these(0,8,0)
77+import Data.These.Combinators (justThese)
78+#endif
79+#if MIN_VERSION_semialign(1,1,0)
80+import Data.Zip (Zip (..))
81+#endif
82+#endif
83+
66#ifdef DEBUG_CYCLES
84#ifdef DEBUG_CYCLES
67import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
85import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
68import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
86import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -74,9 +92,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
74import Data.FastWeakBag (FastWeakBag)
92import Data.FastWeakBag (FastWeakBag)
75import qualified Data.FastWeakBag as FastWeakBag
93import qualified Data.FastWeakBag as FastWeakBag
76import Data.Reflection
94import Data.Reflection
77−import Data.Some (Some)
95+import Data.Some (Some(Some))
78−import qualified Data.Some as Some
79import Data.Type.Coercion
96import Data.Type.Coercion
97+import Data.Profunctor.Unsafe ((#.), (.#))
80import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
98import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
81import qualified Data.WeakBag as WeakBag
99import qualified Data.WeakBag as WeakBag
82import qualified Reflex.Class
100import qualified Reflex.Class
@@ -193,12 +211,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193
211
194newNodeId :: IO Int
212newNodeId :: IO Int
195newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
213newNodeId = 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
214#endif
203
215
204--------------------------------------------------------------------------------
216--------------------------------------------------------------------------------
@@ -302,9 +314,10 @@ cacheEvent e =
302#else
314#else
303 Event $
315 Event $
304#endif
316#endif
317+ unsafePerformIO $ do
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
318+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
319+ <- newIORef emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
320+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308#ifdef DEBUG_TRACE_EVENTS
321#ifdef DEBUG_TRACE_EVENTS
309 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
322 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310#endif
323#endif
@@ -319,9 +332,9 @@ cacheEvent e =
319 scheduleClear occRef
332 scheduleClear occRef
320 propagateFast a subscribers
333 propagateFast a subscribers
321 , subscriberInvalidateHeight = \old -> do
334 , subscriberInvalidateHeight = \old -> do
322− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
335+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
323 , subscriberRecalculateHeight = \new -> do
336 , subscriberRecalculateHeight = \new -> do
324− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
337+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
325 }
338 }
326 when (isJust occ) $ do
339 when (isJust occ) $ do
327 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
340 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
@@ -370,7 +383,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370eventNever :: Event x a
383eventNever :: Event x a
371eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
384eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372
385
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
386+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
374eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
387eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375
388
376eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
389eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +435,14 @@ newSubscriberHold h = return $ Subscriber
422 , subscriberRecalculateHeight = \_ -> return ()
435 , subscriberRecalculateHeight = \_ -> return ()
423 }
436 }
424
437
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
438+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
426newSubscriberFan subscribed = return $ Subscriber
439newSubscriberFan subscribed = return $ Subscriber
427 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
440 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
441 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"
442 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
443 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431 scheduleClear $ fanSubscribedOccurrence subscribed
444 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
445+ let f _ (Pair v subsubs) = do
433 propagate v $ _fanSubscribedChildren_list subsubs
446 propagate v $ _fanSubscribedChildren_list subsubs
434 return $ Constant ()
447 return $ Constant ()
435 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
448 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -437,12 +450,12 @@ newSubscriberFan subscribed = return $ Subscriber
437 , subscriberInvalidateHeight = \old -> do
450 , subscriberInvalidateHeight = \old -> do
438 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
451 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
439 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
452 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
440− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
453+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
441 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
454 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
442 , subscriberRecalculateHeight = \new -> do
455 , subscriberRecalculateHeight = \new -> do
443 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
456 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
444 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
457 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
445− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
458+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
446 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
459 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
447 }
460 }
448
461
@@ -458,7 +471,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
458 oldHeight <- readIORef $ switchSubscribedHeight subscribed
471 oldHeight <- readIORef $ switchSubscribedHeight subscribed
459 when (oldHeight /= invalidHeight) $ do
472 when (oldHeight /= invalidHeight) $ do
460 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
473 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
461− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
474+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
462 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
475 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
463 , subscriberRecalculateHeight = \new -> do
476 , subscriberRecalculateHeight = \new -> do
464 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
477 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -481,8 +494,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
481 Nothing -> do
494 Nothing -> do
482 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
495 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
483 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
496 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
497+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
485− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
486 Just o -> do -- Since it's already firing, no need to adjust height
499 Just o -> do -- Since it's already firing, no need to adjust height
487 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
500 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
488 scheduleClear $ coincidenceSubscribedOccurrence subscribed
501 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -529,14 +542,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
529propagate a subscribers = withIncreasedDepth $ do
542propagate a subscribers = withIncreasedDepth $ do
530 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
543 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
531 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
544 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
532− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
545+ WeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
533
546
534-- | Propagate everything at the current height
547-- | Propagate everything at the current height
535propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
548propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
536propagateFast a subscribers = withIncreasedDepth $ do
549propagateFast a subscribers = withIncreasedDepth $ do
537 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
550 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
538 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
551 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
539− FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
552+ FastWeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
540
553
541--------------------------------------------------------------------------------
554--------------------------------------------------------------------------------
542-- EventSubscribed
555-- EventSubscribed
@@ -545,6 +558,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
545toAny :: a -> Any
558toAny :: a -> Any
546toAny = unsafeCoerce
559toAny = unsafeCoerce
547
560
561+-- Why do we use Any here, instead of just giving eventSubscribedRetained an
562+-- existential type? Sadly, GHC does not currently know how to unbox types
563+-- with existentially quantified fields. So instead we just coerce values
564+-- to type Any on the way in. Since we never coerce them back, this is
565+-- perfectly safe.
548data EventSubscribed x = EventSubscribed
566data EventSubscribed x = EventSubscribed
549 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
567 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
568 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -577,12 +595,12 @@ eventSubscribedNever = EventSubscribed
577#endif
595#endif
578 }
596 }
579
597
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
598+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581eventSubscribedFan !subscribed = EventSubscribed
599eventSubscribedFan !subscribed = EventSubscribed
582 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
600 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583 , eventSubscribedRetained = toAny subscribed
601 , eventSubscribedRetained = toAny subscribed
584#ifdef DEBUG_CYCLES
602#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
603+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586 , eventSubscribedHasOwnHeightRef = False
604 , eventSubscribedHasOwnHeightRef = False
587 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
605 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588#endif
606#endif
@@ -595,7 +613,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595#ifdef DEBUG_CYCLES
613#ifdef DEBUG_CYCLES
596 , eventSubscribedGetParents = do
614 , eventSubscribedGetParents = do
597 s <- readIORef $ switchSubscribedCurrentParent subscribed
615 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
616+ return [Some $ _eventSubscription_subscribed s]
599 , eventSubscribedHasOwnHeightRef = True
617 , eventSubscribedHasOwnHeightRef = True
600 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
618 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601#endif
619#endif
@@ -608,8 +626,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608#ifdef DEBUG_CYCLES
626#ifdef DEBUG_CYCLES
609 , eventSubscribedGetParents = do
627 , eventSubscribedGetParents = do
610 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
628 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
629+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
630+ innerParents = maybeToList $ fmap Some innerSubscription
613 return $ outerParent : innerParents
631 return $ outerParent : innerParents
614 , eventSubscribedHasOwnHeightRef = True
632 , eventSubscribedHasOwnHeightRef = True
615 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
633 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +643,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625
643
626walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
644walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627walkInvalidHeightParents s0 = do
645walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
646+ subscribers <- flip execStateT mempty $ ($ Some s0) $ fix $ \loop (Some s) -> do
629 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
647 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630 when (h == invalidHeight) $ do
648 when (h == invalidHeight) $ do
631 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
649 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
650+ modify (Some s :)
633 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
651 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
652+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635 return subscribers
653 return subscribers
636#endif
654#endif
637
655
@@ -659,7 +677,7 @@ behaviorPull !p = Behavior $ do
659 val <- liftIO $ readIORef $ pullValue p
677 val <- liftIO $ readIORef $ pullValue p
660 case val of
678 case val of
661 Just subscribed -> do
679 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
680+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
681 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
682 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665 return $ pullSubscribedValue subscribed
683 return $ pullSubscribedValue subscribed
@@ -668,7 +686,7 @@ behaviorPull !p = Behavior $ do
668 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
686 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669 parentsRef <- liftIO $ newIORef []
687 parentsRef <- liftIO $ newIORef []
670 holdInits <- askBehaviorHoldInits
688 holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
689+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
690 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673 parents <- liftIO $ readIORef parentsRef
691 parents <- liftIO $ readIORef parentsRef
674 let subscribed = PullSubscribed
692 let subscribed = PullSubscribed
@@ -678,7 +696,7 @@ behaviorPull !p = Behavior $ do
678 , pullSubscribedParents = parents
696 , pullSubscribedParents = parents
679 }
697 }
680 liftIO $ writeIORef (pullValue p) $ Just subscribed
698 liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
699+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
682 return a
700 return a
683
701
684behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
702behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +707,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689readHoldTracked h = do
707readHoldTracked h = do
690 result <- liftIO $ readIORef $ holdValue h
708 result <- liftIO $ readIORef $ holdValue h
691 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
709 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
710+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
693 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
711 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694 return result
712 return result
695
713
@@ -703,42 +721,46 @@ readBehaviorUntracked b = do
703-- Dynamic
721-- Dynamic
704--------------------------------------------------------------------------------
722--------------------------------------------------------------------------------
705
723
724+type DynamicS x p = Dynamic x (PatchTarget p) p
725+
706−data Dynamic x p = Dynamic
726+data Dynamic x target p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
727+ { dynamicCurrent :: !(Behavior x target)
708− , dynamicUpdated :: !(Event x p)
728+ , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709 }
729 }
710
730
731+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
732+
733+
734+
735+
711−dynamicHold :: Hold x p -> Dynamic x p
736+dynamicHold :: Hold x p -> DynamicS x p
712dynamicHold !h = Dynamic
737dynamicHold !h = Dynamic
713 { dynamicCurrent = behaviorHold h
738 { dynamicCurrent = behaviorHold h
714 , dynamicUpdated = eventHold h
739 , dynamicUpdated = eventHold h
715 }
740 }
716
741
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
742+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
718dynamicHoldIdentity = dynamicHold
743dynamicHoldIdentity = dynamicHold
719
744
720−dynamicConst :: PatchTarget p -> Dynamic x p
745+dynamicConst :: PatchTarget p -> DynamicS x p
721dynamicConst !a = Dynamic
746dynamicConst !a = Dynamic
722 { dynamicCurrent = behaviorConst a
747 { dynamicCurrent = behaviorConst a
723 , dynamicUpdated = eventNever
748 , dynamicUpdated = eventNever
724 }
749 }
725
750
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
751+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
727dynamicDyn !d = Dynamic
752dynamicDyn !d = Dynamic
728 { dynamicCurrent = behaviorDyn d
753 { dynamicCurrent = behaviorDyn d
729 , dynamicUpdated = eventDyn d
754 , dynamicUpdated = eventDyn d
730 }
755 }
731
756
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
757+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
733dynamicDynIdentity = dynamicDyn
758dynamicDynIdentity = dynamicDyn
734
759
735--------------------------------------------------------------------------------
760--------------------------------------------------------------------------------
736-- Combinators
761-- Combinators
737--------------------------------------------------------------------------------
762--------------------------------------------------------------------------------
738
763
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
764--type role Hold representational
743data Hold x p
765data Hold x p
744 = Hold { holdValue :: !(IORef (PatchTarget p))
766 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -759,20 +781,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759
781
760-- | Stores all global data relevant to a particular Spider timeline; only one
782-- | Stores all global data relevant to a particular Spider timeline; only one
761-- value should exist for each type @x@
783-- value should exist for each type @x@
784+newtype SpiderTimelineEnv x = STE {unSTE :: SpiderTimelineEnv' x}
785+-- We implement SpiderTimelineEnv with a newtype wrapper so
786+-- we can get the coercions we want safely.
787+type role SpiderTimelineEnv nominal
788+
762−data SpiderTimelineEnv x = SpiderTimelineEnv
789+data SpiderTimelineEnv' x = SpiderTimelineEnv
763 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
790 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
791 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765#ifdef DEBUG
792#ifdef DEBUG
766 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
793 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767#endif
794#endif
768 }
795 }
769−type role SpiderTimelineEnv nominal
796+type role SpiderTimelineEnv' phantom
770
797
771instance Eq (SpiderTimelineEnv x) where
798instance Eq (SpiderTimelineEnv x) where
772 _ == _ = True -- Since only one exists of each type
799 _ == _ = True -- Since only one exists of each type
773
800
774instance GEq SpiderTimelineEnv where
801instance GEq SpiderTimelineEnv where
775− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
802+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
776 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
803 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777 else Nothing
804 else Nothing
778
805
@@ -782,9 +809,9 @@ data EventEnv x
782 , eventEnvDynInits :: !(IORef [SomeDynInit x])
809 , eventEnvDynInits :: !(IORef [SomeDynInit x])
783 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
810 , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
811 , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
812+ , eventEnvClears :: !(IORef [Some Clear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
813+ , eventEnvIntClears :: !(IORef [Some IntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
814+ , eventEnvRootClears :: !(IORef [Some RootClear])
788 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
815 , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
816 , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
817 , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
@@ -795,7 +822,7 @@ runEventM :: EventM x a -> IO a
795runEventM = unEventM
822runEventM = unEventM
796
823
797asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
824asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
798−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
825+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
799
826
800class MonadIO m => Defer a m where
827class MonadIO m => Defer a m where
801 getDeferralQueue :: m (IORef [a])
828 getDeferralQueue :: m (IORef [a])
@@ -856,29 +883,29 @@ putCurrentHeight h = do
856 heightRef <- asksEventEnv eventEnvCurrentHeight
883 heightRef <- asksEventEnv eventEnvCurrentHeight
857 liftIO $ writeIORef heightRef $! h
884 liftIO $ writeIORef heightRef $! h
858
885
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
886+instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
860 {-# INLINE getDeferralQueue #-}
887 {-# INLINE getDeferralQueue #-}
861 getDeferralQueue = asksEventEnv eventEnvClears
888 getDeferralQueue = asksEventEnv eventEnvClears
862
889
863{-# INLINE scheduleClear #-}
890{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
891+scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
892+scheduleClear r = defer $ Some $ Clear r
866
893
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
894+instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868 {-# INLINE getDeferralQueue #-}
895 {-# INLINE getDeferralQueue #-}
869 getDeferralQueue = asksEventEnv eventEnvIntClears
896 getDeferralQueue = asksEventEnv eventEnvIntClears
870
897
871{-# INLINE scheduleIntClear #-}
898{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
899+scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
900+scheduleIntClear r = defer $ Some $ IntClear r
874
901
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
902+instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876 {-# INLINE getDeferralQueue #-}
903 {-# INLINE getDeferralQueue #-}
877 getDeferralQueue = asksEventEnv eventEnvRootClears
904 getDeferralQueue = asksEventEnv eventEnvRootClears
878
905
879{-# INLINE scheduleRootClear #-}
906{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
907+scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
908+scheduleRootClear r = defer $ Some $ RootClear r
882
909
883instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
910instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884 {-# INLINE getDeferralQueue #-}
911 {-# INLINE getDeferralQueue #-}
@@ -933,9 +960,9 @@ getHoldEventSubscription h = do
933
960
934type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
961type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935
962
936−--type role BehaviorM representational
937-- BehaviorM can sample behaviors
963-- BehaviorM can sample behaviors
938−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
964+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderIO (BehaviorEnv x) a }
965+ deriving (Functor, Applicative, MonadIO, MonadFix, MonadReader (BehaviorEnv x))
939
966
940instance Monad (BehaviorM x) where
967instance Monad (BehaviorM x) where
941 {-# INLINE (>>=) #-}
968 {-# INLINE (>>=) #-}
@@ -951,7 +978,7 @@ data BehaviorSubscribed x a
951 = forall p. BehaviorSubscribedHold (Hold x p)
978 = forall p. BehaviorSubscribedHold (Hold x p)
952 | BehaviorSubscribedPull (PullSubscribed x a)
979 | BehaviorSubscribedPull (PullSubscribed x a)
953
980
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
981+newtype SomeBehaviorSubscribed x = SomeBehaviorSubscribed (Some (BehaviorSubscribed x))
955
982
956--type role PullSubscribed representational
983--type role PullSubscribed representational
957data PullSubscribed x a
984data PullSubscribed x a
@@ -986,7 +1013,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
986#endif
1013#endif
987 }
1014 }
988
1015
989−data Root x (k :: * -> *)
1016+data Root x k
990 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
1017 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
991 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
1018 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
992 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
1019 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1083,25 @@ heightBagVerify b@(HeightBag s c) = if
1056heightBagVerify = id
1083heightBagVerify = id
1057#endif
1084#endif
1058
1085
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1086+data FanSubscribedChildren x k v a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1087+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1088+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1089+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
1063 }
1090 }
1064
1091
1065−data FanSubscribed (x :: *) k
1092+data FanSubscribed x k v
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1093+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1094+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1095+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
1069 , fanSubscribedParent :: !(EventSubscription x)
1096 , fanSubscribedParent :: !(EventSubscription x)
1070#ifdef DEBUG_NODEIDS
1097#ifdef DEBUG_NODEIDS
1071 , fanSubscribedNodeId :: Int
1098 , fanSubscribedNodeId :: Int
1072#endif
1099#endif
1073 }
1100 }
1074
1101
1075−data Fan x k
1102+data Fan x k v
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1103+ = Fan { fanParent :: !(Event x (DMap k v))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1104+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1078 }
1105 }
1079
1106
1080data SwitchSubscribed x a
1107data SwitchSubscribed x a
@@ -1128,24 +1155,35 @@ newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1128newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1155newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1129newInvalidatorPull p = return $! InvalidatorPull p
1156newInvalidatorPull p = return $! InvalidatorPull p
1130
1157
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1158+instance HasSpiderTimeline x => Filterable (Event x) where
1132− fmapMaybe f = push $ return . f
1159+ mapMaybe f = push $ return . f
1133
1160
1134instance HasSpiderTimeline x => Align (Event x) where
1161instance HasSpiderTimeline x => Align (Event x) where
1135 nil = eventNever
1162 nil = eventNever
1163+#if MIN_VERSION_these(0, 8, 0)
1164+instance HasSpiderTimeline x => Semialign (Event x) where
1165+#endif
1166+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1167+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1168+
1169+#ifdef MIN_VERSION_semialign
1170+#if MIN_VERSION_semialign(1,1,0)
1171+instance HasSpiderTimeline x => Zip (Event x) where
1172+#endif
1173+ zip x y = mapMaybe justThese $ align x y
1174+#endif
1137
1175
1138data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1176data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1139 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1177 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1140 | HoldDyn !(Hold x p)
1178 | HoldDyn !(Hold x p)
1141
1179
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1180+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
1143
1181
1144−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1182+newMapDyn :: HasSpiderTimeline x => (a -> b) -> DynamicS x (Identity a) -> DynamicS x (Identity b)
1145newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1183newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1146
1184
1147--TODO: Avoid the duplication between this and R.zipDynWith
1185--TODO: Avoid the duplication between this and R.zipDynWith
1148−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1186+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> DynamicS x (Identity a) -> DynamicS x (Identity b) -> DynamicS x (Identity c)
1149zipDynWith f da db =
1187zipDynWith f da db =
1150 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1188 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1151 ec = flip push eab $ \o -> do
1189 ec = flip push eab $ \o -> do
@@ -1168,18 +1206,12 @@ buildDynamic readV0 v' = do
1168 return d
1206 return d
1169
1207
1170unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1208unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1209+unsafeBuildDynamic readV0 v' =
1172− where x = (readV0, v')
1210+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
1173
1211
1174-- ResultM can read behaviors and events
1212-- ResultM can read behaviors and events
1175type ResultM = EventM
1213type ResultM = EventM
1176
1214
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
1215instance HasSpiderTimeline x => Functor (Event x) where
1184 fmap f = push $ return . Just . f
1216 fmap f = push $ return . Just . f
1185
1217
@@ -1192,33 +1224,42 @@ push f e = cacheEvent (pushCheap f e)
1192
1224
1193{-# INLINABLE pull #-}
1225{-# INLINABLE pull #-}
1194pull :: BehaviorM x a -> Behavior x a
1226pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1227+pull a = unsafePerformIO $ do
1196− { pullCompute = a
1228+ ref <- newIORef Nothing
1197− , pullValue = unsafeNewIORef a Nothing
1198#ifdef DEBUG_NODEIDS
1229#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1230+ nid <- newNodeId
1200#endif
1231#endif
1232+ pure $ behaviorPull $ Pull
1233+ { pullCompute = a
1234+ , pullValue = ref
1235+#ifdef DEBUG_NODEIDS
1236+ , pullNodeId = nid
1237+#endif
1201− }
1238+ }
1202
1239
1203{-# INLINABLE switch #-}
1240{-# INLINABLE switch #-}
1204switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1241switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1242+switch a = unsafePerformIO $ do
1243+ ref <- newIORef Nothing
1205−switch a = eventSwitch $ Switch
1244+ pure $ eventSwitch $ Switch
1206− { switchParent = a
1245+ { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1246+ , switchSubscribed = ref
1208− }
1247+ }
1209
1248
1210coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1249coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1250+coincidence a = unsafePerformIO $ do
1251+ ref <- newIORef Nothing
1211−coincidence a = eventCoincidence $ Coincidence
1252+ pure $ eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1253+ { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1254+ , coincidenceSubscribed = ref
1214− }
1255+ }
1215
1256
1216-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1257-- 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
1258run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1218run roots after = do
1259run roots after = do
1219 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1260 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1220 let t = spiderTimeline :: SpiderTimelineEnv x
1261 let t = spiderTimeline :: SpiderTimelineEnv x
1221− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1262+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
1222 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1263 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1223 occBefore <- liftIO $ do
1264 occBefore <- liftIO $ do
1224 occBefore <- readIORef occRef
1265 occBefore <- readIORef occRef
@@ -1256,11 +1297,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?)
1297 GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1257 EQ -> a
1298 EQ -> a
1258
1299
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1300+newtype Clear a = Clear (IORef (Maybe a))
1260
1301
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1302+newtype IntClear a = IntClear (IORef (IntMap a))
1262
1303
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1304+newtype RootClear k = RootClear (IORef (DMap k Identity))
1264
1305
1265data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1306data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1266
1307
@@ -1282,9 +1323,9 @@ type WeakList a = [Weak a]
1282withIncreasedDepth :: CanTrace x m => m a -> m a
1323withIncreasedDepth :: CanTrace x m => m a -> m a
1283withIncreasedDepth a = do
1324withIncreasedDepth a = do
1284 spiderTimeline <- askSpiderTimelineEnv
1325 spiderTimeline <- askSpiderTimelineEnv
1285− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1326+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
1286 result <- a
1327 result <- a
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1328+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
1288 return result
1329 return result
1289#else
1330#else
1290withIncreasedDepth :: m a -> m a
1331withIncreasedDepth :: m a -> m a
@@ -1311,7 +1352,7 @@ traceMWhen _ b getMessage = when b $ do
1311 message <- getMessage
1352 message <- getMessage
1312#ifdef DEBUG
1353#ifdef DEBUG
1313 spiderTimeline <- askSpiderTimelineEnv
1354 spiderTimeline <- askSpiderTimelineEnv
1314− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1355+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
1315#else
1356#else
1316 let d = 0
1357 let d = 0
1317#endif
1358#endif
@@ -1352,25 +1393,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
1393data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1353
1394
1354runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1395runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1355−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1396+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
1356
1397
1357askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1398askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1358askInvalidator = do
1399askInvalidator = do
1359− (!m, _) <- BehaviorM ask
1400+ (!m, _) <- ask
1360 case m of
1401 case m of
1361 Nothing -> return Nothing
1402 Nothing -> return Nothing
1362 Just (!wi, _) -> return $ Just wi
1403 Just (!wi, _) -> return $ Just wi
1363
1404
1364askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1405askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1365askParentsRef = do
1406askParentsRef = do
1366− (!m, _) <- BehaviorM ask
1407+ (!m, _) <- ask
1367 case m of
1408 case m of
1368 Nothing -> return Nothing
1409 Nothing -> return Nothing
1369 Just (_, !p) -> return $ Just p
1410 Just (_, !p) -> return $ Just p
1370
1411
1371askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1412askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1372askBehaviorHoldInits = do
1413askBehaviorHoldInits = do
1373− (_, !his) <- BehaviorM ask
1414+ (_, !his) <- ask
1374 return his
1415 return his
1375
1416
1376{-# INLINE getDynHold #-}
1417{-# INLINE getDynHold #-}
@@ -1415,6 +1456,9 @@ getRootSubscribed k r sub = do
1415 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1456 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1416 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1457 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1417 writeIORef uninitRef $! uninit
1458 writeIORef uninitRef $! uninit
1459+#ifdef DEBUG_NODEIDS
1460+ nid <- newNodeId
1461+#endif
1418 let !subscribed = RootSubscribed
1462 let !subscribed = RootSubscribed
1419 { rootSubscribedKey = k
1463 { rootSubscribedKey = k
1420 , rootSubscribedCachedSubscribed = cached
1464 , rootSubscribedCachedSubscribed = cached
@@ -1423,7 +1467,7 @@ getRootSubscribed k r sub = do
1423 , rootSubscribedUninit = uninit
1467 , rootSubscribedUninit = uninit
1424 , rootSubscribedWeakSelf = weakSelf
1468 , rootSubscribedWeakSelf = weakSelf
1425#ifdef DEBUG_NODEIDS
1469#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1470+ , rootSubscribedNodeId = nid
1427#endif
1471#endif
1428 }
1472 }
1429 -- If we die at the same moment that all our children die, they will
1473 -- If we die at the same moment that all our children die, they will
@@ -1472,16 +1516,10 @@ newFanInt = do
1472 , _fanInt_occRef = occRef
1516 , _fanInt_occRef = occRef
1473 }
1517 }
1474
1518
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
1519fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1520+fanInt p = unsafePerformIO $ do
1483− let self = unsafeNewFanInt p
1521+ self <- newFanInt
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1522+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
1485 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1523 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1486 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1524 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1487 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1525 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1489,15 +1527,15 @@ fanInt p =
1489 liftIO $ writeIORef (_fanInt_occRef self) m
1527 liftIO $ writeIORef (_fanInt_occRef self) m
1490 scheduleIntClear $ _fanInt_occRef self
1528 scheduleIntClear $ _fanInt_occRef self
1491 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1529 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1492− FastWeakBag.traverse b $ \s -> do
1530+ FastWeakBag.traverse_ b $ \s -> do
1493 subscriberPropagate s v
1531 subscriberPropagate s v
1494 , subscriberInvalidateHeight = \old -> do
1532 , subscriberInvalidateHeight = \old -> do
1495 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1533 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1496− FastWeakBag.traverse b $ \s -> do
1534+ FastWeakBag.traverse_ b $ \s -> do
1497 subscriberInvalidateHeight s old
1535 subscriberInvalidateHeight s old
1498 , subscriberRecalculateHeight = \new -> do
1536 , subscriberRecalculateHeight = \new -> do
1499 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1537 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1500− FastWeakBag.traverse b $ \s -> do
1538+ FastWeakBag.traverse_ b $ \s -> do
1501 subscriberRecalculateHeight s new
1539 subscriberRecalculateHeight s new
1502 }
1540 }
1503 liftIO $ do
1541 liftIO $ do
@@ -1517,7 +1555,7 @@ fanInt p =
1517 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1555 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1518
1556
1519{-# INLINABLE getFanSubscribed #-}
1557{-# INLINABLE getFanSubscribed #-}
1520−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1558+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k v -> Subscriber x (v a) -> EventM x (WeakBagTicket, FanSubscribed x k v, Maybe (v a))
1521getFanSubscribed k f sub = do
1559getFanSubscribed k f sub = do
1522 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1560 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1523 case mSubscribed of
1561 case mSubscribed of
@@ -1535,13 +1573,16 @@ getFanSubscribed k f sub = do
1535 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1573 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1536 occRef <- liftIO $ newIORef parentOcc
1574 occRef <- liftIO $ newIORef parentOcc
1537 when (isJust parentOcc) $ scheduleClear occRef
1575 when (isJust parentOcc) $ scheduleClear occRef
1576+#ifdef DEBUG_NODEIDS
1577+ nid <- liftIO newNodeId
1578+#endif
1538 let subscribed = FanSubscribed
1579 let subscribed = FanSubscribed
1539 { fanSubscribedCachedSubscribed = fanSubscribed f
1580 { fanSubscribedCachedSubscribed = fanSubscribed f
1540 , fanSubscribedOccurrence = occRef
1581 , fanSubscribedOccurrence = occRef
1541 , fanSubscribedParent = subscription
1582 , fanSubscribedParent = subscription
1542 , fanSubscribedSubscribers = subscribersRef
1583 , fanSubscribedSubscribers = subscribersRef
1543#ifdef DEBUG_NODEIDS
1584#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1585+ , fanSubscribedNodeId = nid
1545#endif
1586#endif
1546 }
1587 }
1547 let !self = (k, subscribed)
1588 let !self = (k, subscribed)
@@ -1551,7 +1592,7 @@ getFanSubscribed k f sub = do
1551 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1592 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1552 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1593 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1553
1594
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1595+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
1555cleanupFanSubscribed (k, subscribed) = do
1596cleanupFanSubscribed (k, subscribed) = do
1556 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1597 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1557 let reducedSubscribers = DMap.delete k subscribers
1598 let reducedSubscribers = DMap.delete k subscribers
@@ -1563,7 +1604,7 @@ cleanupFanSubscribed (k, subscribed) = do
1563 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1604 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1564
1605
1565{-# INLINE subscribeFanSubscribed #-}
1606{-# INLINE subscribeFanSubscribed #-}
1566−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1607+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
1567subscribeFanSubscribed k subscribed sub = do
1608subscribeFanSubscribed k subscribed sub = do
1568 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1609 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1569 case DMap.lookup k subscribers of
1610 case DMap.lookup k subscribers of
@@ -1601,6 +1642,9 @@ getSwitchSubscribed s sub = do
1601 when (isJust parentOcc) $ scheduleClear occRef
1642 when (isJust parentOcc) $ scheduleClear occRef
1602 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1643 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1603 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1644 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1645+#ifdef DEBUG_NODEIDS
1646+ nid <- liftIO newNodeId
1647+#endif
1604 let !subscribed = SwitchSubscribed
1648 let !subscribed = SwitchSubscribed
1605 { switchSubscribedCachedSubscribed = switchSubscribed s
1649 { switchSubscribedCachedSubscribed = switchSubscribed s
1606 , switchSubscribedOccurrence = occRef
1650 , switchSubscribedOccurrence = occRef
@@ -1613,7 +1657,7 @@ getSwitchSubscribed s sub = do
1613 , switchSubscribedCurrentParent = subscriptionRef
1657 , switchSubscribedCurrentParent = subscriptionRef
1614 , switchSubscribedWeakSelf = weakSelf
1658 , switchSubscribedWeakSelf = weakSelf
1615#ifdef DEBUG_NODEIDS
1659#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1660+ , switchSubscribedNodeId = nid
1617#endif
1661#endif
1618 }
1662 }
1619 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1663 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1658,6 +1702,9 @@ getCoincidenceSubscribed c sub = do
1658 scheduleClear innerSubdRef
1702 scheduleClear innerSubdRef
1659 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1703 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1660 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1704 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1705+#ifdef DEBUG_NODEIDS
1706+ nid <- liftIO newNodeId
1707+#endif
1661 let subscribed = CoincidenceSubscribed
1708 let subscribed = CoincidenceSubscribed
1662 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1709 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1663 , coincidenceSubscribedOccurrence = occRef
1710 , coincidenceSubscribedOccurrence = occRef
@@ -1668,7 +1715,7 @@ getCoincidenceSubscribed c sub = do
1668 , coincidenceSubscribedInnerParent = innerSubdRef
1715 , coincidenceSubscribedInnerParent = innerSubdRef
1669 , coincidenceSubscribedWeakSelf = weakSelf
1716 , coincidenceSubscribedWeakSelf = weakSelf
1670#ifdef DEBUG_NODEIDS
1717#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1718+ , coincidenceSubscribedNodeId = nid
1672#endif
1719#endif
1673 }
1720 }
1674 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1721 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1685,26 +1732,34 @@ cleanupCoincidenceSubscribed subscribed = do
1685subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1732subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1686subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1733subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1687
1734
1688−{-# INLINE merge #-}
1735+{-# INLINE mergeG #-}
1689−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1736+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1737+ => (forall a. q a -> Event x (v a))
1738+ -> DynamicS x (PatchDMap k q) -> Event x (DMap k v)
1690−merge d = cacheEvent (mergeCheap d)
1739+mergeG nt d = cacheEvent (mergeCheap nt d)
1691
1740
1692{-# INLINE mergeWithMove #-}
1741{-# INLINE mergeWithMove #-}
1693−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1742+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1743+ => (forall a. q a -> Event x (v a))
1744+ -> DynamicS x (PatchDMapWithMove k q) -> Event x (DMap k v)
1694−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1745+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
1695
1746
1696{-# INLINE [1] mergeCheap #-}
1747{-# INLINE [1] mergeCheap #-}
1748+mergeCheap
1697−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1749+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1750+ => (forall a. q a -> Event x (v a))
1751+ -> DynamicS x (PatchDMap k q)
1752+ -> Event x (DMap k v)
1698−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1753+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
1699 where
1754 where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1755+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
1701 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1756 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1702 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1757 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1703 (mOldSubd, newPs) <- case me of
1758 (mOldSubd, newPs) <- case me of
1704 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1759 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1705 Just e -> do
1760 Just e -> do
1706 let s = subscriber $ return k
1761 let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1762+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
1708 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1763 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1709 let newParent = MergeSubscribedParent subscription
1764 let newParent = MergeSubscribedParent subscription
1710 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1765 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1714,28 +1769,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1714 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1769 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1715 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1770 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1716 foldM f ([], oldParents) $ DMap.toList p
1771 foldM f ([], oldParents) $ DMap.toList p
1772+
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1773+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
1718 getInitialSubscribers initialParents subscriber = do
1774 getInitialSubscribers initialParents subscriber = do
1719 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1775 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1720 let s = subscriber $ return k
1776 let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1777+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1722 height <- liftIO $ getEventSubscribedHeight parentSubd
1778 height <- liftIO $ getEventSubscribedHeight parentSubd
1723− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1779+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
1724 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1780 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1725 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1781 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1726 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1782 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1727 )
1783 )
1784+
1728 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1785 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1729 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1786 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1730
1787
1731{-# INLINE [1] mergeCheapWithMove #-}
1788{-# INLINE [1] mergeCheapWithMove #-}
1732−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1789+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1790+ => (forall a. q a -> Event x (v a))
1791+ -> DynamicS x (PatchDMapWithMove k q)
1792+ -> Event x (DMap k v)
1733−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1793+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
1734 where
1794 where
1735− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1795+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
1736 updateMe subscriber heightBagRef oldParents p = do
1796 updateMe subscriber heightBagRef oldParents p = do
1737 -- Prepare new parents for insertion
1797 -- Prepare new parents for insertion
1738− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1798+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
1739 subscribeParent k e = do
1799 subscribeParent k e = do
1740 keyRef <- liftIO $ newIORef k
1800 keyRef <- liftIO $ newIORef k
1741 let s = subscriber $ liftIO $ readIORef keyRef
1801 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1744,9 +1804,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1744 newParentHeight <- getEventSubscribedHeight subd
1804 newParentHeight <- getEventSubscribedHeight subd
1745 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1805 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1746 return $ MergeSubscribedParentWithMove subscription keyRef
1806 return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1807+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
1748 -- Collect old parents for deletion and update the keys of moved parents
1808 -- Collect old parents for deletion and update the keys of moved parents
1749− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1809+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1750 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1810 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1751 Nothing -> do
1811 Nothing -> do
1752 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
1812 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1757,46 +1817,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1757 return Nothing
1817 return Nothing
1758 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1818 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1759 return (toDelete, applyAlways p' oldParents)
1819 return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1820+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
1761 getInitialSubscribers initialParents subscriber = do
1821 getInitialSubscribers initialParents subscriber = do
1762 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1822 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1763 keyRef <- liftIO $ newIORef k
1823 keyRef <- liftIO $ newIORef k
1764 let s = subscriber $ liftIO $ readIORef keyRef
1824 let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1825+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1766 height <- liftIO $ getEventSubscribedHeight parentSubd
1826 height <- liftIO $ getEventSubscribedHeight parentSubd
1767− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1827+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1768 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1828 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1769 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1829 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1770 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1830 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1771 )
1831 )
1832+
1772 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1833 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1773 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1834 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1774
1835
1775−type MergeUpdateFunc k x p s
1836+type MergeUpdateFunc k v x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1837+ = (forall a. EventM x (k a) -> Subscriber x (v a))
1777 -> IORef HeightBag
1838 -> IORef HeightBag
1778 -> DMap k s
1839 -> DMap k s
1779 -> p
1840 -> p
1780 -> EventM x ([EventSubscription x], DMap k s)
1841 -> EventM x ([EventSubscription x], DMap k s)
1781
1842
1782−type MergeInitFunc k x s
1843+type MergeInitFunc k v q x s
1783− = DMap k (Event x)
1844+ = DMap k q
1784− -> (forall a. EventM x (k a) -> Subscriber x a)
1845+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1785− -> EventM x (DMap k Identity, [Height], DMap k s)
1846+ -> EventM x (DMap k v, [Height], DMap k s)
1786
1847
1787type MergeDestroyFunc k s
1848type MergeDestroyFunc k s
1788 = DMap k s
1849 = DMap k s
1789 -> IO ()
1850 -> IO ()
1790
1851
1791−data Merge x k s = Merge
1852+data Merge x k v s = Merge
1792 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1853 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1793 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1854 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1794 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1855 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1856+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1857+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
1797 }
1858 }
1798
1859
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1860+invalidateMergeHeight :: Merge x k v s -> IO ()
1800invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1861invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1801
1862
1802invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1863invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1806,8 +1867,7 @@ invalidateMergeHeight' heightRef sub = do
1806 writeIORef heightRef $! invalidHeight
1867 writeIORef heightRef $! invalidHeight
1807 subscriberInvalidateHeight sub oldHeight
1868 subscriberInvalidateHeight sub oldHeight
1808
1869
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1870+revalidateMergeHeight :: Merge x k v s -> IO ()
1811revalidateMergeHeight m = do
1871revalidateMergeHeight m = do
1812 currentHeight <- readIORef $ _merge_heightRef m
1872 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
1873 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1823,19 +1883,19 @@ revalidateMergeHeight m = do
1823 subscriberRecalculateHeight (_merge_sub m) height
1883 subscriberRecalculateHeight (_merge_sub m) height
1824 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1884 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1825
1885
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1886+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
1827scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1887scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1828 vals <- liftIO $ readIORef $ _merge_accumRef m
1888 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
1889 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
1890 --TODO: Assert that m is not empty
1831 subscriberPropagate (_merge_sub m) vals
1891 subscriberPropagate (_merge_sub m) vals
1832
1892
1833−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1893+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
1834mergeSubscriber m getKey = Subscriber
1894mergeSubscriber m getKey = Subscriber
1835 { subscriberPropagate = \a -> do
1895 { subscriberPropagate = \a -> do
1836 oldM <- liftIO $ readIORef $ _merge_accumRef m
1896 oldM <- liftIO $ readIORef $ _merge_accumRef m
1837 k <- getKey
1897 k <- getKey
1838− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1898+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
1839 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1899 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
1900 liftIO $ writeIORef (_merge_accumRef m) $! newM
1841 when (DMap.null oldM) $ do -- Only schedule the firing once
1901 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1850,7 +1910,7 @@ mergeSubscriber m getKey = Subscriber
1850 else liftIO $ do
1910 else liftIO $ do
1851#ifdef DEBUG_CYCLES
1911#ifdef DEBUG_CYCLES
1852 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1912 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1913+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
1854 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1914 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1855#else
1915#else
1856 let cycleInfo = ""
1916 let cycleInfo = ""
@@ -1866,7 +1926,7 @@ mergeSubscriber m getKey = Subscriber
1866 }
1926 }
1867
1927
1868--TODO: Be able to run as much of this as possible promptly
1928--TODO: Be able to run as much of this as possible promptly
1869−updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1929+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
1870updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1930updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1871 where updateMe = do
1931 where updateMe = do
1872 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1932 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1874,9 +1934,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
1874 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1934 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1875 return subscriptionsToKill
1935 return subscriptionsToKill
1876
1936
1877−{-# INLINE mergeCheap' #-}
1937+{-# INLINE mergeGCheap' #-}
1938+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)
1939+ => 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
1940+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1880 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1941 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1881 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1942 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1882 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1943 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1924,11 +1985,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1924 , occ
1985 , occ
1925 )
1986 )
1926
1987
1927−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1988+mergeInt :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1928mergeInt = cacheEvent . mergeIntCheap
1989mergeInt = cacheEvent . mergeIntCheap
1929
1990
1930{-# INLINABLE mergeIntCheap #-}
1991{-# INLINABLE mergeIntCheap #-}
1931−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1992+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1932mergeIntCheap d = Event $ \sub -> do
1993mergeIntCheap d = Event $ \sub -> do
1933 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1994 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1934 accum <- liftIO $ FastMutableIntMap.newEmpty
1995 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2025,14 +2086,16 @@ mergeIntCheap d = Event $ \sub -> do
2025 )
2086 )
2026
2087
2027newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2088newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2089+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
2028
2090
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2091+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2030−fan e =
2092+fanG e = unsafePerformIO $ do
2093+ ref <- newIORef Nothing
2031 let f = Fan
2094 let f = Fan
2032 { fanParent = e
2095 { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2096+ , fanSubscribed = ref
2034 }
2097 }
2035− in EventSelector $ \k -> eventFan k f
2098+ pure $ EventSelectorG $ \k -> eventFan k f
2036
2099
2037runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2100runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2038runHoldInits holdInitRef dynInitRef mergeInitRef = do
2101runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2086,18 +2149,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
2086-- | Run an event action outside of a frame
2149-- | 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
2150runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2088runFrame a = SpiderHost $ do
2151runFrame a = SpiderHost $ do
2089− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2152+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
2090 let go = do
2153 let go = do
2091 result <- a
2154 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
2155 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
2156 return result
2094 result <- runEventM go
2157 result <- runEventM go
2095 toClear <- readIORef $ eventEnvClears env
2158 toClear <- readIORef $ eventEnvClears env
2096− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2159+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
2097 toClearInt <- readIORef $ eventEnvIntClears env
2160 toClearInt <- readIORef $ eventEnvIntClears env
2098− forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2161+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2099 toClearRoot <- readIORef $ eventEnvRootClears env
2162 toClearRoot <- readIORef $ eventEnvRootClears env
2100− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2163+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2101 toAssign <- readIORef $ eventEnvAssignments env
2164 toAssign <- readIORef $ eventEnvAssignments env
2102 toReconnectRef <- newIORef []
2165 toReconnectRef <- newIORef []
2103 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
2166 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2144,7 +2207,7 @@ runFrame a = SpiderHost $ do
2144 myHeight <- readIORef $ switchSubscribedHeight subscribed
2207 myHeight <- readIORef $ switchSubscribedHeight subscribed
2145 when (parentHeight /= myHeight) $ do
2208 when (parentHeight /= myHeight) $ do
2146 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2209 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2147− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2210+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2148 mapM_ _someMergeUpdate_invalidateHeight mergeUpdates --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
2211 mapM_ _someMergeUpdate_invalidateHeight mergeUpdates --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
2149 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
2212 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
2150 unsubscribe subscription
2213 unsubscribe subscription
@@ -2185,7 +2248,7 @@ invalidateCoincidenceHeight subscribed = do
2185 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2248 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2186 when (oldHeight /= invalidHeight) $ do
2249 when (oldHeight /= invalidHeight) $ do
2187 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2250 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2188− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2251+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2189
2252
2190updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
2253updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
2191updateSwitchHeight new subscribed = do
2254updateSwitchHeight new subscribed = do
@@ -2193,7 +2256,7 @@ updateSwitchHeight new subscribed = do
2193 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2256 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2194 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2257 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2195 writeIORef (switchSubscribedHeight subscribed) $! new
2258 writeIORef (switchSubscribedHeight subscribed) $! new
2196− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2259+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2197
2260
2198recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2261recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2199recalculateCoincidenceHeight subscribed = do
2262recalculateCoincidenceHeight subscribed = do
@@ -2202,7 +2265,7 @@ recalculateCoincidenceHeight subscribed = do
2202 height <- calculateCoincidenceHeight subscribed
2265 height <- calculateCoincidenceHeight subscribed
2203 when (height /= invalidHeight) $ do
2266 when (height /= invalidHeight) $ do
2204 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2267 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2205− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2268+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2206
2269
2207calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
2270calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
2208calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
2271calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2297,7 +2360,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
2297 fail _ = error "Dynamic does not support 'fail'"
2360 fail _ = error "Dynamic does not support 'fail'"
2298
2361
2299{-# INLINABLE newJoinDyn #-}
2362{-# INLINABLE newJoinDyn #-}
2300−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
2363+newJoinDyn :: HasSpiderTimeline x => DynamicS x (Identity (DynamicS x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
2301newJoinDyn d =
2364newJoinDyn d =
2302 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2365 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2303 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
2366 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2307,9 +2370,13 @@ newJoinDyn d =
2307 in Reflex.Spider.Internal.unsafeBuildDynamic readV0 v'
2370 in Reflex.Spider.Internal.unsafeBuildDynamic readV0 v'
2308
2371
2309instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
2372instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
2310− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
2373+ fmap = mapDynamicSpider
2311 x <$ d = R.unsafeBuildDynamic (return x) $ x <$ R.updated d
2374 x <$ d = R.unsafeBuildDynamic (return x) $ x <$ R.updated d
2312
2375
2376+mapDynamicSpider :: HasSpiderTimeline x => (a -> b) -> Reflex.Class.Dynamic (SpiderTimeline x) a -> Reflex.Class.Dynamic (SpiderTimeline x) b
2377+mapDynamicSpider f = SpiderDynamic . newMapDyn f . unSpiderDynamic
2378+{-# INLINE [1] mapDynamicSpider #-}
2379+
2313instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
2380instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
2314 pure = SpiderDynamic . dynamicConst
2381 pure = SpiderDynamic . dynamicConst
2315#if MIN_VERSION_base(4,10,0)
2382#if MIN_VERSION_base(4,10,0)
@@ -2443,7 +2510,7 @@ unsafeNewSpiderTimelineEnv = do
2443#ifdef DEBUG
2510#ifdef DEBUG
2444 depthRef <- newIORef 0
2511 depthRef <- newIORef 0
2445#endif
2512#endif
2446− return $ SpiderTimelineEnv
2513+ return $ STE $ SpiderTimelineEnv
2447 { _spiderTimeline_lock = lock
2514 { _spiderTimeline_lock = lock
2448 , _spiderTimeline_eventEnv = env
2515 , _spiderTimeline_eventEnv = env
2449#ifdef DEBUG
2516#ifdef DEBUG
@@ -2453,19 +2520,19 @@ unsafeNewSpiderTimelineEnv = do
2453
2520
2454-- | Create a new SpiderTimelineEnv
2521-- | Create a new SpiderTimelineEnv
2455newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2522newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2456−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2523+newSpiderTimeline = withSpiderTimeline (pure . Some)
2457
2524
2458−data LocalSpiderTimeline x s
2525+data LocalSpiderTimeline (x :: Type) s
2459
2526
2460instance Reifies s (SpiderTimelineEnv x) =>
2527instance Reifies s (SpiderTimelineEnv x) =>
2461 HasSpiderTimeline (LocalSpiderTimeline x s) where
2528 HasSpiderTimeline (LocalSpiderTimeline x s) where
2462− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2529+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
2463
2530
2464localSpiderTimeline
2531localSpiderTimeline
2465− :: Proxy s
2532+ :: proxy s
2466 -> SpiderTimelineEnv x
2533 -> SpiderTimelineEnv x
2467 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2534 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2468−localSpiderTimeline _ = unsafeCoerce
2535+localSpiderTimeline _ = coerce
2469
2536
2470-- | Pass a new timeline to the given function.
2537-- | Pass a new timeline to the given function.
2471withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2538withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2473,18 +2540,18 @@ withSpiderTimeline k = do
2473 env <- unsafeNewSpiderTimelineEnv
2540 env <- unsafeNewSpiderTimelineEnv
2474 reify env $ \s -> k $ localSpiderTimeline s env
2541 reify env $ \s -> k $ localSpiderTimeline s env
2475
2542
2476−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2543+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2477
2544
2478type ComputeM = EventM
2545type ComputeM = EventM
2479
2546
2480−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2547+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2481
2548
2482instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2549instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2483 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2550 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2484 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2551 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2485 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2552 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2486− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2553+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2487− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2554+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
2488 type PullM (SpiderTimeline x) = SpiderPullM x
2555 type PullM (SpiderTimeline x) = SpiderPullM x
2489 type PushM (SpiderTimeline x) = SpiderPushM x
2556 type PushM (SpiderTimeline x) = SpiderPushM x
2490 {-# INLINABLE never #-}
2557 {-# INLINABLE never #-}
@@ -2497,10 +2564,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2497 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2564 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2498 {-# INLINABLE pull #-}
2565 {-# INLINABLE pull #-}
2499 pull = SpiderBehavior . pull . coerce
2566 pull = SpiderBehavior . pull . coerce
2500− {-# INLINABLE merge #-}
2567+ {-# INLINABLE fanG #-}
2501− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2502− {-# INLINABLE fan #-}
2503− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2568+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2569+ {-# INLINABLE mergeG #-}
2570+ mergeG
2571+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2572+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2573+ -> DMap k q
2574+ -> R.Event (SpiderTimeline x) (DMap k v)
2575+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
2504 {-# INLINABLE switch #-}
2576 {-# INLINABLE switch #-}
2505 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2577 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2506 {-# INLINABLE coincidence #-}
2578 {-# INLINABLE coincidence #-}
@@ -2508,15 +2580,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2508 {-# INLINABLE current #-}
2580 {-# INLINABLE current #-}
2509 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2581 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2510 {-# INLINABLE updated #-}
2582 {-# INLINABLE updated #-}
2511− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2583+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
2512 {-# INLINABLE unsafeBuildDynamic #-}
2584 {-# INLINABLE unsafeBuildDynamic #-}
2513 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2585 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2514 {-# INLINABLE unsafeBuildIncremental #-}
2586 {-# INLINABLE unsafeBuildIncremental #-}
2515 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2587 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2516− {-# INLINABLE mergeIncremental #-}
2588+ {-# INLINABLE mergeIncrementalG #-}
2517− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2589+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2518− {-# INLINABLE mergeIncrementalWithMove #-}
2590+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2519− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2591+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
2520 {-# INLINABLE currentIncremental #-}
2592 {-# INLINABLE currentIncremental #-}
2521 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2593 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2522 {-# INLINABLE updatedIncremental #-}
2594 {-# INLINABLE updatedIncremental #-}
@@ -2527,9 +2599,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2527 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2599 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2528 eventCoercion Coercion = Coercion
2600 eventCoercion Coercion = Coercion
2529 behaviorCoercion Coercion = Coercion
2601 behaviorCoercion Coercion = Coercion
2530− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2602+ dynamicCoercion Coercion = Coercion
2603+ incrementalCoercion Coercion Coercion = Coercion
2531 {-# INLINABLE mergeIntIncremental #-}
2604 {-# INLINABLE mergeIntIncremental #-}
2532− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2605+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
2533 {-# INLINABLE fanInt #-}
2606 {-# INLINABLE fanInt #-}
2534 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2607 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2535
2608
@@ -2554,7 +2627,7 @@ instance MonadAtomicRef (EventM x) where
2554 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2627 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2555
2628
2556-- | The monad for actions that manipulate a Spider timeline identified by @x@
2629-- | The monad for actions that manipulate a Spider timeline identified by @x@
2557−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2630+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2558
2631
2559instance Monad (SpiderHost x) where
2632instance Monad (SpiderHost x) where
2560 {-# INLINABLE (>>=) #-}
2633 {-# INLINABLE (>>=) #-}
@@ -2576,7 +2649,7 @@ runSpiderHost (SpiderHost a) = a
2576runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2649runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2577runSpiderHostForTimeline (SpiderHost a) _ = a
2650runSpiderHostForTimeline (SpiderHost a) _ = a
2578
2651
2579−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2652+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2580 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2653 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2581
2654
2582instance Monad (SpiderHostFrame x) where
2655instance 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+76−7
@@ -1,4 +1,7 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE BangPatterns #-}
1{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE GADTs #-}
2{-# LANGUAGE RecursiveDo #-}
5{-# LANGUAGE RecursiveDo #-}
3{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
4module Main where
7module Main where
@@ -6,8 +9,15 @@ module Main where
6import Control.Lens
9import Control.Lens
7import Control.Monad
10import Control.Monad
8import Control.Monad.Fix
11import Control.Monad.Fix
12+import qualified Data.Dependent.Map as DMap
13+import Data.Functor.Misc
14+import qualified Data.Map as M
9import Data.These
15import Data.These
10
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+
11import Reflex
21import Reflex
12import Reflex.EventWriter.Base
22import Reflex.EventWriter.Base
13import Test.Run
23import Test.Run
@@ -28,6 +38,13 @@ main = do
28 print os2
38 print os2
29 os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
39 os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
30 print os3
40 print os3
41+ os4@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
42+ print os4
43+ os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
44+ print os5
45+ os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
46+ print os6
47+ let ![[Nothing, Nothing]] = os6
31 return ()
48 return ()
32
49
33unwrapApp :: (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])
@@ -36,16 +53,13 @@ unwrapApp x appIn = do
36 return e
53 return e
37
54
38testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
55testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
39−testOrdering pulse = do
40− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
41− return ()
42
57
43testSimultaneous :: (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 ()
44testSimultaneous pulse = do
59testSimultaneous pulse = do
45 let e0 = fmapMaybe (^? here) pulse
60 let e0 = fmapMaybe (^? here) pulse
46 e1 = fmapMaybe (^? there) pulse
61 e1 = fmapMaybe (^? there) pulse
47 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)
48− return ()
49
63
50-- | 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
51-- doesn't count along with the new widget.
65-- doesn't count along with the new widget.
@@ -59,7 +73,62 @@ testMoribundTellEvent
59 => Event t ()
73 => Event t ()
60 -> EventWriterT t [Int] m ()
74 -> EventWriterT t [Int] m ()
61testMoribundTellEvent pulse = do
75testMoribundTellEvent pulse = do
62− rec let tellIntOnReplce :: Int -> EventWriterT t [Int] m ()
76+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
63− tellIntOnReplce x = tellEvent $ [x] <$ rwrFinished
77+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
64− (_, rwrFinished) <- runWithReplace (tellIntOnReplce 1) $ tellIntOnReplce 2 <$ pulse
78+ (_, rwrFinished) <- runWithReplace (tellIntOnReplace 1) $ tellIntOnReplace 2 <$ pulse
79+ return ()
80+
81+-- | The equivalent of 'testMoribundTellEvent' for 'traverseDMapWithKeyWithAdjust'.
82+testMoribundTellEventDMap
83+ :: forall t m
84+ . ( Reflex t
85+ , Adjustable t m
86+ , MonadHold t m
87+ , MonadFix m
88+ )
89+ => Event t ()
90+ -> EventWriterT t [Int] m ()
91+testMoribundTellEventDMap pulse = do
92+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
93+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
94+ (_, rwrFinished :: Event t (PatchDMap (Const2 () Int) Identity)) <-
95+ traverseDMapWithKeyWithAdjust
96+ (\(Const2 ()) (Identity v) -> Identity . const v <$> tellIntOnReplace v)
97+ (mapToDMap $ M.singleton () 1)
98+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton () 2) <$ pulse)
99+ return ()
100+
101+-- | Ensures that elements which are _not_ removed can still fire 'tellEvent's
102+-- during the same frame as other elements are updated.
103+testLiveTellEventDMap
104+ :: forall t m
105+ . ( Reflex t
106+ , Adjustable t m
107+ , MonadHold t m
108+ , MonadFix m
109+ )
110+ => Event t ()
111+ -> EventWriterT t [Int] m ()
112+testLiveTellEventDMap pulse = do
113+ rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
114+ tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
115+ (_, rwrFinished :: Event t (PatchDMap (Const2 Int ()) Identity)) <-
116+ traverseDMapWithKeyWithAdjust
117+ (\(Const2 k) (Identity ()) -> Identity <$> tellIntOnReplace k)
118+ (mapToDMap $ M.singleton 1 ())
119+ ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
65 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+10−1
@@ -1,15 +1,16 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RankNTypes #-}
7+{-# LANGUAGE StandaloneDeriving #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE UndecidableInstances #-}
9{-# LANGUAGE UndecidableInstances #-}
8
10
9import Control.Lens
11import Control.Lens
10import Control.Monad.Fix
12import Control.Monad.Fix
11import Data.Align
13import Data.Align
12−import Data.AppendMap () -- for the Align instance
13import qualified Data.AppendMap as AMap
14import qualified Data.AppendMap as AMap
14import Data.Functor.Misc
15import Data.Functor.Misc
15import Data.Map (Map)
16import Data.Map (Map)
@@ -18,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
18import Data.Semigroup
19import Data.Semigroup
19import Data.These
20import Data.These
20
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+
21import Reflex
26import Reflex
22import Reflex.Patch.MapWithMove
27import Reflex.Patch.MapWithMove
23import Test.Run
28import Test.Run
@@ -36,6 +41,10 @@ instance (Ord k, Query a, Eq (QueryResult a), Align (MonoidalMap k)) => Query (S
36newtype Selector k a = Selector { unSelector :: MonoidalMap k a }
41newtype Selector k a = Selector { unSelector :: MonoidalMap k a }
37 deriving (Show, Read, Eq, Ord, Functor)
42 deriving (Show, Read, Eq, Ord, Functor)
38
43
44+#if !(MIN_VERSION_monoidal_containers(0,4,1))
45+deriving instance Ord k => Align (MonoidalMap k)
46+#endif
47+
39instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Semigroup (Selector k a) where
48instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Semigroup (Selector k a) where
40 (Selector a) <> (Selector b) = Selector $ fmapMaybe id $ f a b
49 (Selector a) <> (Selector b) = Selector $ fmapMaybe id $ f a b
41 where
50 where
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/Reflex/Plan/Pure.hs+4−4
@@ -55,15 +55,15 @@ instance TestPlan (Pure Int) PurePlan where
55runPure :: PurePlan a -> (a, IntSet)
55runPure :: PurePlan a -> (a, IntSet)
56runPure (PurePlan p) = runStateT p mempty $ 0
56runPure (PurePlan p) = runStateT p mempty $ 0
57
57
58−relavantTimes :: IntSet -> IntSet
58+relevantTimes :: IntSet -> IntSet
59−relavantTimes occs = IntSet.fromList [0..l + 1]
59+relevantTimes occs = IntSet.fromList [0..l + 1]
60 where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
60 where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
61
61
62testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
62testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
63−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
63+testBehavior (b, occs) = IntMap.fromSet (sample b) (relevantTimes occs)
64
64
65testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
65testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
66−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
66+testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relevantTimes occs)
67
67
68
68
69
69
test/Reflex/Test/CrossImpl.hs+5
@@ -218,6 +218,11 @@ testCases =
218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219 let b' = pull $ sample =<< sample bb
219 let b' = pull $ sample =<< sample bb
220 return (b', e)
220 return (b', e)
221+ , (,) "foldDynWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
222+ d <- foldDyn (:) [] $
223+ pushAlways (\a -> foldDyn (:) [a] e) e
224+ let b = current (join (fmap distributeListOverDynPure d))
225+ return (b, e)
221 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
226 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222 bb <- hold "b" e
227 bb <- hold "b" e
223 bd <- hold never . fmap (const e) =<< headE e
228 bd <- hold never . fmap (const e) =<< headE e
test/Reflex/TestPlan.hs+2−2
@@ -26,8 +26,8 @@ import Reflex.Class
26import Prelude
26import Prelude
27
27
28class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
28class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
29− -- | Speicify a plan of an input Event firing
29+ -- | Specify a plan of an input Event firing
30− -- Occurances must be in the future (i.e. Time > 0)
30+ -- Occurrences must be in the future (i.e. Time > 0)
31 -- Initial specification is
31 -- Initial specification is
32
32
33 plan :: [(Word, a)] -> m (Event t a)
33 plan :: [(Word, a)] -> m (Event t a)
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