on develop help sign in
44 files changed+1195−1082
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+41 .travis.yml−177 ChangeLog.md+40−19 Quickref.md+1 README.md+15−23 cabal.project.freeze−2 reflex.cabal+45−36 release.nix+2−2 src/Data/List/Deferred.hs−60 src/Data/List/NonEmpty/Deferred.hs−12 src/Data/List/NonEmpty/Deferred/Internal.hs−30 src/Data/TagMap.hs−43 src/Data/Unique/Tag/Local.hs−11 src/Data/Unique/Tag/Local/Internal.hs−56 src/Reflex/Adjustable/Class.hs+2−1 src/Reflex/BehaviorWriter/Base.hs+13−4 src/Reflex/Class.hs+56−64 src/Reflex/Dynamic.hs+16−2 src/Reflex/DynamicWriter/Base.hs+13−4 src/Reflex/EventWriter/Base.hs+70−11 src/Reflex/FanTag.hs−23 src/Reflex/Host/Headless.hs+128 src/Reflex/NotReady/Class.hs−5 src/Reflex/PerformEvent/Base.hs+36−78 src/Reflex/PerformEvent/Class.hs−1 src/Reflex/PostBuild/Base.hs+2−1 src/Reflex/PostBuild/Class.hs−1 src/Reflex/Profiled.hs+4−2 src/Reflex/Pure.hs+6−1 src/Reflex/Query/Base.hs+9−3 src/Reflex/Requester/Base.hs+597−13 src/Reflex/Requester/Base/Internal.hs−309 src/Reflex/Requester/Class.hs+1−1 src/Reflex/Spider/Internal.hs+40−4 src/Reflex/TriggerEvent/Base.hs+2 test/Adjustable.hs+2−3 test/EventWriterT.hs+11−15 test/GC.hs+1 test/Headless.hs+10 test/Reflex/Plan/Pure.hs+1 test/Reflex/Test/CrossImpl.hs+1−1 test/Reflex/Test/Micro.hs+8−2 test/RequesterT.hs+20−61 test/hlint.hs+2−1
.github/workflows/haskell.ymlnew file+41
@@ -0,0 +1,41 @@
1+name: github-action
2+
3+on: [push, pull_request]
4+
5+jobs:
6+ build:
7+ strategy:
8+ matrix:
9+ ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2']
10+ os: ['ubuntu-latest', 'macos-latest']
11+ runs-on: ${{ matrix.os }}
12+
13+ name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
14+ steps:
15+ - uses: actions/checkout@v2
16+ - uses: actions/setup-haskell@v1
17+ with:
18+ ghc-version: ${{ matrix.ghc }}
19+ - name: Cache
20+ uses: actions/cache@v1
21+ env:
22+ cache-name: cache-cabal
23+ with:
24+ path: ~/.cabal
25+ key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
26+ restore-keys: |
27+ ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
28+ ${{ runner.os }}-${{ matrix.ghc }}-build-
29+ ${{ runner.os }}-${{ matrix.ghc }}-
30+ ${{ runner.os }}
31+
32+ - name: Install dependencies
33+ run: |
34+ cabal update
35+ cabal build --only-dependencies --enable-tests --enable-benchmarks
36+ - name: Build
37+ run: cabal build --enable-tests --enable-benchmarks all
38+ - name: Run tests
39+ run: cabal test all
40+ - name: Build Docs
41+ run: cabal haddock
.travis.ymldeleted−177
@@ -1,177 +0,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−#
9−language: c
10−dist: xenial
11−sudo: required
12−git:
13− # whether to recursively clone submodules
14− submodules: false
15−cache:
16− directories:
17− - $HOME/.cabal/packages
18− - $HOME/.cabal/store
19−before_cache:
20− - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21− # remove files that are regenerated by 'cabal update'
22− - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23− - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26− - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27− - rm -rfv $CABALHOME/packages/head.hackage
28−matrix:
29− include:
30− - compiler: ghcjs-8.4
31− addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32− - compiler: ghc-8.8.1
33− addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
34− - compiler: ghc-8.6.5
35− addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
36− - compiler: ghc-8.4.4
37− addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
38− - compiler: ghc-8.2.2
39− addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
40− - compiler: ghc-8.0.2
41− addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
42−before_install:
43− - |
44− if [ "$TRAVIS_OS_NAME" = "linux" ]; then
45− sudo add-apt-repository -y ppa:hvr/ghcjs;
46− curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
47− sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
48− sudo apt-get update;
49− sudo apt-get install $CC cabal-install-3.0 nodejs;
50− fi
51− - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
52− - WITHCOMPILER="-w $HC"
53− - |
54− if echo $CC | grep -q ghcjs; then
55− GHCJS=true
56− HC=${HC}js
57− WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
58− else
59− GHCJS=false;
60− fi
61− - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
62− - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
63− - HCPKG="$HC-pkg"
64− - unset CC
65− - CABAL=/opt/ghc/bin/cabal
66− - CABALHOME=$HOME/.cabal
67− - export PATH="$CABALHOME/bin:$PATH"
68− - TOP=$(pwd)
69− - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
70− - echo $HCNUMVER
71− - CABAL="$CABAL -vnormal+nowrap+markoutput"
72− - set -o pipefail
73− - |
74− echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
75− echo 'BEGIN { state = "output"; }' >> .colorful.awk
76− echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
77− echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
78− echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
79− echo ' if (state == "cabal") {' >> .colorful.awk
80− echo ' print blue($0)' >> .colorful.awk
81− echo ' } else {' >> .colorful.awk
82− echo ' print $0' >> .colorful.awk
83− echo ' }' >> .colorful.awk
84− echo '}' >> .colorful.awk
85− - cat .colorful.awk
86− - |
87− color_cabal_output () {
88− awk -f $TOP/.colorful.awk
89− }
90− - echo text | color_cabal_output
91−install:
92− - ${CABAL} --version
93− - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
94− - node --version
95− - echo $GHCJS
96− - TEST=--enable-tests
97− - BENCH=--enable-benchmarks
98− - BENCH=--disable-benchmarks
99− - HEADHACKAGE=false
100− - rm -f $CABALHOME/config
101− - |
102− echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
103− echo "remote-build-reporting: anonymous" >> $CABALHOME/config
104− echo "write-ghc-environment-files: always" >> $CABALHOME/config
105− echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
106− echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
107− echo "world-file: $CABALHOME/world" >> $CABALHOME/config
108− echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
109− echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
110− echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
111− echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
112− echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
113− echo "install-dirs user" >> $CABALHOME/config
114− echo " prefix: $CABALHOME" >> $CABALHOME/config
115− echo "repository hackage.haskell.org" >> $CABALHOME/config
116− echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
117− - cat $CABALHOME/config
118− - rm -fv cabal.project cabal.project.local cabal.project.freeze
119− - travis_retry ${CABAL} v2-update -v
120− - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
121− # Generate cabal.project
122− - rm -rf cabal.project cabal.project.local cabal.project.freeze
123− - touch cabal.project
124− - |
125− echo "packages: ." >> cabal.project
126− - |
127− - cat cabal.project || true
128− - cat cabal.project.local || true
129− - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
130− - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
131− - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
132− - rm cabal.project.freeze
133−script:
134− - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
135− # Packaging...
136− - ${CABAL} v2-sdist all | color_cabal_output
137− # Unpacking...
138− - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
139− - cd ${DISTDIR} || false
140− - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
141− - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
142− - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')"
143− # Generate cabal.project
144− - rm -rf cabal.project cabal.project.local cabal.project.freeze
145− - touch cabal.project
146− - |
147− echo "packages: ${PKGDIR_reflex}" >> cabal.project
148− - |
149− - cat cabal.project || true
150− - cat cabal.project.local || true
151− # Building...
152− # this builds all libraries and executables (without tests/benchmarks)
153− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
154− # Building with tests and benchmarks...
155− # build & run tests, build benchmarks
156− - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
157− # Testing...
158− - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
159− # cabal check...
160− - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check)
161− # haddock...
162− - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
163− # Constraint sets
164− - rm -rf cabal.project.local
165− # Constraint set no-th
166− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output
167− # Constraint set old-these
168− - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi
169− # Constraint set old-witherable
170− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
171− # Constraint set debug-propagation
172− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-propagation' all | color_cabal_output
173− # Constraint set debug-cycles
174− - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-cycles' all | color_cabal_output
175−
176−# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
177−# EOF
ChangeLog.md+40−19
@@ -1,5 +1,45 @@
11# Revision history for reflex
22
3+## 0.8.2.0
4+
5+* Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once.
6+
7+## 0.8.1.1
8+
9+* Allow newer hlint for older GHCs, and add upper bound for newer GHCs
10+
11+## 0.8.1.0
12+
13+* Add support for GHC 8.10
14+* Drop support for GHC < 8.4
15+
16+## 0.8.0.0
17+
18+* Replace 0.7.2.0 with 0.8.0.0 to reflect the `MonadHold` interface change. Deprecates 0.7.2.0.
19+
20+## 0.7.2.0 -- *Deprecated*
21+
22+* ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`.
23+* Extend some dependency version bounds
24+* Fix HLint 3 test
25+
26+## 0.7.1.1
27+
28+*Backport release*.
29+Changes do not carry forward to 0.7.2.0.
30+
31+* Add support for GHC 8.10
32+* Drop support for GHC < 8.4
33+* Extend some dependency version bounds
34+* Fix HLint 3 test
35+
36+## 0.7.1.0
37+
38+* ([#413](https://github.com/reflex-frp/reflex/pull/413), [#417](https://github.com/reflex-frp/reflex/pull/417)) Add `Reflex.Host.Headless` module which provides `runHeadlessApp` as an easy way to run a Reflex network in a "headless" environment.
39+* ([#420](https://github.com/reflex-frp/reflex/pull/420)) Add a [`Data.Zip.Unzip`](https://hackage.haskell.org/package/semialign-1.1/docs/Data-Zip.html#t:Unzip) instance for `Event`.
40+* ([#419](https://github.com/reflex-frp/reflex/pull/419)) Add `distributeIntMapOverDynPure` and `joinDynThroughIntMap` as convenience functions for working with `Dynamic` `IntMap`s.
41+
42+
343## 0.7.0.0
444
545* Add lifting instances for most classes to `Reflex.Profiled.Profiled`. ([#398](https://github.com/reflex-frp/reflex/pull/398))
@@ -31,25 +71,6 @@
3171* Add cabal flags `debug-propagation` and `debug-event-cycles` to build in debugging
3272 code for performance and for cyclic dependencies between events
3373
34−* Refactor of `Reflex.Requester`:
35− * Updated:
36− * `RequesterData` to `RequestData`, `RequesterEnvelope`, and `ResponseData`
37− * `singletonRequesterData` to `singletonRequestData` and `singletonResponseData`
38− * `requesterDataToList` to `requestEnvelopesToDSums`
39− * Removed:
40− * `RequesterDataKey`
41− * `multiEntry`
42− * `unMultiEntry`
43− * `withRequesterT`
44− * `runWithReplaceRequesterTWith`
45− * `requesting'`
46− * `traverseIntMapWithKeyWithAdjustRequesterTWith`
47− * `traverseDMapWithKeyWithAdjustRequesterTWith`
48−
49−* Added `Data.List.Deferred` and `Data.List.NonEmpty.Deferred` for optimizing `<>` operations.
50−
51−* Added `Data.TagMap`, `Reflex.FanTag`, and `Data.Unique.Tag.Local` to improve request and response tagging.
52−
5374## 0.6.3
5475
5576* `Data.WeakBag.traverse` and `Data.FastWeakBag.traverse` have been deprecated.
Quickref.md+1
@@ -54,6 +54,7 @@ Since MonadHold depends on MonadSample, any [S] function also runs in [H] contex
5454[ ] difference :: Event a -> Event b -> Event a
5555[ ] align :: Event a -> Event b -> Event (These a b)
5656[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
57+ -- Note align functions from Data.Align in semialign package
5758[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
5859[ ] leftmost :: [Event a] -> Event a
5960[ ] mergeList :: [Event a] -> Event (NonEmpty a)
README.md+15−23
@@ -1,31 +1,23 @@
1−## [Reflex](https://reflex-frp.org/)
1+# [Reflex](https://reflex-frp.org/)
22
3−[![Hackage](https://img.shields.io/hackage/v/reflex.svg)](http://hackage.haskell.org/package/reflex)
3+[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/reflex.svg)](https://hackage.haskell.org/package/reflex) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/reflex/badge)](https://matrix.hackage.haskell.org/#/package/reflex) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE)
44
5−### Practical Functional Reactive Programming
5+Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.
66
7−Reflex is a 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 interface and an engine that efficiently implements that interface.
88
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.
9+**Visit https://reflex-frp.org for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
1010
11−A summary of Reflex functions is available in the [quick reference](Quickref.md).
11+## Resources
1212
13−**Visit https://reflex-frp.org/ for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
13+* [Official Website](https://reflex-frp.org)
14+* [Quick Reference](Quickref.md)
15+* [Reflex-DOM](https://github.com/reflex-frp/reflex-dom): A framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
16+* [Obelisk](https://github.com/obsidiansystems/obelisk#obelisk): A framework built on Reflex and Reflex-DOM for functional reactive web and mobile applications, with batteries included.
17+* [Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
18+* [/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
19+* [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
1420
15−### Resources
16−[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
21+## Hacking
1722
18−[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
19−
20−[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
21−
22−### Hacking
23−
24−From the root of a [Reflex
25−Platform](https://github.com/reflex-frp/reflex-platform) checkout, run
26−`./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex`. This
27−will check out the reflex source code into the
28−`haskell-overlays/reflex-packages/dep/reflex` directory. You can then
29−point that checkout at your fork, make changes, etc. Use the
30−`./try-reflex` or `./scripts/work-on` scripts to start a shell in
31−which you can test your changes.
23+From the root of a [Reflex Platform](https://github.com/reflex-frp/reflex-platform) checkout, run `./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex`. This will check out the reflex source code into the `haskell-overlays/reflex-packages/dep/reflex` directory. You can then point that checkout at your fork, make changes, etc. Use the `./try-reflex` or `./scripts/work-on` scripts to start a shell in which you can test your changes.
cabal.project.freezedeleted−2
@@ -1,2 +0,0 @@
1−constraints: any.text < 1.2.4.0
2− , hlint < 2.2.6 || > 2.2.6
reflex.cabal+45−36
@@ -1,7 +1,17 @@
11Name: reflex
2−Version: 0.7.0.0
2+Version: 0.8.2.0
33Synopsis: Higher-order Functional Reactive Programming
4−Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4+Description:
5+ Interactive programs without callbacks or side-effects.
6+ Functional Reactive Programming (FRP) uses composable events and time-varying
7+ values to describe interactive systems as pure functions.
8+ Just like other pure functional code, functional reactive code is easier
9+ to get right on the first try, maintain, and reuse.
10+ .
11+ Reflex is a fully-deterministic, higher-order Functional Reactive Programming
12+ interface and an engine that efficiently implements that interface.
13+ .
14+ <https://reflex-frp.org>
515License: BSD3
616License-file: LICENSE
717Author: Ryan Trinkle
@@ -18,8 +28,8 @@ extra-source-files:
1828 ChangeLog.md
1929
2030tested-with:
21− GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1,
22− GHCJS ==8.4
31+ GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2,
32+ GHCJS ==8.6
2333
2434flag use-reflex-optimizer
2535 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -61,22 +71,23 @@ library
6171 hs-source-dirs: src
6272 build-depends:
6373 MemoTrie == 0.6.*,
64− base >= 4.9 && < 4.14,
74+ base >= 4.11 && < 4.15,
6575 bifunctors >= 5.2 && < 5.6,
6676 comonad >= 5.0.4 && < 5.1,
77+ constraints >= 0.10 && <0.14,
6778 constraints-extras >= 0.3 && < 0.4,
6879 containers >= 0.6 && < 0.7,
6980 data-default >= 0.5 && < 0.8,
70− dependent-map >= 0.3 && < 0.4,
71− dlist == 0.8.*,
81+ dependent-map >= 0.3 && < 0.5,
7282 exception-transformers == 0.4.*,
7383 lens >= 4.7 && < 5,
84+ mmorph >= 1.0 && < 1.2,
7485 monad-control >= 1.0.1 && < 1.1,
7586 mtl >= 2.1 && < 2.3,
76− patch >= 0.0.2 && < 0.1,
77− prim-uniq >= 0.1.0.1 && < 0.2,
87+ patch >= 0.0.1 && < 0.1,
88+ prim-uniq >= 0.1.0.1 && < 0.3,
7889 primitive >= 0.5 && < 0.8,
79− profunctors >= 5.3 && < 5.6,
90+ profunctors >= 5.3 && < 5.7,
8091 random == 1.1.*,
8192 ref-tf == 0.4.*,
8293 reflection == 2.1.*,
@@ -86,10 +97,10 @@ library
8697 time >= 1.4 && < 1.10,
8798 transformers >= 0.5.6.0 && < 0.6,
8899 unbounded-delays >= 0.1.0.0 && < 0.2,
89− witherable >= 0.3 && < 0.3.2
100+ witherable >= 0.3 && < 0.5
90101
91102 if flag(split-these)
92− build-depends: these >= 1 && <1.1,
103+ build-depends: these >= 1 && <1.2,
93104 semialign >=1 && <1.2,
94105 monoidal-containers >= 0.6 && < 0.7
95106 else
@@ -101,19 +112,13 @@ library
101112 Data.AppendMap,
102113 Data.FastMutableIntMap,
103114 Data.FastWeakBag,
104− Data.List.Deferred,
105− Data.List.NonEmpty.Deferred,
106− Data.List.NonEmpty.Deferred.Internal,
107115 Data.Map.Misc,
108− Data.TagMap,
109− Data.Unique.Tag.Local,
110− Data.Unique.Tag.Local.Internal,
111116 Data.WeakBag,
112117 Reflex,
118+ Reflex.Class,
113119 Reflex.Adjustable.Class,
114120 Reflex.BehaviorWriter.Base,
115121 Reflex.BehaviorWriter.Class,
116− Reflex.Class,
117122 Reflex.Collection,
118123 Reflex.Dynamic,
119124 Reflex.Dynamic.Uniq,
@@ -123,10 +128,10 @@ library
123128 Reflex.EventWriter,
124129 Reflex.EventWriter.Base,
125130 Reflex.EventWriter.Class,
126− Reflex.FanTag,
127131 Reflex.FastWeak,
128132 Reflex.FunctorMaybe,
129133 Reflex.Host.Class,
134+ Reflex.Host.Headless,
130135 Reflex.Network,
131136 Reflex.NotReady.Class,
132137 Reflex.PerformEvent.Base,
@@ -138,7 +143,6 @@ library
138143 Reflex.Query.Base,
139144 Reflex.Query.Class,
140145 Reflex.Requester.Base,
141− Reflex.Requester.Base.Internal,
142146 Reflex.Requester.Class,
143147 Reflex.Spider,
144148 Reflex.Spider.Internal,
@@ -159,11 +163,11 @@ library
159163 patch:Data.Patch.MapWithMove as Reflex.Patch.MapWithMove
160164
161165 ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
162− ghc-prof-options: -fprof-auto-calls
163166
164167 if flag(debug-trace-events)
165168 cpp-options: -DDEBUG_TRACE_EVENTS
166− build-depends: bytestring
169+ build-depends:
170+ bytestring >= 0.10.8 && < 0.11
167171
168172 if flag(use-reflex-optimizer)
169173 cpp-options: -DUSE_REFLEX_OPTIMIZER
@@ -179,22 +183,23 @@ library
179183 if flag(use-template-haskell)
180184 cpp-options: -DUSE_TEMPLATE_HASKELL
181185 build-depends:
182− dependent-sum >= 0.6 && < 0.7,
183− haskell-src-exts >= 1.16 && < 1.23,
186+ dependent-sum >= 0.6 && < 0.8,
187+ haskell-src-exts >= 1.16 && < 1.24,
184188 haskell-src-meta >= 0.6 && < 0.9,
185− template-haskell >= 2.9 && < 2.16
189+ template-haskell >= 2.9 && < 2.17
186190 exposed-modules:
187191 Reflex.Dynamic.TH
188192 other-extensions: TemplateHaskell
189193 else
190194 build-depends:
191− dependent-sum >= 0.6 && < 0.7
195+ dependent-sum >= 0.6 && < 0.8
192196
193197 if flag(fast-weak) && impl(ghcjs)
194198 cpp-options: -DGHCJS_FAST_WEAK
195199
196200 if impl(ghcjs)
197− build-depends: ghcjs-base
201+ build-depends:
202+ ghcjs-base == 0.2.*
198203
199204test-suite semantics
200205 default-language: Haskell2010
@@ -202,7 +207,6 @@ test-suite semantics
202207 main-is: semantics.hs
203208 hs-source-dirs: test
204209 ghc-options: -O2 -Wall -rtsopts
205− ghc-prof-options: -fprof-auto-calls
206210 build-depends:
207211 base,
208212 bifunctors,
@@ -229,7 +233,6 @@ test-suite CrossImpl
229233 main-is: Reflex/Test/CrossImpl.hs
230234 hs-source-dirs: test
231235 ghc-options: -O2 -Wall -rtsopts
232− ghc-prof-options: -fprof-auto-calls
233236 build-depends:
234237 base,
235238 containers,
@@ -255,7 +258,10 @@ test-suite hlint
255258 , directory
256259 , filepath
257260 , filemanip
258− , hlint < 2.1 || >= 2.2.2
261+ if impl(ghc >= 8.8)
262+ build-depends: hlint >= 3 && < 4
263+ else
264+ build-depends: hlint (< 2.1 || >= 2.2.2) && < 4
259265 if impl(ghcjs)
260266 buildable: False
261267
@@ -321,14 +327,11 @@ test-suite RequesterT
321327 , constraints
322328 , constraints-extras
323329 , containers
324− , constraints
325− , constraints-extras
326330 , deepseq
327331 , dependent-map
328332 , dependent-sum
329333 , lens
330334 , mtl
331− , primitive
332335 , ref-tf
333336 , reflex
334337 , text
@@ -343,6 +346,14 @@ test-suite RequesterT
343346 Reflex.Plan.Pure
344347 Test.Run
345348
349+test-suite Headless
350+ default-language: Haskell2010
351+ type: exitcode-stdio-1.0
352+ main-is: Headless.hs
353+ hs-source-dirs: test
354+ build-depends: base
355+ , reflex
356+
346357test-suite Adjustable
347358 default-language: Haskell2010
348359 type: exitcode-stdio-1.0
@@ -436,7 +447,6 @@ benchmark spider-bench
436447 hs-source-dirs: bench test
437448 main-is: Main.hs
438449 ghc-options: -Wall -O2 -rtsopts
439− ghc-prof-options: -fprof-auto-calls
440450 build-depends:
441451 base,
442452 containers,
@@ -463,7 +473,6 @@ benchmark saulzar-bench
463473 c-sources: bench-cbits/checkCapability.c
464474 main-is: RunAll.hs
465475 ghc-options: -Wall -O2 -rtsopts -threaded
466− ghc-prof-options: -fprof-auto-calls
467476 build-depends:
468477 base,
469478 containers,
release.nix+2−2
@@ -1,12 +1,12 @@
11{ reflex-platform-fun ? import ./dep/reflex-platform
2+, supportedSystems ? ["x86_64-linux" "x86_64-darwin"]
23}:
34
45let
56 native-reflex-platform = reflex-platform-fun {};
67 inherit (native-reflex-platform.nixpkgs) lib;
7− systems = ["x86_64-linux" "x86_64-darwin"];
88
9− perPlatform = lib.genAttrs systems (system: let
9+ perPlatform = lib.genAttrs supportedSystems (system: let
1010 reflex-platform = reflex-platform-fun { inherit system; };
1111 compilers = [
1212 "ghc"
src/Data/List/Deferred.hsdeleted−60
@@ -1,60 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−module Data.List.Deferred
3− ( Deferred
4− , empty
5− , singleton
6− , toNonEmpty
7− , fromNonEmpty
8− , toList
9− ) where
10−
11−import Data.List.NonEmpty.Deferred.Internal (NonEmptyDeferred (..))
12−import qualified Data.List.NonEmpty.Deferred as NonEmpty
13−
14−data Deferred a
15− = Deferred_Empty
16− | Deferred_Singleton a
17− | Deferred_Append !(NonEmptyDeferred a) !(NonEmptyDeferred a)
18−
19−{-# INLINE toNonEmpty #-}
20−toNonEmpty :: Deferred a -> Maybe (NonEmptyDeferred a)
21−toNonEmpty = \case
22− Deferred_Empty -> Nothing
23− Deferred_Singleton a -> Just $ NonEmpty.singleton a
24− Deferred_Append a b -> Just $ a <> b
25−
26−{-# INLINE fromNonEmpty #-}
27−fromNonEmpty :: NonEmptyDeferred a -> Deferred a
28−fromNonEmpty = \case
29− NonEmptyDeferred_Singleton a -> Deferred_Singleton a
30− NonEmptyDeferred_Append a b -> Deferred_Append a b
31−
32−{-# INLINE empty #-}
33−empty :: Deferred a
34−empty = Deferred_Empty
35−
36−{-# INLINE singleton #-}
37−singleton :: a -> Deferred a
38−singleton = fromNonEmpty . NonEmpty.singleton
39−
40−{-# INLINE toList #-}
41−toList :: Deferred a -> [a]
42−toList = \case
43− Deferred_Empty -> []
44− Deferred_Singleton a -> [a]
45− Deferred_Append a b -> NonEmpty.toList $ a <> b
46−
47−instance Semigroup (Deferred a) where
48− (<>) = \case
49− Deferred_Empty -> id
50− a@(Deferred_Singleton va) -> \case
51− Deferred_Empty -> a
52− Deferred_Singleton vb -> Deferred_Append (NonEmpty.singleton va) (NonEmpty.singleton vb)
53− Deferred_Append b1 b2 -> Deferred_Append (NonEmpty.singleton va) (b1 <> b2)
54− a@(Deferred_Append a1 a2) -> \case
55− Deferred_Empty -> a
56− Deferred_Singleton vb -> Deferred_Append (a1 <> a2) (NonEmpty.singleton vb)
57− Deferred_Append b1 b2 -> Deferred_Append (a1 <> a2) (b1 <> b2)
58−
59−instance Monoid (Deferred a) where
60− mempty = Deferred_Empty
src/Data/List/NonEmpty/Deferred.hsdeleted−12
@@ -1,12 +0,0 @@
1−-- | Uses a non-associative internal structure to represent a NonEmpty list, but
2−-- prevents external observers from observing the non-associativity. This
3−-- allows O(1) '(<>)'.
4−
5−module Data.List.NonEmpty.Deferred
6− ( NonEmptyDeferred
7− , singleton
8− , toNonEmpty
9− , toList
10− ) where
11−
12−import Data.List.NonEmpty.Deferred.Internal
src/Data/List/NonEmpty/Deferred/Internal.hsdeleted−30
@@ -1,30 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−module Data.List.NonEmpty.Deferred.Internal where
3−
4−import Data.List.NonEmpty (NonEmpty (..))
5−import qualified Data.List.NonEmpty as NonEmpty
6−
7−data NonEmptyDeferred a
8− = NonEmptyDeferred_Singleton a
9− | NonEmptyDeferred_Append !(NonEmptyDeferred a) !(NonEmptyDeferred a)
10−
11−{-# INLINE singleton #-}
12−singleton :: a -> NonEmptyDeferred a
13−singleton = NonEmptyDeferred_Singleton
14−
15−{-# INLINE toNonEmpty #-}
16−toNonEmpty :: NonEmptyDeferred a -> NonEmpty a
17−toNonEmpty = go []
18− where go t = \case
19− NonEmptyDeferred_Singleton a -> a :| t
20− NonEmptyDeferred_Append a b -> go (NonEmpty.toList $ go t b) a
21−
22−{-# INLINE toList #-}
23−toList :: NonEmptyDeferred a -> [a]
24−toList = go []
25− where go t = \case
26− NonEmptyDeferred_Singleton a -> a : t
27− NonEmptyDeferred_Append a b -> go (go t b) a
28−
29−instance Semigroup (NonEmptyDeferred a) where
30− (<>) = NonEmptyDeferred_Append
src/Data/TagMap.hsdeleted−43
@@ -1,43 +0,0 @@
1−{-# LANGUAGE PolyKinds #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−module Data.TagMap
4− ( TagMap
5− , unTagMap
6− , fromDMap
7− , toDMap
8− , fromList
9− , insert
10− , size
11− , singletonTagMap
12− ) where
13−
14−import Data.IntMap (IntMap)
15−import qualified Data.IntMap as IntMap
16−import Data.Dependent.Map (DMap)
17−import qualified Data.Dependent.Map as DMap
18−import Data.Dependent.Sum (DSum (..))
19−import Data.Unique.Tag.Local
20−
21−import GHC.Exts (Any)
22−import Unsafe.Coerce
23−
24−-- | Like DMap, but with 'Data.Unique.Tag.Tag' as the keys. Implemented using 'Data.IntMap.IntMap' under the hood.
25−newtype TagMap x (v :: k -> *) = TagMap { unTagMap :: IntMap Any }
26−
27−fromDMap :: forall k x (v :: k -> *). DMap (Tag x) v -> TagMap x v
28−fromDMap = TagMap . IntMap.fromDistinctAscList . fmap (\((k :: Tag x (a :: k)) :=> v) -> (tagId k, (unsafeCoerce :: v a -> Any) v)) . DMap.toAscList
29−
30−toDMap :: forall x v. TagMap x v -> DMap (Tag x) v
31−toDMap = DMap.fromDistinctAscList . fmap (\(k, v) -> unsafeTagFromId k :=> (unsafeCoerce :: Any -> v a) v) . IntMap.toAscList . unTagMap
32−
33−insert :: forall x a v. Tag x a -> v a -> TagMap x v -> TagMap x v
34−insert k v = TagMap . IntMap.insert (tagId k) ((unsafeCoerce :: v a -> Any) v) . unTagMap
35−
36−fromList :: [DSum (Tag x) v] -> TagMap x v
37−fromList = TagMap . IntMap.fromList . fmap (\(t :=> v) -> (tagId t, (unsafeCoerce :: v a -> Any) v))
38−
39−size :: TagMap x v -> Int
40−size = IntMap.size . unTagMap
41−
42−singletonTagMap :: forall ps k v a. Tag ps k -> v a -> TagMap k v
43−singletonTagMap tag v = TagMap $ IntMap.singleton (tagId tag) $ (unsafeCoerce :: v a -> Any) v
src/Data/Unique/Tag/Local.hsdeleted−11
@@ -1,11 +0,0 @@
1−module Data.Unique.Tag.Local
2− ( Tag
3− , TagGen (..)
4− , tagId
5− , unsafeTagFromId
6− , newTag
7− , newTagGen
8− , withTagGen
9− ) where
10−
11−import Data.Unique.Tag.Local.Internal
src/Data/Unique/Tag/Local/Internal.hsdeleted−56
@@ -1,56 +0,0 @@
1−-- | The type-safety of this module depends on two assumptions:
2−-- 1. If the `s` parameters on two `TagGen`s can unify, then they contain the same MutVar
3−-- 2. Two Tag values made from the same TagGen never contain the same Int
4−
5−{-# LANGUAGE TypeFamilies #-}
6−{-# LANGUAGE PolyKinds #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE MagicHash #-}
10−module Data.Unique.Tag.Local.Internal where
11−
12−import Control.Monad.Primitive
13−import Data.Primitive.MutVar
14−import Data.GADT.Compare
15−import Data.Some
16−import GHC.Exts (Int (..), Int#, MutVar#, unsafeCoerce#)
17−
18−import Unsafe.Coerce
19−
20−-- `x` is which generator it's from
21−-- `a` is the type of the thing it's tagging
22−newtype Tag x a = Tag Int
23−
24−tagId :: Tag x a -> Int
25−tagId (Tag n) = n
26−
27−-- | WARNING: If you construct a tag with the wrong type, it will result in
28−-- incorrect unsafeCoerce applications, which can segfault or cause arbitrary
29−-- other damage to your program
30−unsafeTagFromId :: Int -> Tag x a
31−unsafeTagFromId = Tag
32−
33−-- We use Int because it is supported by e.g. IntMap
34−newtype TagGen ps s = TagGen { unTagGen :: MutVar ps Int }
35−
36−instance Show (TagGen ps s) where
37− show (TagGen (MutVar m)) = show $ I# ((unsafeCoerce# :: MutVar# ps Int -> Int#) m)
38−
39−instance GEq (TagGen ps) where
40− TagGen a `geq` TagGen b =
41− if a == b
42− then Just $ unsafeCoerce Refl
43− else Nothing
44−
45−newTag :: PrimMonad m => TagGen (PrimState m) s -> m (Tag s a)
46−newTag (TagGen r) = do
47− n <- atomicModifyMutVar' r $ \x -> (succ x, x)
48− pure $ Tag n
49−
50−newTagGen :: PrimMonad m => m (Some (TagGen (PrimState m)))
51−newTagGen = Some . TagGen <$> newMutVar minBound
52−
53−withTagGen :: PrimMonad m => (forall s. TagGen (PrimState m) s -> m a) -> m a
54−withTagGen f = do
55− g <- newTagGen
56− withSome g f
src/Reflex/Adjustable/Class.hs+2−1
@@ -30,7 +30,8 @@ module Reflex.Adjustable.Class
3030
3131import Control.Monad.Identity
3232import Control.Monad.Reader
33−import Data.Dependent.Map (DMap, GCompare (..))
33+import Data.Dependent.Map (DMap)
34+import Data.GADT.Compare (GCompare(..))
3435import qualified Data.Dependent.Map as DMap
3536import Data.Functor.Constant
3637import Data.Functor.Misc
src/Reflex/BehaviorWriter/Base.hs+13−4
@@ -24,6 +24,7 @@ module Reflex.BehaviorWriter.Base
2424import Control.Monad.Exception
2525import Control.Monad.Identity
2626import Control.Monad.IO.Class
27+import Control.Monad.Morph
2728import Control.Monad.Reader
2829import Control.Monad.Ref
2930import Control.Monad.State.Strict
@@ -48,7 +49,18 @@ import Reflex.TriggerEvent.Class
4849
4950-- | A basic implementation of 'BehaviorWriter'.
5051newtype 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+ -- The list is kept in reverse order
53+ deriving
54+ ( Functor
55+ , Applicative
56+ , Monad
57+ , MonadTrans
58+ , MFunctor
59+ , MonadIO
60+ , MonadFix
61+ , MonadAsyncException
62+ , MonadException
63+ )
5264
5365-- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided
5466-- along with the result of the action.
@@ -73,9 +85,6 @@ withBehaviorWriterT f dw = do
7385deriving instance MonadHold t m => MonadHold t (BehaviorWriterT t w m)
7486deriving instance MonadSample t m => MonadSample t (BehaviorWriterT t w m)
7587
76−instance MonadTrans (BehaviorWriterT t w) where
77− lift = BehaviorWriterT . lift
78−
7988instance MonadRef m => MonadRef (BehaviorWriterT t w m) where
8089 type Ref (BehaviorWriterT t w m) = Ref m
8190 newRef = lift . newRef
src/Reflex/Class.hs+56−64
@@ -169,8 +169,6 @@ module Reflex.Class
169169 , tagCheap
170170 , mergeWithCheap
171171 , mergeWithCheap'
172− , sconcatCheap
173− , mconcatCheap
174172 -- * Slow, but general, implementations
175173 , slowHeadE
176174 ) where
@@ -182,7 +180,7 @@ import Prelude hiding (zip, zipWith)
182180import Data.These.Combinators (justThese)
183181#endif
184182#if MIN_VERSION_semialign(1,1,0)
185−import Data.Zip (Zip (..))
183+import Data.Zip (Zip (..), Unzip (..))
186184#endif
187185#endif
188186
@@ -198,7 +196,8 @@ import Data.Align
198196import Data.Bifunctor
199197import Data.Coerce
200198import Data.Default
201−import Data.Dependent.Map (DMap, DSum (..))
199+import Data.Dependent.Map (DMap)
200+import Data.Dependent.Sum (DSum (..))
202201import qualified Data.Dependent.Map as DMap
203202import Data.Functor.Compose
204203import Data.Functor.Product
@@ -212,8 +211,7 @@ import Data.IntMap.Strict (IntMap)
212211import qualified Data.IntMap.Strict as IntMap
213212import Data.List.NonEmpty (NonEmpty (..))
214213import Data.Map (Map)
215−import qualified Data.Map as Map
216−import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
214+import Data.Semigroup (Semigroup (..))
217215import Data.Some (Some(Some))
218216import Data.String
219217import Data.These
@@ -423,6 +421,13 @@ class MonadSample t m => MonadHold t m where
423421 -- | Create a new 'Event' that only occurs only once, on the first occurrence of
424422 -- the supplied 'Event'.
425423 headE :: Event t a -> m (Event t a)
424+ -- | An event which only occurs at the current moment in time, such that:
425+ --
426+ -- > coincidence (pushAlways (\a -> (a <$) <$> now) e) = e
427+ --
428+ now :: m (Event t ())
429+ default now :: (m ~ f m', MonadTrans f, MonadHold t m') => m (Event t ())
430+ now = lift now
426431
427432-- | Accumulate an 'Incremental' with the supplied initial value and the firings of the provided 'Event',
428433-- using the combining function to produce a patch.
@@ -568,6 +573,7 @@ instance MonadHold t m => MonadHold t (ReaderT r m) where
568573 holdIncremental a0 = lift . holdIncremental a0
569574 buildDynamic a0 = lift . buildDynamic a0
570575 headE = lift . headE
576+ now = lift now
571577
572578instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
573579 sample = lift . sample
@@ -578,6 +584,7 @@ instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
578584 holdIncremental a0 = lift . holdIncremental a0
579585 buildDynamic a0 = lift . buildDynamic a0
580586 headE = lift . headE
587+ now = lift now
581588
582589instance MonadSample t m => MonadSample t (StateT s m) where
583590 sample = lift . sample
@@ -588,6 +595,7 @@ instance MonadHold t m => MonadHold t (StateT s m) where
588595 holdIncremental a0 = lift . holdIncremental a0
589596 buildDynamic a0 = lift . buildDynamic a0
590597 headE = lift . headE
598+ now = lift now
591599
592600instance MonadSample t m => MonadSample t (ExceptT e m) where
593601 sample = lift . sample
@@ -598,6 +606,7 @@ instance MonadHold t m => MonadHold t (ExceptT e m) where
598606 holdIncremental a0 = lift . holdIncremental a0
599607 buildDynamic a0 = lift . buildDynamic a0
600608 headE = lift . headE
609+ now = lift now
601610
602611instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
603612 sample = lift . sample
@@ -608,6 +617,7 @@ instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
608617 holdIncremental a0 = lift . holdIncremental a0
609618 buildDynamic a0 = lift . buildDynamic a0
610619 headE = lift . headE
620+ now = lift now
611621
612622instance MonadSample t m => MonadSample t (ContT r m) where
613623 sample = lift . sample
@@ -618,6 +628,7 @@ instance MonadHold t m => MonadHold t (ContT r m) where
618628 holdIncremental a0 = lift . holdIncremental a0
619629 buildDynamic a0 = lift . buildDynamic a0
620630 headE = lift . headE
631+ now = lift now
621632
622633--------------------------------------------------------------------------------
623634-- Convenience functions
@@ -742,6 +753,11 @@ instance Reflex t => Functor (Event t) where
742753 {-# INLINE (<$) #-}
743754 x <$ e = fmapCheap (const x) e
744755
756+-- TODO Remove this instance
757+instance Reflex t => FunctorMaybe (Event t) where
758+ {-# INLINE fmapMaybe #-}
759+ fmapMaybe = mapMaybe
760+
745761instance Reflex t => Filterable (Event t) where
746762 {-# INLINE mapMaybe #-}
747763 mapMaybe f = push $ return . f
@@ -895,12 +911,6 @@ instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
895911 sconcat = fmap sconcat . mergeList . toList
896912 stimes n = fmap $ stimes n
897913
898−sconcatCheap :: (Semigroup a, Reflex t) => NonEmpty (Event t a) -> Event t a
899−sconcatCheap = fmapCheap sconcat . mergeList . toList
900−
901−mconcatCheap :: (Semigroup a, Reflex t) => [Event t a] -> Event t a
902−mconcatCheap = fmapCheap sconcat . mergeList
903−
904914instance (Semigroup a, Reflex t) => Monoid (Event t a) where
905915 mempty = never
906916 mappend = (<>)
@@ -915,8 +925,6 @@ mergeWith = mergeWith' id
915925
916926{-# INLINE mergeWith' #-}
917927mergeWith' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
918−mergeWith' _ _ [] = never
919−mergeWith' f _ [e] = fmap f e
920928mergeWith' f g es = fmap (Prelude.foldl1 g . fmap f)
921929 . mergeInt
922930 . IntMap.fromDistinctAscList
@@ -934,7 +942,6 @@ leftmost = mergeWith const
934942-- time.
935943mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
936944mergeList [] = never
937−mergeList [e] = fmapCheap (:|[]) e
938945mergeList es = mergeWithFoldCheap' id es
939946
940947unsafeMapIncremental
@@ -951,6 +958,10 @@ unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIn
951958mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
952959mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
953960
961+-- | Like 'mergeMap' but for 'IntMap'.
962+mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
963+mergeIntMap = fmap dmapToIntMap . merge . intMapWithFunctorToDMap
964+
954965-- | Create a merge whose parents can change over time
955966mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
956967mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
@@ -1027,45 +1038,33 @@ switchHoldPromptly ea0 eea = do
10271038switchHoldPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
10281039switchHoldPromptOnly e0 e' = do
10291040 eLag <- switch <$> hold e0 e'
1030− return $ fmapMaybeCheap id $ leftmost
1031− [ fmapCheap Just $ coincidence e'
1032− , fmapCheap (const Nothing) e'
1033− , fmapCheap Just eLag
1034− ]
1041+ return $ coincidence $ leftmost [e', eLag <$ eLag]
10351042
10361043-- | When the given outer event fires, condense the inner events into the contained patch. Non-firing inner events will be replaced with deletions.
10371044coincidencePatchMap :: (Reflex t, Ord k) => Event t (PatchMap k (Event t v)) -> Event t (PatchMap k v)
1038−coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ fforCheap e $ \(PatchMap m) -> if Map.null m then never else
1039− let firingNewItems = mergeMap $ fforMaybe m $ \case
1040− Nothing -> Nothing
1041− Just ev -> Just $ fmapCheap Just ev
1042− oldItemMask = (Nothing <$ m) <$ e
1043− in alignWith (mergeThese Map.union) firingNewItems oldItemMask -- Must be left-biased
1045+coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ ffor e $ \(PatchMap m) -> mergeMap $ ffor m $ \case
1046+ Nothing -> fmapCheap (const Nothing) e
1047+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
10441048
10451049-- | See 'coincidencePatchMap'
10461050coincidencePatchIntMap :: Reflex t => Event t (PatchIntMap (Event t v)) -> Event t (PatchIntMap v)
1047−coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ fforCheap e $ \(PatchIntMap m) -> if IntMap.null m then never else
1048− let firingNewItems = mergeIntMap $ fforMaybe m $ \case
1049− Nothing -> Nothing
1050− Just ev -> Just $ fmapCheap Just ev
1051− oldItemMask = (Nothing <$ m) <$ e
1052− in alignWith (mergeThese IntMap.union) firingNewItems oldItemMask -- Must be left-biased
1051+coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ ffor e $ \(PatchIntMap m) -> mergeIntMap $ ffor m $ \case
1052+ Nothing -> fmapCheap (const Nothing) e
1053+ Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
10531054
10541055-- | See 'coincidencePatchMap'
10551056coincidencePatchMapWithMove :: (Reflex t, Ord k) => Event t (PatchMapWithMove k (Event t v)) -> Event t (PatchMapWithMove k v)
1056−coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ fforCheap e $ \p -> if Map.null (unPatchMapWithMove p) then never else
1057− let firingNewItems = mergeMap $ fforMaybe (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1058− PatchMapWithMove.From_Insert ev -> Just $ fforCheap ev $ \v ->
1059− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1060− _ -> Nothing
1061− oldItemMask = fforCheap e $ \_ -> ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1062− PatchMapWithMove.From_Delete ->
1063− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1064− PatchMapWithMove.From_Move k ->
1065− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1066− PatchMapWithMove.From_Insert _ ->
1067− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1068− in alignWith (mergeThese Map.union) firingNewItems oldItemMask -- Must be left-biased
1057+coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ ffor e $ \p -> mergeMap $ ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
1058+ PatchMapWithMove.From_Delete -> fforCheap e $ \_ ->
1059+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1060+ PatchMapWithMove.From_Move k -> fforCheap e $ \_ ->
1061+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
1062+ PatchMapWithMove.From_Insert ev -> leftmost
1063+ [ fforCheap ev $ \v ->
1064+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
1065+ , fforCheap e $ \_ ->
1066+ ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
1067+ ]
10691068
10701069-- | Given a 'PatchTarget' of events (e.g., a 'Map' with 'Event' values) and an event of 'Patch'es
10711070-- (e.g., a 'PatchMap' with 'Event' values), produce an 'Event' of the 'PatchTarget' type that
@@ -1106,6 +1105,12 @@ instance Reflex t => Zip (Event t) where
11061105 zip x y = mapMaybe justThese $ align x y
11071106#endif
11081107
1108+#ifdef MIN_VERSION_semialign
1109+#if MIN_VERSION_semialign(1,1,0)
1110+instance Reflex t => Unzip (Event t) where
1111+ unzip = splitE
1112+#endif
1113+#endif
11091114
11101115-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
11111116-- the 'Behavior' is true at the time of occurrence.
@@ -1318,7 +1323,7 @@ accumMaybeMDyn
13181323 -> Event t b
13191324 -> m (Dynamic t a)
13201325accumMaybeMDyn f z e = do
1321− rec let e' = flip pushCheap e $ \o -> do
1326+ rec let e' = flip push e $ \o -> do
13221327 v <- sample $ current d'
13231328 f v o
13241329 d' <- holdDyn z e'
@@ -1374,8 +1379,8 @@ mapAccumMaybeMDyn f z e = do
13741379 return $ case result of
13751380 (Nothing, Nothing) -> Nothing
13761381 _ -> Just result
1377− d' <- holdDyn z $ fmapMaybeCheap fst e'
1378− return (d', fmapMaybeCheap snd e')
1382+ d' <- holdDyn z $ mapMaybe fst e'
1383+ return (d', mapMaybe snd e')
13791384
13801385-- | Accumulate a 'Behavior' by folding occurrences of an 'Event'
13811386-- with the provided function.
@@ -1413,7 +1418,7 @@ accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
14131418{-# INLINE accumMaybeMB #-}
14141419accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
14151420accumMaybeMB f z e = do
1416− rec let e' = flip pushCheap e $ \o -> do
1421+ rec let e' = flip push e $ \o -> do
14171422 v <- sample d'
14181423 f v o
14191424 d' <- hold z e'
@@ -1463,8 +1468,8 @@ mapAccumMaybeMB f z e = do
14631468 return $ case result of
14641469 (Nothing, Nothing) -> Nothing
14651470 _ -> Just result
1466− d' <- hold z $ fmapMaybeCheap fst e'
1467− return (d', fmapMaybeCheap snd e')
1471+ d' <- hold z $ mapMaybe fst e'
1472+ return (d', mapMaybe snd e')
14681473
14691474-- | Accumulate occurrences of an 'Event', producing an output occurrence each
14701475-- time. Discard the underlying 'Accumulator'.
@@ -1674,8 +1679,6 @@ mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
16741679-- | A "cheap" version of 'mergeWithFoldCheap''. See the performance note on 'pushCheap'.
16751680{-# INLINE mergeWithFoldCheap' #-}
16761681mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1677−mergeWithFoldCheap' _ [] = never
1678−mergeWithFoldCheap' f [e] = fmapCheap (f . (:|[])) e
16791682mergeWithFoldCheap' f es =
16801683 fmapCheap (f . (\(h : t) -> h :| t) . IntMap.elems)
16811684 . mergeInt
@@ -1690,18 +1693,7 @@ mergeWithFoldCheap' f es =
16901693-- | See 'switchHoldPromptly'
16911694switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
16921695switchPromptly = switchHoldPromptly
1693−
16941696{-# 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'. " #-}
16951697-- | See 'switchHoldPromptOnly'
16961698switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
16971699switchPromptOnly = switchHoldPromptOnly
1698−
1699−{-# DEPRECATED mergeIntMap "Use 'mergeInt' instead" #-}
1700−-- | Like 'mergeMap' but for 'IntMap'.
1701−mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
1702−mergeIntMap = mergeInt
1703−
1704−-- NOTE: A deprecation warning is expected on this instance
1705−instance Reflex t => FunctorMaybe (Event t) where
1706− {-# INLINE fmapMaybe #-}
1707− fmapMaybe = mapMaybe
src/Reflex/Dynamic.hs+16−2
@@ -53,10 +53,12 @@ module Reflex.Dynamic
5353 , foldDynMaybe
5454 , foldDynMaybeM
5555 , joinDynThroughMap
56+ , joinDynThroughIntMap
5657 , traceDyn
5758 , traceDynWith
5859 , splitDynPure
5960 , distributeMapOverDynPure
61+ , distributeIntMapOverDynPure
6062 , distributeDMapOverDynPure
6163 , distributeListOverDynPure
6264 , Demux
@@ -80,6 +82,7 @@ import Data.Functor.Compose
8082import Data.Functor.Misc
8183import Reflex.Class
8284
85+import Control.Monad
8386import Control.Monad.Fix
8487import Control.Monad.Identity
8588import Data.Align
@@ -87,6 +90,8 @@ import Data.Dependent.Map (DMap)
8790import qualified Data.Dependent.Map as DMap
8891import Data.Dependent.Sum (DSum (..))
8992import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
93+import Data.IntMap (IntMap)
94+import qualified Data.IntMap as IntMap
9095import Data.Map (Map)
9196import Data.Maybe
9297import Data.Monoid ((<>))
@@ -191,7 +196,6 @@ switchPromptlyDyn de =
191196 let eLag = switch $ current de
192197 eCoincidences = coincidence $ updated de
193198 in leftmost [eCoincidences, eLag]
194−--TODO: switchPromptlyDyn should get the `only` treatment like switchHoldPromptOnly
195199
196200-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
197201splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
@@ -202,6 +206,11 @@ splitDynPure d = (fmap fst d, fmap snd d)
202206distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
203207distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
204208
209+-- | Convert an 'IntMap' with 'Dynamic' elements into a 'Dynamic' of an 'IntMap' with
210+-- non-'Dynamic' elements.
211+distributeIntMapOverDynPure :: (Reflex t) => IntMap (Dynamic t v) -> Dynamic t (IntMap v)
212+distributeIntMapOverDynPure = fmap dmapToIntMap . distributeDMapOverDynPure . intMapWithFunctorToDMap
213+
205214-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
206215-- non-'Dynamic' elements, preserving the order of the elements.
207216{-# DEPRECATED distributeListOverDynPure "Use 'distributeListOverDyn' instead" #-}
@@ -214,6 +223,11 @@ distributeListOverDynPure = distributeListOverDyn
214223joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
215224joinDynThroughMap = (distributeMapOverDynPure =<<)
216225
226+-- | Combine a 'Dynamic' of an 'IntMap' of 'Dynamic's into a 'Dynamic'
227+-- with the current values of the 'Dynamic's in a map.
228+joinDynThroughIntMap :: forall t a. (Reflex t) => Dynamic t (IntMap (Dynamic t a)) -> Dynamic t (IntMap a)
229+joinDynThroughIntMap = (distributeIntMapOverDynPure =<<)
230+
217231-- | Print the value of the 'Dynamic' when it is first read and on each
218232-- subsequent change that is observed (as 'traceEvent'), prefixed with the
219233-- provided string. This should /only/ be used for debugging.
@@ -334,7 +348,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
334348--
335349-- > demuxed (demux d) k === fmap (== k) d
336350--
337−-- However, when getDemuxed is used multiple times, the complexity is only
351+-- However, when 'demuxed' is used multiple times, the complexity is only
338352-- /O(log(n))/, rather than /O(n)/ for fmap.
339353data Demux t k = Demux { demuxValue :: Behavior t k
340354 , demuxSelector :: EventSelector t (Const2 k Bool)
src/Reflex/DynamicWriter/Base.hs+13−4
@@ -22,6 +22,7 @@ module Reflex.DynamicWriter.Base
2222import Control.Monad.Exception
2323import Control.Monad.Identity
2424import Control.Monad.IO.Class
25+import Control.Monad.Morph
2526import Control.Monad.Primitive
2627import Control.Monad.Reader
2728import Control.Monad.Ref
@@ -51,9 +52,6 @@ import Reflex.Query.Class
5152import Reflex.Requester.Class
5253import Reflex.TriggerEvent.Class
5354
54−instance MonadTrans (DynamicWriterT t w) where
55− lift = DynamicWriterT . lift
56−
5755mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
5856mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
5957
@@ -90,7 +88,18 @@ mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current)
9088
9189-- | A basic implementation of 'DynamicWriter'.
9290newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
93− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
91+ -- The list is kept in reverse order
92+ deriving
93+ ( Functor
94+ , Applicative
95+ , Monad
96+ , MonadTrans
97+ , MFunctor
98+ , MonadIO
99+ , MonadFix
100+ , MonadAsyncException
101+ , MonadException
102+ )
94103
95104deriving instance MonadHold t m => MonadHold t (DynamicWriterT t w m)
96105deriving instance MonadSample t m => MonadSample t (DynamicWriterT t w m)
src/Reflex/EventWriter/Base.hs+70−11
@@ -35,41 +35,98 @@ import Reflex.TriggerEvent.Class
3535
3636import Control.Monad.Exception
3737import Control.Monad.Identity
38+import Control.Monad.Morph
3839import Control.Monad.Primitive
3940import Control.Monad.Reader
4041import Control.Monad.Ref
4142import Control.Monad.State.Strict
4243import Data.Dependent.Map (DMap)
4344import qualified Data.Dependent.Map as DMap
45+import Data.Dependent.Sum (DSum (..))
4446import Data.Functor.Compose
4547import Data.Functor.Misc
46−import Data.GADT.Compare (GCompare (..))
48+import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
4749import Data.IntMap.Strict (IntMap)
4850import qualified Data.IntMap.Strict as IntMap
4951import Data.List.NonEmpty (NonEmpty (..))
50−import Data.List.Deferred (Deferred)
51−import qualified Data.List.Deferred as Deferred
5252import Data.Map (Map)
5353import qualified Data.Map as Map
5454import Data.Semigroup
5555import Data.Some (Some)
5656import Data.Tuple
57+import Data.Type.Equality
58+
59+import Unsafe.Coerce
60+
61+{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
62+newtype TellId w x
63+ = TellId Int -- ^ WARNING: Do not construct this directly; use 'tellId' instead
64+ deriving (Show, Eq, Ord, Enum)
65+
66+tellId :: Int -> TellId w w
67+tellId = TellId
68+{-# INLINE tellId #-}
69+
70+tellIdRefl :: TellId w x -> w :~: x
71+tellIdRefl _ = unsafeCoerce Refl
72+
73+withTellIdRefl :: TellId w x -> (w ~ x => r) -> r
74+withTellIdRefl tid r = case tellIdRefl tid of
75+ Refl -> r
76+
77+instance GEq (TellId w) where
78+ a `geq` b =
79+ withTellIdRefl a $
80+ withTellIdRefl b $
81+ if a == b
82+ then Just Refl
83+ else Nothing
84+
85+instance GCompare (TellId w) where
86+ a `gcompare` b =
87+ withTellIdRefl a $
88+ withTellIdRefl b $
89+ case a `compare` b of
90+ LT -> GLT
91+ EQ -> GEQ
92+ GT -> GGT
93+
94+data EventWriterState t w = EventWriterState
95+ { _eventWriterState_nextId :: {-# UNPACK #-} !Int -- Always negative (and decreasing over time)
96+ , _eventWriterState_told :: ![DSum (TellId w) (Event t)] -- In increasing order
97+ }
5798
5899-- | A basic implementation of 'EventWriter'.
59−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (Deferred (Event t w)) m a }
60− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
100+newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
101+ deriving
102+ ( Functor
103+ , Applicative
104+ , Monad
105+ , MonadTrans
106+ , MFunctor
107+ , MonadFix
108+ , MonadIO
109+ , MonadException
110+ , MonadAsyncException
111+ )
61112
62113-- | Run a 'EventWriterT' action.
63114runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
64115runEventWriterT (EventWriterT a) = do
65− (result, requests) <- runStateT a mempty
66− return (result, mconcatCheap $ Deferred.toList requests)
116+ (result, requests) <- runStateT a $ EventWriterState (-1) []
117+ let combineResults :: DMap (TellId w) Identity -> w
118+ combineResults = sconcat
119+ . (\(h : t) -> h :| t) -- Unconditional; 'merge' guarantees that it will only fire with non-empty DMaps
120+ . DMap.foldlWithKey (\vs tid (Identity v) -> withTellIdRefl tid $ v : vs) [] -- This is where we finally reverse the DMap to get things in the correct order
121+ return (result, fmap combineResults $ merge $ DMap.fromDistinctAscList $ _eventWriterState_told requests) --TODO: We can probably make this fromDistinctAscList more efficient by knowing the length in advance, but this will require exposing internals of DMap; also converting it to use a strict list might help
67122
68123instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m) where
69− tellEvent w = EventWriterT $ modify (<> Deferred.singleton w)
70−
71−instance MonadTrans (EventWriterT t w) where
72− lift = EventWriterT . lift
124+ tellEvent w = EventWriterT $ modify $ \old ->
125+ let myId = _eventWriterState_nextId old
126+ in EventWriterState
127+ { _eventWriterState_nextId = pred myId
128+ , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
129+ }
73130
74131instance MonadSample t m => MonadSample t (EventWriterT t w m) where
75132 sample = lift . sample
@@ -85,6 +142,8 @@ instance MonadHold t m => MonadHold t (EventWriterT t w m) where
85142 buildDynamic a0 = lift . buildDynamic a0
86143 {-# INLINABLE headE #-}
87144 headE = lift . headE
145+ {-# INLINABLE now #-}
146+ now = lift now
88147
89148instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
90149 runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
src/Reflex/FanTag.hsdeleted−23
@@ -1,23 +0,0 @@
1−{-# LANGUAGE PolyKinds #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−module Reflex.FanTag
4− ( EventSelectorTag
5− , unEventSelectorTag
6− , fanTag
7− , selectTag
8− ) where
9−
10−import Data.Unique.Tag.Local
11−import Data.TagMap
12−import Reflex.Class
13−
14−import GHC.Exts (Any)
15−import Unsafe.Coerce
16−
17−newtype EventSelectorTag t x (v :: k -> *) = EventSelectorTag { unEventSelectorTag :: EventSelectorInt t Any }
18−
19−fanTag :: Reflex t => Event t (TagMap x v) -> EventSelectorTag t x v
20−fanTag = EventSelectorTag . fanInt . fmapCheap unTagMap
21−
22−selectTag :: forall t x v a. Reflex t => EventSelectorTag t x v -> Tag x a -> Event t (v a)
23−selectTag (EventSelectorTag s) = fmapCheap (unsafeCoerce :: Any -> v a) . selectInt s . tagId
src/Reflex/Host/Headless.hsnew file+128
@@ -0,0 +1,128 @@
1+{-# LANGUAGE LambdaCase #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE RankNTypes #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE ConstraintKinds #-}
6+
7+module Reflex.Host.Headless where
8+
9+import Control.Concurrent.Chan (newChan, readChan)
10+import Control.Monad (unless)
11+import Control.Monad.Fix (MonadFix, fix)
12+import Control.Monad.IO.Class (MonadIO, liftIO)
13+import Control.Monad.Primitive (PrimMonad)
14+import Control.Monad.Ref (MonadRef, Ref, readRef)
15+import Data.Dependent.Sum (DSum (..), (==>))
16+import Data.Foldable (for_)
17+import Data.Functor.Identity (Identity(..))
18+import Data.IORef (IORef, readIORef)
19+import Data.Maybe (catMaybes, fromMaybe)
20+import Data.Traversable (for)
21+
22+import Reflex
23+import Reflex.Host.Class
24+
25+type MonadHeadlessApp t m =
26+ ( Adjustable t m
27+ , MonadFix m
28+ , MonadHold t m
29+ , MonadIO (HostFrame t)
30+ , MonadIO (Performable m)
31+ , MonadIO m
32+ , MonadRef (HostFrame t)
33+ , NotReady t m
34+ , PerformEvent t m
35+ , PostBuild t m
36+ , PrimMonad (HostFrame t)
37+ , Ref (HostFrame t) ~ IORef
38+ , Ref m ~ IORef
39+ , Reflex t
40+ , ReflexHost t
41+ , TriggerEvent t m
42+ )
43+
44+-- | Run a headless FRP network. Inside the action, you will most probably use
45+-- the capabilities provided by the 'TriggerEvent' and 'PerformEvent' type
46+-- classes to interface the FRP network with the outside world. Useful for
47+-- testing. Each headless network runs on its own spider timeline.
48+runHeadlessApp
49+ :: (forall t m. MonadHeadlessApp t m => m (Event t ()))
50+ -- ^ The action to be run in the headless FRP network. The FRP network is
51+ -- closed at the first occurrence of the resulting 'Event'.
52+ -> IO ()
53+runHeadlessApp guest =
54+ -- We are using the 'Spider' implementation of reflex. Running the host
55+ -- allows us to take actions on the FRP timeline.
56+ withSpiderTimeline $ runSpiderHostForTimeline $ do
57+ -- Create the "post-build" event and associated trigger. This event fires
58+ -- once, when the application starts.
59+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
60+ -- Create a queue to which we will write 'Event's that need to be
61+ -- processed.
62+ events <- liftIO newChan
63+ -- Run the "guest" application, providing the appropriate context. We'll
64+ -- pure the result of the action, and a 'FireCommand' that will be used to
65+ -- trigger events.
66+ (result, fc@(FireCommand fire)) <- do
67+ hostPerformEventT $ -- Allows the guest app to run
68+ -- 'performEvent', so that actions
69+ -- (e.g., IO actions) can be run when
70+ -- 'Event's fire.
71+
72+ flip runPostBuildT postBuild $ -- Allows the guest app to access to
73+ -- a "post-build" 'Event'
74+
75+ flip runTriggerEventT events $ -- Allows the guest app to create new
76+ -- events and triggers and write
77+ -- those triggers to a channel from
78+ -- which they will be read and
79+ -- processed.
80+ guest
81+
82+ -- Read the trigger reference for the post-build event. This will be
83+ -- 'Nothing' if the guest application hasn't subscribed to this event.
84+ mPostBuildTrigger <- readRef postBuildTriggerRef
85+
86+ -- Subscribe to an 'Event' of that the guest application can use to
87+ -- request application shutdown. We'll check whether this 'Event' is firing
88+ -- to determine whether to terminate.
89+ shutdown <- subscribeEvent result
90+
91+ -- When there is a subscriber to the post-build event, fire the event.
92+ soa <- for mPostBuildTrigger $ \postBuildTrigger ->
93+ fire [postBuildTrigger :=> Identity ()] $ isFiring shutdown
94+
95+ -- The main application loop. We wait for new events and fire those that
96+ -- have subscribers. If we detect a shutdown request, the application
97+ -- terminates.
98+ unless (or (fromMaybe [] soa)) $ fix $ \loop -> do
99+ -- Read the next event (blocking).
100+ ers <- liftIO $ readChan events
101+ stop <- do
102+ -- Fire events that have subscribers.
103+ fireEventTriggerRefs fc ers $
104+ -- Check if the shutdown 'Event' is firing.
105+ isFiring shutdown
106+ if or stop
107+ then pure ()
108+ else loop
109+ where
110+ isFiring ev = readEvent ev >>= \case
111+ Nothing -> pure False
112+ Just _ -> pure True
113+ -- Use the given 'FireCommand' to fire events that have subscribers
114+ -- and call the callback for the 'TriggerInvocation' of each.
115+ fireEventTriggerRefs
116+ :: MonadIO m
117+ => FireCommand t m
118+ -> [DSum (EventTriggerRef t) TriggerInvocation]
119+ -> ReadPhase m a
120+ -> m [a]
121+ fireEventTriggerRefs (FireCommand fire) ers rcb = do
122+ mes <- liftIO $
123+ for ers $ \(EventTriggerRef er :=> TriggerInvocation a _) -> do
124+ me <- readIORef er
125+ pure $ fmap (==> a) me
126+ a <- fire (catMaybes mes) rcb
127+ liftIO $ for_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
128+ pure a
src/Reflex/NotReady/Class.hs−5
@@ -25,7 +25,6 @@ import Reflex.PerformEvent.Base (PerformEventT (..))
2525import Reflex.PostBuild.Base (PostBuildT)
2626import Reflex.Query.Base (QueryT)
2727import Reflex.Requester.Base (RequesterT)
28−import Reflex.Requester.Base.Internal (RequesterInternalT)
2928import Reflex.TriggerEvent.Base (TriggerEventT)
3029
3130class Monad m => NotReady t m | m -> t where
@@ -73,10 +72,6 @@ instance NotReady t m => NotReady t (RequesterT t request response m) where
7372 notReadyUntil = lift . notReadyUntil
7473 notReady = lift notReady
7574
76−instance NotReady t m => NotReady t (RequesterInternalT s t request response m) where
77− notReadyUntil = lift . notReadyUntil
78− notReady = lift notReady
79−
8075instance NotReady t m => NotReady t (TriggerEventT t m) where
8176 notReadyUntil = lift . notReadyUntil
8277 notReady = lift notReady
src/Reflex/PerformEvent/Base.hs+36−78
@@ -28,10 +28,8 @@ import Reflex.Class
2828import Reflex.Adjustable.Class
2929import Reflex.Host.Class
3030import Reflex.PerformEvent.Class
31−import Reflex.Requester.Base.Internal
31+import Reflex.Requester.Base
3232import Reflex.Requester.Class
33−import Reflex.EventWriter.Class
34−import Reflex.EventWriter.Base
3533
3634import Control.Lens
3735import Control.Monad.Exception
@@ -39,21 +37,13 @@ import Control.Monad.Identity
3937import Control.Monad.Primitive
4038import Control.Monad.Reader
4139import Control.Monad.Ref
40+import Data.Coerce
4241import Data.Dependent.Map (DMap)
4342import qualified Data.Dependent.Map as DMap
4443import Data.Dependent.Sum
45−import Data.Functor.Compose
46−import Data.Functor.Misc
4744import Data.IntMap.Strict (IntMap)
4845import qualified Data.IntMap.Strict as IntMap
49−import Data.List.NonEmpty (NonEmpty (..))
50−import Data.List.NonEmpty.Deferred (NonEmptyDeferred)
51−import Data.Map (Map)
52−import qualified Data.Map as Map
53−import Data.Semigroup (Semigroup (sconcat))
5446import qualified Data.Semigroup as S
55−import Data.Unique.Tag.Local
56−import Data.Tuple
5747
5848-- | A function that fires events for the given 'EventTrigger's and then runs
5949-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
@@ -78,7 +68,7 @@ instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m
7868 type PrimState (PerformEventT t m) = PrimState (HostFrame t)
7969 primitive = PerformEventT . lift . primitive
8070
81−instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
71+instance (ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) where
8272 type Performable (PerformEventT t m) = HostFrame t
8373 {-# INLINABLE performEvent_ #-}
8474 performEvent_ = PerformEventT . requesting_
@@ -86,70 +76,37 @@ instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent
8676 performEvent = PerformEventT . requestingIdentity
8777
8878instance (ReflexHost t, PrimMonad (HostFrame t)) => Adjustable t (PerformEventT t m) where
89− {-# INLINE runWithReplace #-}
90− runWithReplace a0 a' = PerformEventT $ RequesterT $ do
91− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
92− let runA :: forall a. PerformEventT t m a -> HostFrame t (a, Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))
93− runA (PerformEventT (RequesterT a)) = runEventWriterT $ runReaderT (unRequesterInternalT a) env
94− (result0, requests0) <- lift $ runA a0
95− newA <- requestingIdentity $ runA <$> a'
96− requests <- switchHoldPromptOnly requests0 $ fmapCheap snd newA
97− RequesterInternalT $ tellEvent requests
98− pure (result0, fmapCheap fst newA)
99− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
100− traverseIntMapWithKeyWithAdjust f a0 a' = PerformEventT $ RequesterT $ do
101− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
102− let runA :: forall a. PerformEventT t m a -> HostFrame t (a, Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))
103− runA (PerformEventT (RequesterT a)) = runEventWriterT $ runReaderT (unRequesterInternalT a) env
104− children' <- requestingIdentity $ itraverse (\k -> runA . f k) <$> a'
105− children0 <- lift $ itraverse (\k -> runA . f k) a0
106− let results0 = fmap fst children0
107− requests0 = fmap snd children0
108− results' = fmap fst <$> children'
109− requests' = fmap snd `fmapCheap` children'
110− requests <- switchHoldPromptOnlyIncremental mergeIntIncremental coincidencePatchIntMap requests0 requests'
111− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatIntMapMaybe
112− pure (results0, results')
113− {-# INLINE traverseDMapWithKeyWithAdjust #-}
114− traverseDMapWithKeyWithAdjust (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
115− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
116− let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
117− runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
118− where (PerformEventT (RequesterT a)) = f k v
119− children' <- requestingIdentity $ traversePatchDMapWithKey runA <$> a'
120− children0 <- lift $ DMap.traverseWithKey runA a0
121− let results0 = DMap.map (snd . getCompose) children0
122− requests0 = weakenDMapWith (fst . getCompose) children0
123− results' = mapPatchDMap (snd . getCompose) <$> children'
124− requests' = weakenPatchDMapWith (fst . getCompose) `fmapCheap` children'
125− requests <- switchHoldPromptOnlyIncremental mergeMapIncremental coincidencePatchMap requests0 requests'
126− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
127− pure (results0, results')
128− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
129− traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
130− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
131− let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
132− runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
133− where (PerformEventT (RequesterT a)) = f k v
134− children' <- requestingIdentity $ traversePatchDMapWithMoveWithKey runA <$> a'
135− children0 <- lift $ DMap.traverseWithKey runA a0
136− let results0 = DMap.map (snd . getCompose) children0
137− requests0 = weakenDMapWith (fst . getCompose) children0
138− results' = mapPatchDMapWithMove (snd . getCompose) <$> children'
139− requests' = weakenPatchDMapWithMoveWith (fst . getCompose) `fmapCheap` children'
140− requests <- switchHoldPromptOnlyIncremental mergeMapIncrementalWithMove coincidencePatchMapWithMove requests0 requests'
141− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
142− pure (results0, results')
143−
144−concatIntMapMaybe :: Semigroup a => IntMap a -> Maybe a
145−concatIntMapMaybe m = case IntMap.elems m of
146− [] -> Nothing
147− h : t -> Just $ sconcat $ h :| t
148−
149−concatMapMaybe :: Semigroup a => Map k a -> Maybe a
150−concatMapMaybe m = case Map.elems m of
151− [] -> Nothing
152− h : t -> Just $ sconcat $ h :| t
79+ runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
80+ where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
81+ f a0 a' = do
82+ result0 <- lift a0
83+ result' <- requestingIdentity a'
84+ return (result0, result')
85+ traverseIntMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseIntMapWithKeyWithAdjustRequesterTWith (defaultAdjustIntBase traverseIntMapPatchWithKey) patchIntMapNewElementsMap mergeIntIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
86+ traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
87+ traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
88+
89+defaultAdjustBase :: forall t v v2 k' p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
90+ => ((forall a. k' a -> v a -> HostFrame t (v2 a)) -> p k' v -> HostFrame t (p k' v2))
91+ -> (forall a. k' a -> v a -> HostFrame t (v2 a))
92+ -> DMap k' v
93+ -> Event t (p k' v)
94+ -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' v2, Event t (p k' v2))
95+defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
96+ result0 <- lift $ DMap.traverseWithKey f' dm0
97+ result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
98+ return (result0, result')
99+
100+defaultAdjustIntBase :: forall t v v2 p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
101+ => ((IntMap.Key -> v -> HostFrame t v2) -> p v -> HostFrame t (p v2))
102+ -> (IntMap.Key -> v -> HostFrame t v2)
103+ -> IntMap v
104+ -> Event t (p v)
105+ -> RequesterT t (HostFrame t) Identity (HostFrame t) (IntMap v2, Event t (p v2))
106+defaultAdjustIntBase traversePatchWithKey f' dm0 dm' = do
107+ result0 <- lift $ IntMap.traverseWithKey f' dm0
108+ result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
109+ return (result0, result')
153110
154111instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
155112 {-# INLINABLE newEventWithTrigger #-}
@@ -167,7 +124,6 @@ hostPerformEventT :: forall t m a.
167124 , MonadReflexHost t m
168125 , MonadRef m
169126 , Ref m ~ Ref IO
170− , PrimMonad (HostFrame t)
171127 )
172128 => PerformEventT t m a
173129 -> m (a, FireCommand t m)
@@ -208,6 +164,8 @@ instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
208164 buildDynamic getV0 v' = PerformEventT $ lift $ buildDynamic getV0 v'
209165 {-# INLINABLE headE #-}
210166 headE = PerformEventT . lift . headE
167+ {-# INLINABLE now #-}
168+ now = PerformEventT . lift $ now
211169
212170instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
213171 type Ref (PerformEventT t m) = Ref (HostFrame t)
src/Reflex/PerformEvent/Class.hs−1
@@ -8,7 +8,6 @@
88{-# LANGUAGE RankNTypes #-}
99{-# LANGUAGE ScopedTypeVariables #-}
1010{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE TypeOperators #-}
1211{-# LANGUAGE UndecidableInstances #-}
1312#ifdef USE_REFLEX_OPTIMIZER
1413{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/PostBuild/Base.hs+2−1
@@ -9,7 +9,6 @@
99{-# LANGUAGE RecursiveDo #-}
1010{-# LANGUAGE ScopedTypeVariables #-}
1111{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
1312{-# LANGUAGE UndecidableInstances #-}
1413#ifdef USE_REFLEX_OPTIMIZER
1514{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
@@ -84,6 +83,8 @@ instance MonadHold t m => MonadHold t (PostBuildT t m) where
8483 buildDynamic a0 = lift . buildDynamic a0
8584 {-# INLINABLE headE #-}
8685 headE = lift . headE
86+ {-# INLINABLE now #-}
87+ now = lift now
8788
8889instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
8990 type Performable (PostBuildT t m) = Performable m
src/Reflex/PostBuild/Class.hs−1
@@ -6,7 +6,6 @@
66{-# LANGUAGE FunctionalDependencies #-}
77{-# LANGUAGE ScopedTypeVariables #-}
88{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE TypeOperators #-}
109{-# LANGUAGE UndecidableInstances #-}
1110#ifdef USE_REFLEX_OPTIMIZER
1211{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Profiled.hs+4−2
@@ -28,7 +28,8 @@ import Control.Monad.Ref
2828import Control.Monad.State.Strict (StateT, execStateT, modify)
2929import Data.Bifunctor
3030import Data.Coerce
31−import Data.Dependent.Map (DMap, GCompare)
31+import Data.Dependent.Map (DMap)
32+import Data.GADT.Compare (GCompare)
3233import Data.FastMutableIntMap
3334import Data.IORef
3435import Data.List
@@ -146,7 +147,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
146147 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
147148 pull = Behavior_Profiled . pull . coerce
148149 fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
149− mergeG :: forall (k :: z -> *) q v. GCompare k
150+ mergeG :: forall z (k :: z -> *) q v. GCompare k
150151 => (forall a. q a -> Event (ProfiledTimeline t) (v a))
151152 -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
152153 mergeG nt = Event_Profiled #. mergeG (coerce nt)
@@ -183,6 +184,7 @@ instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
183184 holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
184185 buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
185186 headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
187+ now = ProfiledM $ Event_Profiled <$> now
186188
187189instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
188190 sample (Behavior_Profiled b) = ProfiledM $ sample b
src/Reflex/Pure.hs+6−1
@@ -32,7 +32,8 @@ module Reflex.Pure
3232 ) where
3333
3434import Control.Monad
35−import Data.Dependent.Map (DMap, GCompare)
35+import Data.Dependent.Map (DMap)
36+import Data.GADT.Compare (GCompare)
3637import qualified Data.Dependent.Map as DMap
3738import Data.IntMap (IntMap)
3839import qualified Data.IntMap as IntMap
@@ -210,3 +211,7 @@ instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
210211 Just x -> fromMaybe lastValue $ apply x lastValue
211212
212213 headE = slowHeadE
214+ now t = Event $ guard . (t ==)
215+
216+
217+
src/Reflex/Query/Base.hs+9−3
@@ -22,16 +22,19 @@ module Reflex.Query.Base
2222import Control.Applicative (liftA2)
2323import Control.Monad.Exception
2424import Control.Monad.Fix
25+import Control.Monad.Morph
2526import Control.Monad.Primitive
2627import Control.Monad.Reader
2728import Control.Monad.Ref
2829import Control.Monad.State.Strict
2930import Data.Align
30−import Data.Dependent.Map (DMap, DSum (..))
31+import Data.Dependent.Map (DMap)
3132import qualified Data.Dependent.Map as DMap
33+import Data.Dependent.Sum (DSum(..))
3234import Data.Foldable
3335import Data.Functor.Compose
3436import Data.Functor.Misc
37+import Data.GADT.Compare (GCompare)
3538import Data.IntMap (IntMap)
3639import qualified Data.IntMap as IntMap
3740import Data.Map (Map)
@@ -135,7 +138,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
135138 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
136139 return (liftedResult0, liftedResult')
137140
138− traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
141+ traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
139142 traverseDMapWithKeyWithAdjust f dm0 dm' = do
140143 let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
141144 f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
@@ -180,7 +183,7 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
180183 tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
181184 return (liftedResult0, liftedResult')
182185
183− traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
186+ traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
184187 traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
185188 let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
186189 f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
@@ -237,6 +240,9 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
237240instance MonadTrans (QueryT t q) where
238241 lift = QueryT . lift . lift . lift
239242
243+instance MFunctor (QueryT t q) where
244+ hoist = mapQueryT
245+
240246instance PrimMonad m => PrimMonad (QueryT t q m) where
241247 type PrimState (QueryT t q m) = PrimState m
242248 primitive = lift . primitive
src/Reflex/Requester/Base.hs+597−13
@@ -1,23 +1,607 @@
11-- | This module provides 'RequesterT', the standard implementation of
22-- 'Requester'.
3+{-# LANGUAGE AllowAmbiguousTypes #-}
4+{-# LANGUAGE ApplicativeDo #-}
5+{-# LANGUAGE CPP #-}
6+{-# LANGUAGE EmptyDataDecls #-}
7+{-# LANGUAGE ExistentialQuantification #-}
8+{-# LANGUAGE FlexibleInstances #-}
9+{-# LANGUAGE GADTs #-}
10+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11+{-# LANGUAGE MultiParamTypeClasses #-}
12+{-# LANGUAGE Rank2Types #-}
13+{-# LANGUAGE RecursiveDo #-}
14+{-# LANGUAGE ScopedTypeVariables #-}
15+{-# LANGUAGE StandaloneDeriving #-}
16+{-# LANGUAGE TypeFamilies #-}
17+{-# LANGUAGE TypeOperators #-}
18+{-# LANGUAGE UndecidableInstances #-}
19+#ifdef USE_REFLEX_OPTIMIZER
20+{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21+#endif
322module Reflex.Requester.Base
423 ( RequesterT (..)
5− , RequestData (..)
6− , ResponseData (..)
7− , RequestEnvelope (..)
824 , runRequesterT
9−-- , withRequesterT
10−-- , RequesterData
11−-- , RequesterDataKey
25+ , withRequesterT
26+ , runWithReplaceRequesterTWith
27+ , traverseIntMapWithKeyWithAdjustRequesterTWith
28+ , traverseDMapWithKeyWithAdjustRequesterTWith
29+ , RequesterData
30+ , RequesterDataKey
1231 , traverseRequesterData
1332 , forRequesterData
14− , requestEnvelopesToDSums
15− , singletonRequestData
16− , singletonResponseData
33+ , requesterDataToList
34+ , singletonRequesterData
1735 , matchResponsesWithRequests
18−-- , multiEntry
19−-- , unMultiEntry
20−-- , requesting'
36+ , matchResponseMapWithRequests
37+ , multiEntry
38+ , unMultiEntry
39+ , requesting'
2140 ) where
2241
23−import Reflex.Requester.Base.Internal
42+import Reflex.Class
43+import Reflex.Adjustable.Class
44+import Reflex.Dynamic
45+import Reflex.Host.Class
46+import Reflex.PerformEvent.Class
47+import Reflex.PostBuild.Class
48+import Reflex.Requester.Class
49+import Reflex.TriggerEvent.Class
50+
51+import Control.Applicative (liftA2)
52+import Control.Monad.Exception
53+import Control.Monad.Identity
54+import Control.Monad.Morph
55+import Control.Monad.Primitive
56+import Control.Monad.Reader
57+import Control.Monad.Ref
58+import Control.Monad.State.Strict
59+import Data.Bits
60+import Data.Coerce
61+import Data.Constraint
62+import Data.Dependent.Map (DMap)
63+import qualified Data.Dependent.Map as DMap
64+import Data.Dependent.Sum (DSum (..))
65+import Data.Functor.Compose
66+import Data.Functor.Misc
67+import Data.IntMap.Strict (IntMap)
68+import qualified Data.IntMap.Strict as IntMap
69+import Data.Map (Map)
70+import qualified Data.Map as Map
71+import Data.Monoid ((<>))
72+import Data.Proxy
73+import qualified Data.Semigroup as S
74+import Data.Some (Some(Some))
75+import Data.Type.Equality
76+import Data.Unique.Tag
77+
78+import GHC.Exts (Any)
79+import Unsafe.Coerce
80+
81+--TODO: Make this module type-safe
82+
83+newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
84+
85+newtype RequesterData f = RequesterData (TagMap (Entry f))
86+
87+emptyRequesterData :: RequesterData f
88+emptyRequesterData = RequesterData $ TagMap IntMap.empty
89+
90+data RequesterDataKey a where
91+ RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
92+ RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead
93+ RequesterDataKey_Multi2 :: GCompare k => {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
94+ RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
95+
96+singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
97+singletonRequesterData rdk v = case rdk of
98+ RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
99+ RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
100+ RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Multi2Contents
101+ { _multi2Contents_values = Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
102+ , _multi2Contents_dict = Dict
103+ }
104+ RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
105+
106+mergeRequesterData :: RequesterData f -> RequesterData f -> RequesterData f
107+mergeRequesterData (RequesterData a) (RequesterData b) = RequesterData $ mergeTagMap a b
108+
109+mergeTagMap :: forall f. TagMap (Entry f) -> TagMap (Entry f) -> TagMap (Entry f)
110+mergeTagMap (TagMap m) (TagMap n) =
111+ TagMap $ IntMap.unionWithKey (g' combiner) m n
112+ where
113+ combiner :: forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a
114+ combiner k (Entry a) (Entry b) = Entry $ case myKeyType k of
115+ MyTagType_Single -> a
116+ MyTagType_Multi -> IntMap.unionWith mergeRequesterData a b
117+ MyTagType_Multi2 -> case _multi2Contents_dict a of
118+ Dict -> Multi2Contents
119+ { _multi2Contents_values = Map.unionWith (IntMap.unionWith mergeRequesterData) (_multi2Contents_values a) (_multi2Contents_values b)
120+ , _multi2Contents_dict = Dict
121+ }
122+ MyTagType_Multi3 -> IntMap.unionWith (IntMap.unionWith mergeRequesterData) a b
123+ g' :: (forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a) -> Int -> Any -> Any -> Any
124+ g' f rawKey a b =
125+ let k = MyTag rawKey :: MyTag a
126+ fromAny :: Any -> Entry f a
127+ fromAny = unsafeCoerce
128+ toAny :: Entry f a -> Any
129+ toAny = unsafeCoerce
130+ in toAny $ f k (fromAny a) (fromAny b)
131+
132+requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
133+requesterDataToList (RequesterData m) = do
134+ k :=> Entry e <- tagMapToList m
135+ case myKeyType k of
136+ MyTagType_Single -> return $ RequesterDataKey_Single k :=> e
137+ MyTagType_Multi -> do
138+ (k', e') <- IntMap.toList e
139+ k'' :=> e'' <- requesterDataToList e'
140+ return $ RequesterDataKey_Multi k k' k'' :=> e''
141+ MyTagType_Multi2 -> case _multi2Contents_dict e of
142+ Dict -> do
143+ (k', e') <- Map.toList $ _multi2Contents_values e
144+ (k'', e'') <- IntMap.toList e'
145+ k''' :=> e''' <- requesterDataToList e''
146+ return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
147+ MyTagType_Multi3 -> do
148+ (k', e') <- IntMap.toList e
149+ (k'', e'') <- IntMap.toList e'
150+ k''' :=> e''' <- requesterDataToList e''
151+ return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e'''
152+
153+singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f
154+singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v
155+
156+tagMapToList :: forall f. TagMap f -> [DSum MyTag f]
157+tagMapToList (TagMap m) = f <$> IntMap.toList m
158+ where f :: (Int, Any) -> DSum MyTag f
159+ f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v
160+
161+traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g)
162+traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m
163+ where
164+ g :: Int -> Any -> t Any
165+ g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v)
166+
167+-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
168+traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response)
169+traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order
170+ where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
171+ go k (Entry request) = Entry <$> case myKeyType k of
172+ MyTagType_Single -> f request
173+ MyTagType_Multi -> traverse (traverseRequesterData f) request
174+ MyTagType_Multi2 -> case request of
175+ Multi2Contents { _multi2Contents_values = request', _multi2Contents_dict = Dict } -> do
176+ v <- traverse (traverse (traverseRequesterData f)) request'
177+ pure $ Multi2Contents
178+ { _multi2Contents_values = v
179+ , _multi2Contents_dict = Dict
180+ }
181+ MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
182+
183+-- | 'traverseRequesterData' with its arguments flipped
184+forRequesterData :: forall request response m. Applicative m => RequesterData request -> (forall a. request a -> m (response a)) -> m (RequesterData response)
185+forRequesterData r f = traverseRequesterData f r
186+
187+data MyTagType :: * -> * where
188+ MyTagType_Single :: MyTagType (Single a)
189+ MyTagType_Multi :: MyTagType Multi
190+ MyTagType_Multi2 :: MyTagType (Multi2 k)
191+ MyTagType_Multi3 :: MyTagType Multi3
192+
193+myKeyType :: MyTag x -> MyTagType x
194+myKeyType (MyTag k) = case k .&. 0x3 of
195+ 0x0 -> unsafeCoerce MyTagType_Single
196+ 0x1 -> unsafeCoerce MyTagType_Multi
197+ 0x2 -> unsafeCoerce MyTagType_Multi2
198+ 0x3 -> unsafeCoerce MyTagType_Multi3
199+ t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
200+
201+data Single a
202+data Multi
203+data Multi2 (k :: * -> *)
204+data Multi3
205+
206+class MyTagTypeOffset x where
207+ myTagTypeOffset :: proxy x -> Int
208+
209+instance MyTagTypeOffset (Single a) where
210+ myTagTypeOffset _ = 0x0
211+
212+instance MyTagTypeOffset Multi where
213+ myTagTypeOffset _ = 0x1
214+
215+instance MyTagTypeOffset (Multi2 k) where
216+ myTagTypeOffset _ = 0x2
217+
218+instance MyTagTypeOffset Multi3 where
219+ myTagTypeOffset _ = 0x3
220+
221+type family EntryContents request a where
222+ EntryContents request (Single a) = request a
223+ EntryContents request Multi = IntMap (RequesterData request)
224+ EntryContents request (Multi2 k) = Multi2Contents k request
225+ EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
226+
227+data Multi2Contents k request = Multi2Contents
228+ { _multi2Contents_dict :: {-# UNPACK #-} !(Dict (GCompare k)) -- This is a Dict instead of an existential context because we only want to use it in certain circumstances
229+ , _multi2Contents_values :: {-# UNPACK #-} !(Map (Some k) (IntMap (RequesterData request)))
230+ }
231+
232+newtype Entry request x = Entry { unEntry :: EntryContents request x }
233+
234+{-# INLINE singleEntry #-}
235+singleEntry :: f a -> Entry f (Single a)
236+singleEntry = Entry
237+
238+{-# INLINE multiEntry #-}
239+multiEntry :: IntMap (RequesterData f) -> Entry f Multi
240+multiEntry = Entry
241+
242+{-# INLINE unMultiEntry #-}
243+unMultiEntry :: Entry f Multi -> IntMap (RequesterData f)
244+unMultiEntry = unEntry
245+
246+-- | We use a hack here to pretend we have x ~ request a; we don't want to use a GADT, because GADTs (even with zero-size existential contexts) can't be newtypes
247+-- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another
248+newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum)
249+
250+newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum)
251+
252+{-# INLINE castMyTagWrap #-}
253+castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
254+castMyTagWrap = coerce
255+
256+instance GEq MyTag where
257+ (MyTag a) `geq` (MyTag b) =
258+ if a == b
259+ then Just $ unsafeCoerce Refl
260+ else Nothing
261+
262+instance GCompare MyTag where
263+ (MyTag a) `gcompare` (MyTag b) =
264+ case a `compare` b of
265+ LT -> GLT
266+ EQ -> unsafeCoerce GEQ
267+ GT -> GGT
268+
269+instance GEq (MyTagWrap f) where
270+ (MyTagWrap a) `geq` (MyTagWrap b) =
271+ if a == b
272+ then Just $ unsafeCoerce Refl
273+ else Nothing
274+
275+instance GCompare (MyTagWrap f) where
276+ (MyTagWrap a) `gcompare` (MyTagWrap b) =
277+ case a `compare` b of
278+ LT -> GLT
279+ EQ -> unsafeCoerce GEQ
280+ GT -> GGT
281+
282+data RequesterState t (request :: * -> *) = RequesterState
283+ { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
284+ , _requesterState_requests :: ![(Int, Event t Any)]
285+ }
286+
287+-- | A basic implementation of 'Requester'.
288+newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a }
289+ deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
290+-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
291+#if MIN_VERSION_base(4,9,1)
292+ , MonadAsyncException
293+#endif
294+ )
295+
296+deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
297+deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
298+deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
299+deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
300+
301+instance PrimMonad m => PrimMonad (RequesterT t request response m) where
302+ type PrimState (RequesterT t request response m) = PrimState m
303+ primitive = lift . primitive
304+
305+-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
306+instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
307+ mempty = pure mempty
308+ mappend = liftA2 mappend
309+
310+instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
311+ (<>) = liftA2 (S.<>)
312+
313+
314+-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
315+-- requests are made, and responses should be provided in the input 'Event'.
316+-- The 'Tag' keys will be used to return the responses to the same place the
317+-- requests were issued.
318+runRequesterT :: (Reflex t, Monad m)
319+ => RequesterT t request response m a
320+ -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
321+ -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
322+runRequesterT (RequesterT a) responses = do
323+ (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $
324+ coerceEvent responses
325+ return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
326+
327+-- | Map a function over the request and response of a 'RequesterT'
328+withRequesterT
329+ :: (Reflex t, MonadFix m)
330+ => (forall x. req x -> req' x) -- ^ The function to map over the request
331+ -> (forall x. rsp' x -> rsp x) -- ^ The function to map over the response
332+ -> RequesterT t req rsp m a -- ^ The internal 'RequesterT' whose input and output will be transformed
333+ -> RequesterT t req' rsp' m a -- ^ The resulting 'RequesterT'
334+withRequesterT freq frsp child = do
335+ rec let rsp = fmap (runIdentity . traverseRequesterData (Identity . frsp)) rsp'
336+ (a, req) <- lift $ runRequesterT child rsp
337+ rsp' <- fmap (flip selectInt 0 . fanInt . fmapCheap unMultiEntry) $ requesting' $
338+ fmapCheap (multiEntry . IntMap.singleton 0) $ fmap (runIdentity . traverseRequesterData (Identity . freq)) req
339+ return a
340+
341+instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
342+ type Request (RequesterT t request response m) = request
343+ type Response (RequesterT t request response m) = response
344+ requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a)))
345+ requesting_ = void . tagRequest . fmapCheap singleEntry
346+
347+{-# INLINE tagRequest #-}
348+tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
349+tagRequest req = do
350+ old <- RequesterT get
351+ let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x)
352+ t = MyTagWrap n
353+ RequesterT $ put $ RequesterState
354+ { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4
355+ , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old
356+ }
357+ return t
358+
359+{-# INLINE responseFromTag #-}
360+responseFromTag :: forall m t request response x. Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
361+responseFromTag (MyTagWrap t) = do
362+ responses :: EventSelectorInt t Any <- RequesterT ask
363+ return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
364+
365+instance MonadTrans (RequesterT t request response) where
366+ lift = RequesterT . lift . lift
367+
368+instance MFunctor (RequesterT t request response) where
369+ hoist f = RequesterT . hoist (hoist f) . unRequesterT
370+
371+instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
372+ type Performable (RequesterT t request response m) = Performable m
373+ performEvent_ = lift . performEvent_
374+ performEvent = lift . performEvent
375+
376+instance MonadRef m => MonadRef (RequesterT t request response m) where
377+ type Ref (RequesterT t request response m) = Ref m
378+ newRef = lift . newRef
379+ readRef = lift . readRef
380+ writeRef r = lift . writeRef r
381+
382+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
383+ newEventWithTrigger = lift . newEventWithTrigger
384+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
385+
386+instance MonadReader r m => MonadReader r (RequesterT t request response m) where
387+ ask = lift ask
388+ local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
389+ reader = lift . reader
390+
391+instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where
392+ runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
393+ traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
394+ {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
395+ traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
396+ traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
397+
398+requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
399+requesting' = responseFromTag . castMyTagWrap <=< tagRequest
400+
401+{-# INLINABLE runWithReplaceRequesterTWith #-}
402+runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
403+ , MonadFix m
404+ )
405+ => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
406+ -> RequesterT t request response m a
407+ -> Event t (RequesterT t request response m b)
408+ -> RequesterT t request response m (a, Event t b)
409+runWithReplaceRequesterTWith f a0 a' = do
410+ rec na' <- numberOccurrencesFrom 1 a'
411+ responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here
412+ let responses' = fanInt responses
413+ ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na'
414+ requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v'
415+ return (result0, fmapCheap (fst . snd) v')
416+
417+{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-}
418+traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p.
419+ ( Reflex t
420+ , MonadHold t m
421+ , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request)))
422+ , Patch (p (Event t (IntMap (RequesterData request))))
423+ , Functor p
424+ , MonadFix m
425+ )
426+ => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v'))
427+ -> IntMap (IntMap.Key, v)
428+ -> Event t (p (IntMap.Key, v))
429+ -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v')))
430+ )
431+ -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request))))
432+ -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request))))
433+ -> (IntMap.Key -> v -> RequesterT t request response m v')
434+ -> IntMap v
435+ -> Event t (p v)
436+ -> RequesterT t request response m (IntMap v', Event t (p v'))
437+traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
438+ rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
439+ let responses :: EventSelectorInt t (IntMap (RequesterData response))
440+ responses = fanInt $ fmapCheap unpack response
441+ unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response))
442+ unpack = unEntry
443+ pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3
444+ pack = Entry
445+ f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
446+ f' k (n, v) = do
447+ (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?
448+ return (fmapCheap (IntMap.singleton n) myRequests, result)
449+ ndm' <- numberOccurrencesFrom 1 dm'
450+ (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
451+ let result0 = fmap snd children0
452+ result' = fforCheap children' $ fmap snd
453+ requests0 :: IntMap (Event t (IntMap (RequesterData request)))
454+ requests0 = fmap fst children0
455+ requests' :: Event t (p (Event t (IntMap (RequesterData request))))
456+ requests' = fforCheap children' $ fmap fst
457+ promptRequests :: Event t (IntMap (IntMap (RequesterData request)))
458+ promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
459+ requests <- holdIncremental requests0 requests'
460+ return (result0, result')
461+
462+{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-}
463+traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
464+ ( GCompare k
465+ , Reflex t
466+ , MonadHold t m
467+ , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request)))
468+ , Patch (p' (Some k) (Event t (IntMap (RequesterData request))))
469+ , MonadFix m
470+ )
471+ => (forall k' v1 v2. GCompare k'
472+ => (forall a. k' a -> v1 a -> m (v2 a))
473+ -> DMap k' v1
474+ -> Event t (p k' v1)
475+ -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
476+ )
477+ -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2)
478+ -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
479+ -> (forall v2. p' (Some k) v2 -> Map (Some k) v2)
480+ -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
481+ -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
482+ -> DMap k v
483+ -> Event t (p k v)
484+ -> RequesterT t request response m (DMap k v', Event t (p k v'))
485+traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
486+ rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
487+ let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
488+ responses = fanMap $ fmapCheap unpack response
489+ unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
490+ unpack = _multi2Contents_values . unEntry
491+ pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
492+ pack m = Entry $ Multi2Contents { _multi2Contents_values = m, _multi2Contents_dict = Dict }
493+ f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
494+ f' k (Compose (n, v)) = do
495+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
496+ return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
497+ ndm' <- numberOccurrencesFrom 1 dm'
498+ (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
499+ let result0 = DMap.map (snd . getCompose) children0
500+ result' = fforCheap children' $ mapPatch $ snd . getCompose
501+ requests0 :: Map (Some k) (Event t (IntMap (RequesterData request)))
502+ requests0 = weakenDMapWith (fst . getCompose) children0
503+ requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request))))
504+ requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
505+ promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request)))
506+ promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
507+ requests <- holdIncremental requests0 requests'
508+ return (result0, result')
509+
510+data Decoder rawResponse response =
511+ forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
512+
513+matchResponsesWithRequests
514+ :: forall t rawRequest rawResponse request response m.
515+ ( MonadFix m
516+ , MonadHold t m
517+ , Reflex t
518+ )
519+ => (forall a. request a -> (rawRequest, rawResponse -> response a))
520+ -- ^ Given a request (from 'Requester'), produces the wire format of the
521+ -- request and a function used to process the associated response
522+ -> Event t (RequesterData request)
523+ -- ^ The outgoing requests
524+ -> Event t (Int, rawResponse)
525+ -- ^ The incoming responses, tagged by an identifying key
526+ -> m ( Event t (Map Int rawRequest)
527+ , Event t (RequesterData response)
528+ )
529+ -- ^ A map of outgoing wire-format requests and an event of responses keyed
530+ -- by the 'RequesterData' key of the associated outgoing request
531+matchResponsesWithRequests f send recv = matchResponseMapWithRequests f send $ uncurry Map.singleton <$> recv
532+
533+-- | Matches incoming responses with previously-sent requests
534+-- and uses the provided request "decoder" function to process
535+-- incoming responses.
536+matchResponseMapWithRequests
537+ :: forall t rawRequest rawResponse request response m.
538+ ( MonadFix m
539+ , MonadHold t m
540+ , Reflex t
541+ )
542+ => (forall a. request a -> (rawRequest, rawResponse -> response a))
543+ -- ^ Given a request (from 'Requester'), produces the wire format of the
544+ -- request and a function used to process the associated response
545+ -> Event t (RequesterData request)
546+ -- ^ The outgoing requests
547+ -> Event t (Map Int rawResponse)
548+ -- ^ A map of incoming responses, tagged by an identifying key
549+ -> m ( Event t (Map Int rawRequest)
550+ , Event t (RequesterData response)
551+ )
552+ -- ^ A map of outgoing wire-format requests and an event of responses keyed
553+ -- by the 'RequesterData' key of the associated outgoing request
554+matchResponseMapWithRequests f send recv = do
555+ rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
556+ waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
557+ holdIncremental mempty $ leftmost
558+ [ fmap (\(_, outstanding, _) -> outstanding) outgoing
559+ , snd <$> incoming
560+ ]
561+ let outgoing = processOutgoing nextId send
562+ incoming = processIncoming waitingFor recv
563+ return (fmap (\(_, _, rawReqs) -> rawReqs) outgoing, fst <$> incoming)
564+ where
565+ -- Tags each outgoing request with an identifying integer key
566+ -- and returns the next available key, a map of response decoders
567+ -- for requests for which there are outstanding responses, and the
568+ -- raw requests to be sent out.
569+ processOutgoing
570+ :: Behavior t Int
571+ -- The next available key
572+ -> Event t (RequesterData request)
573+ -- The outgoing request
574+ -> Event t ( Int
575+ , PatchMap Int (Decoder rawResponse response)
576+ , Map Int rawRequest )
577+ -- The new next-available-key, a map of requests expecting responses, and the tagged raw requests
578+ processOutgoing nextId out = flip pushAlways out $ \dm -> do
579+ oldNextId <- sample nextId
580+ let (result, newNextId) = flip runState oldNextId $ forM (requesterDataToList dm) $ \(k :=> v) -> do
581+ n <- get
582+ put $ succ n
583+ let (rawReq, rspF) = f v
584+ return (n, rawReq, Decoder k rspF)
585+ patchWaitingFor = PatchMap $ Map.fromList $
586+ (\(n, _, dec) -> (n, Just dec)) <$> result
587+ toSend = Map.fromList $ (\(n, rawReq, _) -> (n, rawReq)) <$> result
588+ return (newNextId, patchWaitingFor, toSend)
589+ -- Looks up the each incoming raw response in a map of response
590+ -- decoders and returns the decoded response and a patch that can
591+ -- be used to clear the ID of the consumed response out of the queue
592+ -- of expected responses.
593+ processIncoming
594+ :: Incremental t (PatchMap Int (Decoder rawResponse response))
595+ -- A map of outstanding expected responses
596+ -> Event t (Map Int rawResponse)
597+ -- A incoming response paired with its identifying key
598+ -> Event t (RequesterData response, PatchMap Int v)
599+ -- The decoded response and a patch that clears the outstanding responses queue
600+ processIncoming waitingFor inc = flip push inc $ \rspMap -> do
601+ wf <- sample $ currentIncremental waitingFor
602+ let match rawRsp (Decoder k rspF) =
603+ let rsp = rspF rawRsp
604+ in singletonRequesterData k rsp
605+ matches = Map.intersectionWith match rspMap wf
606+ pure $ if Map.null matches then Nothing else Just
607+ (Map.foldl' mergeRequesterData emptyRequesterData matches, PatchMap $ Nothing <$ matches)
src/Reflex/Requester/Base/Internal.hsdeleted−309
@@ -1,309 +0,0 @@
1−{-# LANGUAGE AllowAmbiguousTypes #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE EmptyDataDecls #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE RecursiveDo #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE StandaloneDeriving #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−{-# LANGUAGE InstanceSigs #-}
17−#ifdef USE_REFLEX_OPTIMIZER
18−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19−#endif
20−module Reflex.Requester.Base.Internal where
21−
22−import Reflex.Class
23−import Reflex.Adjustable.Class
24−import Reflex.Host.Class
25−import Reflex.PerformEvent.Class
26−import Reflex.PostBuild.Class
27−import Reflex.Requester.Class
28−import Reflex.TriggerEvent.Class
29−import Reflex.EventWriter.Base
30−import Reflex.EventWriter.Class
31−
32−import Control.Applicative (liftA2)
33−import Control.Monad.Exception
34−import Control.Monad.Fail
35−import Control.Monad.Identity
36−import Control.Monad.Primitive
37−import Control.Monad.Reader
38−import Control.Monad.Ref
39−import Control.Monad.State
40−import Data.Coerce
41−import Data.Dependent.Map (DSum (..))
42−import qualified Data.List.NonEmpty as NonEmpty
43−import Data.List.NonEmpty.Deferred (NonEmptyDeferred)
44−import qualified Data.List.NonEmpty.Deferred as NonEmptyDeferred
45−import Data.Monoid ((<>))
46−import qualified Data.Semigroup as S
47−import Data.Some (Some(Some))
48−import Data.Type.Equality
49−import Data.TagMap (TagMap)
50−import qualified Data.TagMap as TagMap
51−import Reflex.FanTag
52−import Data.Unique.Tag.Local
53−import Data.GADT.Compare
54−import Data.Witherable
55−
56−import Unsafe.Coerce
57−
58−import Debug.Trace
59−
60−import Data.Map (Map)
61−import qualified Data.Map as Map
62−import qualified Data.IntMap as IntMap
63−import GHC.Exts (Any)
64−
65−data RequestData ps request = forall s. RequestData !(TagGen ps s) !(NonEmptyDeferred (RequestEnvelope s request))
66−
67−data RequestEnvelope s request = forall a. RequestEnvelope {-# UNPACK #-} !(Maybe (Tag s a)) !(request a)
68−
69−data ResponseData ps response = forall s. ResponseData !(TagGen ps s) !(TagMap s response)
70−
71−traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequestData (PrimState m) request -> m (ResponseData (PrimState m) response)
72−traverseRequesterData f (RequestData tg es) = ResponseData tg . TagMap.fromList <$> wither g (NonEmpty.toList $ NonEmptyDeferred.toNonEmpty es)
73− where g (RequestEnvelope mt req) = case mt of
74− Just t -> Just . (t :=>) <$> f req
75− Nothing -> Nothing <$ f req
76−
77−-- | 'traverseRequesterData' with its arguments flipped
78−forRequesterData :: forall request response m. Applicative m => RequestData (PrimState m) request -> (forall a. request a -> m (response a)) -> m (ResponseData (PrimState m) response)
79−forRequesterData r f = traverseRequesterData f r
80−
81−runRequesterT :: forall t request response m a
82− . ( Reflex t
83− , PrimMonad m
84− , MonadFix m
85− )
86− => RequesterT t request response m a
87− -> Event t (ResponseData (PrimState m) response)
88− -> m (a, Event t (RequestData (PrimState m) request))
89−runRequesterT (RequesterT a) wrappedResponses = withRequesterInternalT $ \requests -> do
90− (_, tg) <- RequesterInternalT ask
91− result <- a
92− let responses = fforMaybe wrappedResponses $ \(ResponseData tg' m) -> case tg `geq` tg' of
93− Nothing -> trace ("runRequesterT: bad TagGen: expected " <> show tg <> " but got " <> show tg') Nothing --TODO: Warn somehow
94− Just Refl -> Just m
95− pure (responses, (result, fmapCheap (RequestData tg) requests))
96−
97−instance MonadTrans (RequesterInternalT s t request response) where
98− lift = RequesterInternalT . lift . lift
99−
100−instance MonadTrans (RequesterT t request response) where
101− lift a = RequesterT $ RequesterInternalT $ lift $ lift a
102−
103−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
104−-- requests are made, and responses should be provided in the input 'Event'.
105−-- The 'Tag' keys will be used to return the responses to the same place the
106−-- requests were issued.
107−withRequesterInternalT
108− :: forall t m request response a.
109− ( Reflex t
110− , PrimMonad m
111− , MonadFix m
112− )
113− => (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom request)) -> RequesterInternalT FakeRequesterStatePhantom t request response m (Event t (TagMap FakeRequesterStatePhantom response), a))
114− -> m a
115−withRequesterInternalT f = do
116− stg <- newTagGen
117− let tg = case stg of
118− Some tg' -> (unsafeCoerce :: TagGen (PrimState m) s -> TagGen (PrimState m) FakeRequesterStatePhantom) tg'
119− rec let RequesterInternalT a = (unsafeCoerce :: RequesterInternalT s t request response m (Event t (TagMap FakeRequesterStatePhantom response), a) -> RequesterInternalT FakeRequesterStatePhantom t request response m (Event t (TagMap FakeRequesterStatePhantom response), a)) $ f requests
120− ((responses, result), requests) <- runEventWriterT $ runReaderT a (fanTag responses, tg)
121− pure result
122−
123−-- This is because using forall ruins inlining
124−data FakeRequesterStatePhantom
125−
126−newtype RequesterT t (request :: * -> *) (response :: * -> *) m a = RequesterT { unRequesterT :: RequesterInternalT FakeRequesterStatePhantom t request response m a }
127− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
128−
129−newtype RequesterInternalT s t request response m a = RequesterInternalT { unRequesterInternalT :: ReaderT (EventSelectorTag t s response, TagGen (PrimState m) s) (EventWriterT t (NonEmptyDeferred (RequestEnvelope s request)) m) a }
130− deriving
131− ( Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
132−#if MIN_VERSION_base(4,9,1)
133− , MonadAsyncException
134−#endif
135− )
136−
137−-- I don't think this can actually be supported without unsafeCoercing around the fact that the phantoms don't match up. In fact, implementations could probably supply `unmask` functions that would actually do the wrong thing.
138−-- #if MIN_VERSION_base(4,9,1)
139−-- instance MonadAsyncException m => MonadAsyncException (RequesterT t request response m) where
140−-- mask f = RequesterT $ mask $ \unmask -> unRequesterT $ f $ \x -> RequesterT $ unmask $ unRequesterT x
141−-- #endif
142−
143−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
144−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
145−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
146−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
147−
148−deriving instance MonadSample t m => MonadSample t (RequesterInternalT s t request response m)
149−deriving instance MonadHold t m => MonadHold t (RequesterInternalT s t request response m)
150−deriving instance PostBuild t m => PostBuild t (RequesterInternalT s t request response m)
151−deriving instance TriggerEvent t m => TriggerEvent t (RequesterInternalT s t request response m)
152−
153−instance PrimMonad m => PrimMonad (RequesterT t request response m) where
154− type PrimState (RequesterT t request response m) = PrimState m
155− primitive = lift . primitive
156−
157−-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
158−instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
159− mempty = pure mempty
160− mappend = liftA2 mappend
161−
162−instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
163− (<>) = liftA2 (S.<>)
164−
165−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
166− type Performable (RequesterT t request response m) = Performable m
167− performEvent_ = lift . performEvent_
168− performEvent = lift . performEvent
169−
170−instance MonadRef m => MonadRef (RequesterT t request response m) where
171− type Ref (RequesterT t request response m) = Ref m
172− newRef = lift . newRef
173− readRef = lift . readRef
174− writeRef r = lift . writeRef r
175−
176−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
177− newEventWithTrigger = lift . newEventWithTrigger
178− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
179−
180−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
181− ask = lift ask
182− local f (RequesterT a) = RequesterT $ RequesterInternalT $ mapReaderT (mapEventWriterT $ local f) $ unRequesterInternalT a
183− reader = lift . reader
184−
185−instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
186− type Request (RequesterT t request response m) = request
187− type Response (RequesterT t request response m) = response
188− requesting e = RequesterT $ requesting e
189− requesting_ e = RequesterT $ requesting_ e
190−
191−instance (Reflex t, PrimMonad m) => Requester t (RequesterInternalT s t request response m) where
192− type Request (RequesterInternalT s t request response m) = request
193− type Response (RequesterInternalT s t request response m) = response
194− requesting e = RequesterInternalT $ do
195− (s, tg) <- ask
196− t <- lift $ lift $ newTag tg
197− tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope (Just t)) e
198− pure $ selectTag s t
199− requesting_ e = RequesterInternalT $ do
200− tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope Nothing) e
201−
202−instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterInternalT s t request response m) where
203− {-# INLINE runWithReplace #-}
204− runWithReplace (RequesterInternalT a0) a' = RequesterInternalT $ runWithReplace a0 (coerceEvent a')
205− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
206− traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseIntMapWithKeyWithAdjust (coerce . f) dm0 dm'
207− {-# INLINE traverseDMapWithKeyWithAdjust #-}
208− traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
209− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
210− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjustWithMove (coerce . f) dm0 dm'
211−
212−instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterT t request response m) where
213− {-# INLINE runWithReplace #-}
214− runWithReplace (RequesterT a0) a' = RequesterT $ runWithReplace a0 (fmapCheap unRequesterT a')
215− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
216− traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseIntMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
217− {-# INLINE traverseDMapWithKeyWithAdjust #-}
218− traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
219− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
220− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) dm0 dm'
221−
222−
223−data Decoder rawResponse response =
224− forall s a. Decoder (Tag s a) (rawResponse -> response a)
225−
226−-- | Matches incoming responses with previously-sent requests
227−-- and uses the provided request "decoder" function to process
228−-- incoming responses.
229−matchResponsesWithRequests
230− :: forall t rawRequest rawResponse request response m.
231− ( MonadFix m
232− , MonadHold t m
233− , PrimMonad m
234− , Reflex t
235− )
236− => (forall a. request a -> (rawRequest, rawResponse -> response a))
237− -- ^ Given a request (from 'Requester'), produces the wire format of the
238− -- request and a function used to process the associated response
239− -> Event t (RequestData (PrimState m) request)
240− -- ^ The outgoing requests
241− -> Event t (Int, rawResponse)
242− -- ^ The incoming responses, tagged by an identifying key
243− -> m ( Event t (Map Int rawRequest)
244− , Event t (ResponseData (PrimState m) response)
245− )
246− -- ^ A map of outgoing wire-format requests and an event of responses keyed
247− -- by the 'RequesterData' key of the associated outgoing request
248−matchResponsesWithRequests f send recv = withTagGen $ \tagGen -> mdo
249− waitingFor <- holdIncremental mempty $
250− leftmost [ fst <$> outgoing, snd <$> incoming ]
251− let outgoing = processOutgoing send
252− incoming = processIncoming waitingFor tagGen recv
253− return (snd <$> outgoing, fst <$> incoming)
254− where
255−
256− -- Tags each outgoing request with an identifying integer key
257− -- and returns the next available key, a map of response decoders
258− -- for requests for which there are outstanding responses, and the
259− -- raw requests to be sent out.
260− processOutgoing
261− :: Event t (RequestData (PrimState m) request)
262− -- The outgoing request
263− -> Event t ( PatchMap Int (Decoder rawResponse response)
264− , Map Int rawRequest )
265− -- A map of requests expecting responses, and the tagged raw requests
266− processOutgoing eventOutgoingRequest = flip pushAlways eventOutgoingRequest $ \(RequestData _ requestEnvelopes) -> do
267− let results = ffor (requestEnvelopesToDSums requestEnvelopes) $ \(k :=> v) ->
268− let (rawRequest, responseDecoder) = f v
269− in (tagId k, rawRequest, Decoder k responseDecoder)
270− let patchWaitingFor = PatchMap $ Map.fromList $ (\(n, _, dec) -> (n, Just dec)) <$> results
271− let toSend = Map.fromList $ (\(n, rawRequest, _) -> (n, rawRequest)) <$> results
272− return (patchWaitingFor, toSend)
273−
274− -- Looks up the each incoming raw response in a map of response
275− -- decoders and returns the decoded response and a patch that can
276− -- be used to clear the ID of the consumed response out of the queue
277− -- of expected responses.
278− processIncoming
279− :: Incremental t (PatchMap Int (Decoder rawResponse response))
280− -- A map of outstanding expected responses
281− -> TagGen (PrimState m) s
282− -- A tag generator
283− -> Event t (Int, rawResponse)
284− -- A incoming response paired with its identifying key
285− -> Event t (ResponseData (PrimState m) response, PatchMap Int v)
286− -- The decoded response and a patch that clears the outstanding responses queue
287− processIncoming incWaitingFor tagGen inc = flip push inc $ \(n, rawRsp) -> do
288− waitingFor <- sample $ currentIncremental incWaitingFor
289− case Map.lookup n waitingFor of
290− Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
291− Just (Decoder tag responseDecoder) ->
292− return $ Just
293− ( singletonResponseData tagGen (unsafeTagFromId n) (responseDecoder rawRsp)
294− , PatchMap $ Map.singleton n Nothing
295− )
296−
297−requestEnvelopesToDSums :: forall s request. NonEmptyDeferred (RequestEnvelope s request) -> [DSum (Tag s) request]
298−requestEnvelopesToDSums requestEnvelopes = catMaybes $ f <$> NonEmptyDeferred.toList requestEnvelopes
299− where f :: RequestEnvelope s request -> Maybe (DSum (Tag s) request)
300− f (RequestEnvelope (Just tag) v) = Just (tag :=> v)
301− f (RequestEnvelope Nothing _) = Nothing
302−
303−singletonRequestData :: TagGen ps s -> Maybe (Tag s a) -> request a -> RequestData ps request
304−singletonRequestData tagGen maybeTag request =
305− RequestData tagGen $ NonEmptyDeferred.singleton $ RequestEnvelope maybeTag request
306−
307−singletonResponseData :: TagGen ps s -> Tag ps s -> response a -> ResponseData ps response
308−singletonResponseData tagGen tag response =
309− ResponseData tagGen $ TagMap.singletonTagMap tag response
src/Reflex/Requester/Class.hs+1−1
@@ -40,7 +40,7 @@ class (Reflex t, Monad m) => Requester t m | m -> t where
4040 requesting :: Event t (Request m a) -> m (Event t (Response m a))
4141 -- | Emit a request whenever the given 'Event' fires, and ignore all responses.
4242 requesting_ :: Event t (Request m a) -> m ()
43−--TODO: Make Requester polykinded, where Request m and Response m both take an argument of the same kind
43+
4444
4545instance Requester t m => Requester t (ReaderT r m) where
4646 type Request (ReaderT r m) = Request m
src/Reflex/Spider/Internal.hs+40−4
@@ -9,7 +9,6 @@
99{-# LANGUAGE GADTs #-}
1010{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1111{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiWayIf #-}
1312{-# LANGUAGE RankNTypes #-}
1413{-# LANGUAGE RoleAnnotations #-}
1514{-# LANGUAGE ScopedTypeVariables #-}
@@ -20,6 +19,7 @@
2019{-# LANGUAGE DeriveFunctor #-}
2120{-# LANGUAGE StandaloneDeriving #-}
2221{-# LANGUAGE InstanceSigs #-}
22+{-# LANGUAGE MultiWayIf #-}
2323
2424#ifdef USE_REFLEX_OPTIMIZER
2525{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
@@ -46,8 +46,9 @@ import Control.Monad.Fail (MonadFail)
4646import qualified Control.Monad.Fail as MonadFail
4747import Data.Align
4848import Data.Coerce
49−import Data.Dependent.Map (DMap, DSum (..))
49+import Data.Dependent.Map (DMap)
5050import qualified Data.Dependent.Map as DMap
51+import Data.Dependent.Sum (DSum (..))
5152import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..))
5253import qualified Data.FastMutableIntMap as FastMutableIntMap
5354import Data.Foldable hiding (concat, elem, sequence_)
@@ -306,7 +307,20 @@ data CacheSubscribed x a
306307#endif
307308 }
308309
309−
310+nowSpiderEventM :: (HasSpiderTimeline x) => EventM x (R.Event (SpiderTimeline x) ())
311+nowSpiderEventM =
312+ SpiderEvent <$> now
313+
314+now :: (MonadIO m, Defer (Some Clear) m, HasSpiderTimeline x
315+ ) => m (Event x ())
316+now = do
317+ nowOrNot <- liftIO $ newIORef $ Just ()
318+ scheduleClear nowOrNot
319+ return . Event $ \_ -> do
320+ occ <- liftIO . readIORef $ nowOrNot
321+ return ( EventSubscription (return ()) eventSubscribedNow
322+ , occ
323+ )
310324
311325-- | Construct an 'Event' whose value is guaranteed not to be recomputed
312326-- repeatedly
@@ -593,6 +607,16 @@ eventSubscribedNever = EventSubscribed
593607 , eventSubscribedWhoCreated = return ["never"]
594608#endif
595609 }
610+eventSubscribedNow :: EventSubscribed x
611+eventSubscribedNow = EventSubscribed
612+ { eventSubscribedHeightRef = zeroRef
613+ , eventSubscribedRetained = toAny ()
614+#ifdef DEBUG_CYCLES
615+ , eventSubscribedGetParents = return []
616+ , eventSubscribedHasOwnHeightRef = False
617+ , eventSubscribedWhoCreated = return ["now"]
618+#endif
619+ }
596620
597621eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
598622eventSubscribedFan !subscribed = EventSubscribed
@@ -2496,6 +2520,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Event
24962520 {-# INLINABLE headE #-}
24972521 headE = R.slowHeadE
24982522-- headE (SpiderEvent e) = SpiderEvent <$> Reflex.Spider.Internal.headE e
2523+ {-# INLINABLE now #-}
2524+ now = nowSpiderEventM
24992525
25002526instance Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25012527 {-# INLINABLE sample #-}
@@ -2517,6 +2543,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25172543 {-# INLINABLE headE #-}
25182544 headE = R.slowHeadE
25192545-- headE (SpiderEvent e) = SpiderPushM $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2546+ {-# INLINABLE now #-}
2547+ now = SpiderPushM nowSpiderEventM
2548+
25202549
25212550instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
25222551 {-# INLINE return #-}
@@ -2580,6 +2609,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25802609 buildDynamic getV0 e = runFrame . runSpiderHostFrame $ Reflex.Class.buildDynamic getV0 e
25812610 {-# INLINABLE headE #-}
25822611 headE e = runFrame . runSpiderHostFrame $ Reflex.Class.headE e
2612+ {-# INLINABLE now #-}
2613+ now = runFrame . runSpiderHostFrame $ Reflex.Class.now
2614+
25832615
25842616instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
25852617 sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
@@ -2596,6 +2628,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25962628 {-# INLINABLE headE #-}
25972629 headE = R.slowHeadE
25982630-- headE (SpiderEvent e) = SpiderHostFrame $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2631+ {-# INLINABLE now #-}
2632+ now = SpiderHostFrame Reflex.Class.now
25992633
26002634instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
26012635 {-# INLINABLE sample #-}
@@ -2616,6 +2650,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Refle
26162650 buildDynamic getV0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.buildDynamic getV0 e
26172651 {-# INLINABLE headE #-}
26182652 headE e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.headE e
2653+ {-# INLINABLE now #-}
2654+ now = Reflex.Spider.Internal.ReadPhase Reflex.Class.now
26192655
26202656--------------------------------------------------------------------------------
26212657-- Deprecated items
@@ -2739,7 +2775,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
27392775 fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
27402776 {-# INLINABLE mergeG #-}
27412777 mergeG
2742− :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2778+ :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k
27432779 => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
27442780 -> DMap k q
27452781 -> R.Event (SpiderTimeline x) (DMap k v)
src/Reflex/TriggerEvent/Base.hs+2
@@ -122,6 +122,8 @@ instance MonadHold t m => MonadHold t (TriggerEventT t m) where
122122 buildDynamic a0 = lift . buildDynamic a0
123123 {-# INLINABLE headE #-}
124124 headE = lift . headE
125+ {-# INLINABLE now #-}
126+ now = lift now
125127
126128instance Adjustable t m => Adjustable t (TriggerEventT t m) where
127129 {-# INLINABLE runWithReplace #-}
test/Adjustable.hs+2−3
@@ -22,7 +22,7 @@ main = do
2222 -- If the final counter value in the adjusted widgets corresponds to the number of times it has
2323 -- been incremented, we know that the networks haven't broken.
2424 let expectedCount = length $ ffilter (== Increment) actions
25− let !True = last (last os) == [expectedCount,expectedCount]
25+ -- let !True = last (last os) == [expectedCount,expectedCount] -- TODO re-enable this test after issue #369 has been resolved
2626 return ()
2727
2828data PatchMapTestAction
@@ -59,5 +59,4 @@ testPatchMapWithMove pulse = do
5959 (Map.fromList $ zip [0..] "ab")
6060 (fmapMaybe id pulseAction)
6161 return ()
62− return result
63−
62+ return result
test/EventWriterT.hs+11−15
@@ -24,30 +24,26 @@ import Test.Run
2424
2525main :: IO ()
2626main = do
27− os1 <- runApp' (unwrapApp testOrdering) $
27+ os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
2828 [ Just ()
2929 ]
3030 print os1
31− os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
32− [ This ()
33− , That ()
34− , This ()
35− , These () ()
36− ]
31+ os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
32+ <- runApp' (unwrapApp testSimultaneous) $ map Just $
33+ [ This ()
34+ , That ()
35+ , This ()
36+ , These () ()
37+ ]
3738 print os2
38− os3 <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
39+ os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
3940 print os3
40− os4 <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
41+ os4@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
4142 print os4
42− os5 <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
43+ os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
4344 print os5
4445 os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
4546 print os6
46− let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
47− let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2
48− let ![[Nothing, Just [2]]] = os3
49− let ![[Nothing, Just [2]]] = os4
50− let ![[Nothing, Just [1, 2]]] = os5
5147 let ![[Nothing, Nothing]] = os6
5248 return ()
5349
test/GC.hs+1
@@ -18,6 +18,7 @@ import Data.GADT.Compare
1818import Data.IORef
1919import Data.Semigroup
2020import Data.These
21+import Data.Type.Equality ((:~:)(Refl))
2122
2223import Data.Functor.Misc
2324import Data.Patch
test/Headless.hsnew file+10
@@ -0,0 +1,10 @@
1+module Main where
2+
3+import Reflex
4+import Reflex.Host.Headless (runHeadlessApp)
5+
6+main :: IO ()
7+main = do
8+ runHeadlessApp $ do
9+ pb <- getPostBuild
10+ performEvent (pure <$> pb)
test/Reflex/Plan/Pure.hs+1
@@ -41,6 +41,7 @@ instance MonadHold (Pure Int) PurePlan where
4141 holdIncremental initial = liftPlan . holdIncremental initial
4242 buildDynamic getInitial = liftPlan . buildDynamic getInitial
4343 headE = liftPlan . headE
44+ now = liftPlan now
4445
4546instance MonadSample (Pure Int) PurePlan where
4647 sample = liftPlan . sample
test/Reflex/Test/CrossImpl.hs+1−1
@@ -26,7 +26,7 @@ import qualified Reflex.Profiled as Prof
2626import Control.Arrow (second, (&&&))
2727import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
2828import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29−import Data.Dependent.Map (DSum (..))
29+import Data.Dependent.Sum (DSum (..))
3030import Data.Foldable
3131import Data.Map.Strict (Map)
3232import qualified Data.Map.Strict as Map
test/Reflex/Test/Micro.hs+8−2
@@ -322,8 +322,14 @@ testCases =
322322 let e = never <$ switch b
323323 return $ void e
324324 lazyHold
325−
326−
325+ , testE "now-1" $ do
326+ e1 <- events1
327+ switchHoldPromptly never . pushAlways (\a -> fmap (a <$) now) $ e1
328+ , testE "now-2" $ do
329+ e1 <- events1
330+ let e = pushAlways (\a -> if a == "a" then now else return never) e1
331+ x <- accumDyn (<>) never e
332+ return . coincidence $ updated x
327333 ] where
328334
329335 events1, events2, events3 :: TestPlan t m => m (Event t String)
test/RequesterT.hs+20−61
@@ -5,7 +5,6 @@
55{-# LANGUAGE GADTs #-}
66{-# LANGUAGE LambdaCase #-}
77{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
98{-# LANGUAGE RecursiveDo #-}
109{-# LANGUAGE ScopedTypeVariables #-}
1110{-# LANGUAGE TemplateHaskell #-}
@@ -15,10 +14,8 @@ module Main where
1514
1615import Control.Lens hiding (has)
1716import Control.Monad
18−import Control.Monad.Fail (MonadFail)
1917import Control.Monad.Fix
2018import Control.Monad.IO.Class (MonadIO)
21−import Control.Monad.Primitive
2219import Data.Constraint.Extras
2320import Data.Constraint.Extras.TH
2421import Data.Constraint.Forall
@@ -33,7 +30,6 @@ import Data.Semigroup ((<>))
3330#endif
3431import Data.Text (Text)
3532import Data.These
36−import Data.List.NonEmpty.Deferred
3733import Text.Read (readMaybe)
3834
3935#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
@@ -45,8 +41,6 @@ import Reflex.Requester.Base
4541import Reflex.Requester.Class
4642import Test.Run
4743
48−import Debug.Trace hiding (traceEvent)
49−
5044data RequestInt a where
5145 RequestInt :: Int -> RequestInt Int
5246
@@ -55,7 +49,7 @@ main = do
5549 os1 <- runApp' (unwrapApp testOrdering) $
5650 [ Just ()
5751 ]
58− --print os1
52+ print os1
5953 os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
6054 [ This ()
6155 , That ()
@@ -71,43 +65,31 @@ main = do
7165 print os5
7266 os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
7367 print os6
74− os7 <- runApp' testMatchRequestsWithResponses $ map Just [ TestRequest_Increment 1, TestRequest_Increment 2 ]
68+ os7 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Increment 1 ]
7569 print os7
76− os8 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "abcd" ]
70+ os8 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "yoyo" ]
7771 print os8
78− os9 <- runApp' testMoribundPerformEvent $ map Just [ 1 .. 3 ]
79− print os9
80− os10 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Increment 1 ]
81− print os10
82− os11 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "yoyo" ]
83− print os11
84− let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
85− let ![[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]] = os2
72+ let ![[Just [1,2,3,4,5,6,7,8,9,10]]] = os1 -- The order is reversed here: see the documentation for 'runRequesterT'
73+ let ![[Just [9,7,5,3,1]],[Nothing,Nothing],[Just [10,8,6,4,2]],[Just [10,8,6,4,2],Nothing]] = os2
8674 let ![[Nothing, Just [2]]] = os3
8775 let ![[Nothing, Just [2]]] = os4
8876 let ![[Nothing, Just [1, 2]]] = os5
8977 -- let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
90− let !(Just [(-9223372036854775808,"2")]) = M.toList <$> head (head os7)
91− let !(Just [(-9223372036854775808,"dcba")]) = M.toList <$> head (head os8)
92− let ![[Nothing,Just "0:1"],[Nothing,Just "1:2"],[Nothing,Just "2:3"]] = os9
93− let !(Just [(-9223372036854775808,"2")]) = M.toList <$> head (head os10)
94− let !(Just [(-9223372036854775808,"oyoy")]) = M.toList <$> head (head os11)
78+ let !(Just [(1,"2")]) = M.toList <$> head (head os7)
79+ let !(Just [(1,"oyoy")]) = M.toList <$> head (head os8)
9580
9681 return ()
9782
98−unwrapApp :: forall t m a.
99− ( Reflex t
100− , MonadFix m
101− , PrimMonad m
102− )
83+unwrapRequest :: DSum tag RequestInt -> Int
84+unwrapRequest (_ :=> RequestInt i) = i
85+
86+unwrapApp :: ( Reflex t, Monad m )
10387 => (a -> RequesterT t RequestInt Identity m ())
10488 -> a
10589 -> m (Event t [Int])
10690unwrapApp x appIn = do
10791 ((), e) <- runRequesterT (x appIn) never
108− let unwrapRequests :: forall x. RequestData (PrimState m) RequestInt -> [Int]
109− unwrapRequests (RequestData _ es) = fmap (\(RequestEnvelope _ (RequestInt i)) -> i) $ toList es
110− return $ fmap unwrapRequests e
92+ return $ fmap (map unwrapRequest . requesterDataToList) e
11193
11294testOrdering :: ( Response m ~ Identity
11395 , Request m ~ RequestInt
@@ -218,11 +200,6 @@ data TestRequest a where
218200 TestRequest_Reverse :: String -> TestRequest String
219201 TestRequest_Increment :: Int -> TestRequest Int
220202
221−instance Show (TestRequest a) where
222− show = \case
223− TestRequest_Reverse str -> "reverse " <> str
224− TestRequest_Increment i -> "increment " <> show i
225−
226203testMatchRequestsWithResponses
227204 :: forall m t req a
228205 . ( MonadFix m
@@ -232,21 +209,17 @@ testMatchRequestsWithResponses
232209 , MonadIO (Performable m)
233210 , ForallF Show req
234211 , Has Read req
235− , PrimMonad m
236− , Show (req a)
237− , Show a
238− , MonadIO m
239212 )
240213 => Event t (req a) -> m (Event t (Map Int String))
241214testMatchRequestsWithResponses pulse = mdo
242215 (_, requests) <- runRequesterT (requesting pulse) responses
243− let rawResponseMap = M.map (\v ->
216+ let rawResponses = M.map (\v ->
244217 case words v of
245218 ["reverse", str] -> reverse str
246219 ["increment", i] -> show $ succ $ (read i :: Int)
247220 ) <$> rawRequestMap
248− (rawRequestMap, responses) <- matchResponsesWithRequests reqEncoder requests (head . M.toList <$> rawResponseMap)
249− pure rawResponseMap
221+ (rawRequestMap, responses) <- matchResponsesWithRequests reqEncoder requests (head . M.toList <$> rawResponses)
222+ pure rawResponses
250223 where
251224 reqEncoder :: forall a. req a -> (String, String -> Maybe a)
252225 reqEncoder r =
@@ -254,23 +227,9 @@ testMatchRequestsWithResponses pulse = mdo
254227 , \x -> has @Read r $ readMaybe x
255228 )
256229
257−-- If a widget is destroyed, and simultaneously it tries to use performEvent, the event does not get performed.
258−-- TODO Determine whether this is actually the behavior we want.
259−testMoribundPerformEvent
260− :: forall t m
261− . ( Adjustable t m
262− , PerformEvent t m
263− , MonadHold t m
264− , Reflex t
265− )
266− => Event t Int -> m (Event t String)
267−testMoribundPerformEvent pulse = do
268− (outputInitial, outputReplaced) <- runWithReplace (performPrint 0 pulse) $ ffor pulse $ \i -> performPrint i pulse
269− switchHold outputInitial outputReplaced
270− where
271− performPrint i evt =
272− performEvent $ ffor evt $ \output ->
273− return $ show i <> ":" <> show output
274−
275−
276230deriveArgDict ''TestRequest
231+
232+instance Show (TestRequest a) where
233+ show = \case
234+ TestRequest_Reverse str -> "reverse " <> str
235+ TestRequest_Increment i -> "increment " <> show i
test/hlint.hs+2−1
@@ -1,7 +1,7 @@
11module Main where
22
33import Control.Monad
4−import Language.Haskell.HLint3 (hlint)
4+import Language.Haskell.HLint (hlint)
55import System.Directory
66import System.Exit (exitFailure, exitSuccess)
77import System.FilePath
@@ -22,6 +22,7 @@ main = do
2222 , "--ignore=Use unless"
2323 , "--ignore=Reduce duplication"
2424 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--cpp-define=DEBUG"
2526 , "--ignore=Use tuple-section"
2627 ]
2728 recurseInto = and <$> sequence