on develop help sign in
48 files changed+1330−1112
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+41 .travis.yml−177 ChangeLog.md+58−20 Quickref.md+1 README.md+15−17 cabal.project.freeze−2 reflex.cabal+47−35 release.nix+2−2 src/Data/List/Deferred.hs−60 src/Data/List/NonEmpty/Deferred.hs−13 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−59 src/Reflex/Adjustable/Class.hs+2−1 src/Reflex/BehaviorWriter/Base.hs+16−7 src/Reflex/BehaviorWriter/Class.hs+11−5 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/FastWeak.hs+10 src/Reflex/Host/Headless.hs+128 src/Reflex/NotReady/Class.hs−5 src/Reflex/PerformEvent/Base.hs+36−82 src/Reflex/PerformEvent/Class.hs+6−1 src/Reflex/PostBuild/Base.hs+2−1 src/Reflex/PostBuild/Class.hs−1 src/Reflex/Profiled.hs+59−4 src/Reflex/Pure.hs+6−1 src/Reflex/Query/Base.hs+9−3 src/Reflex/Query/Class.hs+5−5 src/Reflex/Requester/Base.hs+597−13 src/Reflex/Requester/Base/Internal.hs−310 src/Reflex/Requester/Class.hs+1−1 src/Reflex/Spider/Internal.hs+40−14 src/Reflex/TriggerEvent/Base.hs+2 src/Reflex/TriggerEvent/Class.hs+6 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+13−8 test/Reflex/Test/Micro.hs+8−2 test/RequesterT.hs+27−56 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+58−20
@@ -1,6 +1,63 @@
11# Revision history for reflex
22
3−## Unreleased
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+
43+## 0.7.0.0
44+
45+* Add lifting instances for most classes to `Reflex.Profiled.Profiled`. ([#398](https://github.com/reflex-frp/reflex/pull/398))
46+* Class `MonadQuery t q m` now has a `Monad m` superclass constraint. ([#400](https://github.com/reflex-frp/reflex/pull/400))
47+* **(Breaking change)** Rename class `MonadBehaviorWriter` -> `BehaviorWriter` for consistency with `EventWriter`/`DynamicWriter`. ([#401](https://github.com/reflex-frp/reflex/pull/401))
48+* Introduce deprecated alias `MonadBehaviorWriter = BehaviorWriter`. ([#401](https://github.com/reflex-frp/reflex/pull/401))
49+* Fix bug in spider where event subscriptions would be prematurely finalized due to over-aggressive inlining. ([#409](https://github.com/reflex-frp/reflex/pull/409))
50+* Add instances of `PerformEvent` and `TriggerEvent` for `MaybeT`. ([#395](https://github.com/reflex-frp/reflex/pull/395))
51+
52+## 0.6.4.1
53+
54+* Fix a bug in the Reflex Profiled transformer where
55+ `Reflex.Class.mergeIncrementalG` and
56+ `Reflex.Class.mergeIncrementalWithMoveG` implementations referenced
57+ itself instead of the inner transformed timeline, causing an
58+ infinite loop.
59+
60+## 0.6.4
461
562* Support GHC 8.8
663
@@ -14,25 +71,6 @@
1471* Add cabal flags `debug-propagation` and `debug-event-cycles` to build in debugging
1572 code for performance and for cyclic dependencies between events
1673
17−* Refactor of `Reflex.Requester`:
18− * Updated:
19− * `RequesterData` to `RequestData`, `RequesterEnvelope`, and `ResponseData`
20− * `singletonRequesterData` to `singletonRequestData` and `singletonResponseData`
21− * `requesterDataToList` to `requestEnvelopesToDSums`
22− * Removed:
23− * `RequesterDataKey`
24− * `multiEntry`
25− * `unMultiEntry`
26− * `withRequesterT`
27− * `runWithReplaceRequesterTWith`
28− * `requesting'`
29− * `traverseIntMapWithKeyWithAdjustRequesterTWith`
30− * `traverseDMapWithKeyWithAdjustRequesterTWith`
31−
32−* Added `Data.List.Deferred` and `Data.List.NonEmpty.Deferred` for optimizing `<>` operations.
33−
34−* Added `Data.TagMap`, `Reflex.FanTag`, and `Data.Unique.Tag.Local` to improve request and response tagging.
35−
3674## 0.6.3
3775
3876* `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−17
@@ -1,25 +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−Use the `./scripts/hack-on reflex` script in [Reflex Platform](https://github.com/reflex-frp/reflex-platform) to checkout the source code of `reflex` locally in `reflex-platform/reflex` directory.
25−Then do modifications to the source in place, and use the `./try-reflex` or `./scripts/work-on` scripts to create the shell to test your changes.
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+47−35
@@ -1,7 +1,17 @@
11Name: reflex
2−Version: 0.6.3
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
@@ -318,17 +324,17 @@ test-suite RequesterT
318324 main-is: RequesterT.hs
319325 hs-source-dirs: test
320326 build-depends: base
321− , containers
322327 , constraints
323328 , constraints-extras
329+ , containers
324330 , deepseq
325331 , dependent-map
326332 , dependent-sum
327333 , lens
328334 , mtl
329− , primitive
330335 , ref-tf
331336 , reflex
337+ , text
332338 , these
333339 , transformers
334340
@@ -340,6 +346,14 @@ test-suite RequesterT
340346 Reflex.Plan.Pure
341347 Test.Run
342348
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+
343357test-suite Adjustable
344358 default-language: Haskell2010
345359 type: exitcode-stdio-1.0
@@ -433,7 +447,6 @@ benchmark spider-bench
433447 hs-source-dirs: bench test
434448 main-is: Main.hs
435449 ghc-options: -Wall -O2 -rtsopts
436− ghc-prof-options: -fprof-auto-calls
437450 build-depends:
438451 base,
439452 containers,
@@ -460,7 +473,6 @@ benchmark saulzar-bench
460473 c-sources: bench-cbits/checkCapability.c
461474 main-is: RunAll.hs
462475 ghc-options: -Wall -O2 -rtsopts -threaded
463− ghc-prof-options: -fprof-auto-calls
464476 build-depends:
465477 base,
466478 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−13
@@ -1,13 +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−{-# LANGUAGE LambdaCase #-}
6−module Data.List.NonEmpty.Deferred
7− ( NonEmptyDeferred
8− , singleton
9− , toNonEmpty
10− , toList
11− ) where
12−
13−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−59
@@ -1,59 +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 EmptyDataDecls #-}
6−{-# LANGUAGE DeriveFunctor #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE PolyKinds #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE MagicHash #-}
13−module Data.Unique.Tag.Local.Internal where
14−
15−import Control.Monad.Primitive
16−import Data.Primitive.MutVar
17−import Data.GADT.Compare
18−import Data.Some
19−import GHC.Exts (Int (..), Int#, MutVar#, unsafeCoerce#)
20−
21−import Unsafe.Coerce
22−
23−-- `x` is which generator it's from
24−-- `a` is the type of the thing it's tagging
25−newtype Tag x a = Tag Int
26−
27−tagId :: Tag x a -> Int
28−tagId (Tag n) = n
29−
30−-- | WARNING: If you construct a tag with the wrong type, it will result in
31−-- incorrect unsafeCoerce applications, which can segfault or cause arbitrary
32−-- other damage to your program
33−unsafeTagFromId :: Int -> Tag x a
34−unsafeTagFromId n = Tag n
35−
36−-- We use Int because it is supported by e.g. IntMap
37−newtype TagGen ps s = TagGen { unTagGen :: MutVar ps Int }
38−
39−instance Show (TagGen ps s) where
40− show (TagGen (MutVar m)) = show $ I# ((unsafeCoerce# :: MutVar# ps Int -> Int#) m)
41−
42−instance GEq (TagGen ps) where
43− TagGen a `geq` TagGen b =
44− if a == b
45− then Just $ unsafeCoerce Refl
46− else Nothing
47−
48−newTag :: PrimMonad m => TagGen (PrimState m) s -> m (Tag s a)
49−newTag (TagGen r) = do
50− n <- atomicModifyMutVar' r $ \x -> (succ x, x)
51− pure $ Tag n
52−
53−newTagGen :: PrimMonad m => m (Some (TagGen (PrimState m)))
54−newTagGen = Some . TagGen <$> newMutVar minBound
55−
56−withTagGen :: PrimMonad m => (forall s. TagGen (PrimState m) s -> m a) -> m a
57−withTagGen f = do
58− g <- newTagGen
59− 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+16−7
@@ -1,6 +1,6 @@
11{-|
22Module: Reflex.BehaviorWriter.Base
3−Description: Implementation of MonadBehaviorWriter
3+Description: Implementation of BehaviorWriter
44-}
55{-# LANGUAGE CPP #-}
66{-# LANGUAGE FlexibleInstances #-}
@@ -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
@@ -46,9 +47,20 @@ import Reflex.Query.Class
4647import Reflex.Requester.Class
4748import Reflex.TriggerEvent.Class
4849
49−-- | A basic implementation of 'MonadBehaviorWriter'.
50+-- | 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
@@ -89,7 +98,7 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (BehaviorWri
8998 newEventWithTrigger = lift . newEventWithTrigger
9099 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
91100
92−instance (Monad m, Monoid w, Reflex t) => MonadBehaviorWriter t w (BehaviorWriterT t w m) where
101+instance (Monad m, Monoid w, Reflex t) => BehaviorWriter t w (BehaviorWriterT t w m) where
93102 tellBehavior w = BehaviorWriterT $ modify (w :)
94103
95104instance MonadReader r m => MonadReader r (BehaviorWriterT t w m) where
src/Reflex/BehaviorWriter/Class.hs+11−5
@@ -1,8 +1,9 @@
11{-|
22Module: Reflex.BehaviorWriter.Class
3−Description: This module defines the 'MonadBehaviorWriter' class
3+Description: This module defines the 'BehaviorWriter' class
44-}
55{-# LANGUAGE CPP #-}
6+{-# LANGUAGE ConstraintKinds #-}
67{-# LANGUAGE FlexibleInstances #-}
78{-# LANGUAGE FunctionalDependencies #-}
89{-# LANGUAGE UndecidableInstances #-}
@@ -10,16 +11,21 @@ Description: This module defines the 'MonadBehaviorWriter' class
1011{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1112#endif
1213module Reflex.BehaviorWriter.Class
13− ( MonadBehaviorWriter (..)
14+ ( MonadBehaviorWriter
15+ , BehaviorWriter(..)
1416 ) where
1517
1618import Control.Monad.Reader (ReaderT, lift)
1719import Reflex.Class (Behavior)
1820
19−-- | 'MonadBehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
21+{-# DEPRECATED MonadBehaviorWriter "Use 'BehaviorWriter' instead" #-}
22+-- | Type synonym for 'BehaviorWriter'
23+type MonadBehaviorWriter = BehaviorWriter
24+
25+-- | 'BehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
2026-- and combines them monoidally to provide a 'Behavior' result.
21−class (Monad m, Monoid w) => MonadBehaviorWriter t w m | m -> t w where
27+class (Monad m, Monoid w) => BehaviorWriter t w m | m -> t w where
2228 tellBehavior :: Behavior t w -> m ()
2329
24−instance MonadBehaviorWriter t w m => MonadBehaviorWriter t w (ReaderT r m) where
30+instance BehaviorWriter t w m => BehaviorWriter t w (ReaderT r m) where
2531 tellBehavior = lift . tellBehavior
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/FastWeak.hs+10
@@ -151,6 +151,16 @@ getFastWeakTicket w = do
151151
152152-- | Create a 'FastWeakTicket' directly from a value, creating a 'FastWeak' in the process
153153-- which can be obtained with 'getFastWeakTicketValue'.
154+--
155+-- This function is marked NOINLINE so it is opaque to GHC.
156+-- If we do not do this, then GHC will sometimes fuse the constructor away
157+-- so any weak references that are attached to the ticket will have their
158+-- finalizer run. Using the opaque constructor, GHC does not see the
159+-- constructor application, so it behaves like an IORef and cannot be fused away.
160+--
161+-- The result is also evaluated to WHNF, since forcing a thunk invalidates
162+-- the weak pointer to it in some cases.
163+{-# NOINLINE mkFastWeakTicket #-}
154164mkFastWeakTicket :: a -> IO (FastWeakTicket a)
155165-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the
156166-- associated FastWeak to actually be weak
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−82
@@ -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,74 +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− --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
98− RequesterInternalT $ tellEvent requests
99− pure (result0, fmapCheap fst newA)
100− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
101− traverseIntMapWithKeyWithAdjust f a0 a' = PerformEventT $ RequesterT $ do
102− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
103− let runA :: forall a. PerformEventT t m a -> HostFrame t (a, Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))
104− runA (PerformEventT (RequesterT a)) = runEventWriterT $ runReaderT (unRequesterInternalT a) env
105− children' <- requestingIdentity $ itraverse (\k -> runA . f k) <$> a'
106− children0 <- lift $ itraverse (\k -> runA . f k) a0
107− let results0 = fmap fst children0
108− requests0 = fmap snd children0
109− results' = fmap fst <$> children'
110− requests' = fmap snd `fmapCheap` children'
111− requests <- switchHoldPromptOnlyIncremental mergeIntIncremental coincidencePatchIntMap requests0 requests'
112− --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
113− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatIntMapMaybe
114− pure (results0, results')
115− {-# INLINE traverseDMapWithKeyWithAdjust #-}
116− traverseDMapWithKeyWithAdjust (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
117− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
118− let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
119− runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
120− where (PerformEventT (RequesterT a)) = f k v
121− children' <- requestingIdentity $ traversePatchDMapWithKey runA <$> a'
122− children0 <- lift $ DMap.traverseWithKey runA a0
123− let results0 = DMap.map (snd . getCompose) children0
124− requests0 = weakenDMapWith (fst . getCompose) children0
125− results' = mapPatchDMap (snd . getCompose) <$> children'
126− requests' = weakenPatchDMapWith (fst . getCompose) `fmapCheap` children'
127− requests <- switchHoldPromptOnlyIncremental mergeMapIncremental coincidencePatchMap requests0 requests'
128− --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
129− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
130− pure (results0, results')
131− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
132− traverseDMapWithKeyWithAdjustWithMove (f :: forall a. k a -> v a -> PerformEventT t m (v' a)) (a0 :: DMap k v) a' = PerformEventT $ RequesterT $ do
133− env@(_, _ :: TagGen (PrimState (HostFrame t)) FakeRequesterStatePhantom) <- RequesterInternalT ask
134− let runA :: forall a. k a -> v a -> HostFrame t (Compose ((,) (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom (HostFrame t))))) v' a)
135− runA k v = fmap (Compose . swap) $ runEventWriterT $ runReaderT (unRequesterInternalT a) env
136− where (PerformEventT (RequesterT a)) = f k v
137− children' <- requestingIdentity $ traversePatchDMapWithMoveWithKey runA <$> a'
138− children0 <- lift $ DMap.traverseWithKey runA a0
139− let results0 = DMap.map (snd . getCompose) children0
140− requests0 = weakenDMapWith (fst . getCompose) children0
141− results' = mapPatchDMapWithMove (snd . getCompose) <$> children'
142− requests' = weakenPatchDMapWithMoveWith (fst . getCompose) `fmapCheap` children'
143− requests <- switchHoldPromptOnlyIncremental mergeMapIncrementalWithMove coincidencePatchMapWithMove requests0 requests'
144− --TODO: promptly *prevent* events, then sign up the new ones; this is a serious breaking change to PerformEvent
145− RequesterInternalT $ tellEvent $ fforMaybeCheap requests concatMapMaybe
146− pure (results0, results')
147−
148−concatIntMapMaybe :: Semigroup a => IntMap a -> Maybe a
149−concatIntMapMaybe m = case IntMap.elems m of
150− [] -> Nothing
151− h : t -> Just $ sconcat $ h :| t
152−
153−concatMapMaybe :: Semigroup a => Map k a -> Maybe a
154−concatMapMaybe m = case Map.elems m of
155− [] -> Nothing
156− 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')
157110
158111instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
159112 {-# INLINABLE newEventWithTrigger #-}
@@ -171,7 +124,6 @@ hostPerformEventT :: forall t m a.
171124 , MonadReflexHost t m
172125 , MonadRef m
173126 , Ref m ~ Ref IO
174− , PrimMonad (HostFrame t)
175127 )
176128 => PerformEventT t m a
177129 -> m (a, FireCommand t m)
@@ -212,6 +164,8 @@ instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
212164 buildDynamic getV0 v' = PerformEventT $ lift $ buildDynamic getV0 v'
213165 {-# INLINABLE headE #-}
214166 headE = PerformEventT . lift . headE
167+ {-# INLINABLE now #-}
168+ now = PerformEventT . lift $ now
215169
216170instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
217171 type Ref (PerformEventT t m) = Ref (HostFrame t)
src/Reflex/PerformEvent/Class.hs+6−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 #-}
@@ -22,6 +21,7 @@ import Reflex.Class
2221import Reflex.TriggerEvent.Class
2322
2423import Control.Monad.Reader
24+import Control.Monad.Trans.Maybe (MaybeT (..))
2525
2626-- | 'PerformEvent' represents actions that can trigger other actions based on
2727-- 'Event's.
@@ -61,3 +61,8 @@ instance PerformEvent t m => PerformEvent t (ReaderT r m) where
6161 performEvent e = do
6262 r <- ask
6363 lift $ performEvent $ flip runReaderT r <$> e
64+
65+instance PerformEvent t m => PerformEvent t (MaybeT m) where
66+ type Performable (MaybeT m) = MaybeT (Performable m)
67+ performEvent_ = lift . performEvent_ . fmapCheap (void . runMaybeT)
68+ performEvent = lift . fmap (fmapMaybe id) . performEvent . fmapCheap runMaybeT
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+59−4
@@ -26,8 +26,10 @@ import Control.Monad.Primitive
2626import Control.Monad.Reader
2727import Control.Monad.Ref
2828import Control.Monad.State.Strict (StateT, execStateT, modify)
29+import Data.Bifunctor
2930import Data.Coerce
30−import Data.Dependent.Map (DMap, GCompare)
31+import Data.Dependent.Map (DMap)
32+import Data.GADT.Compare (GCompare)
3133import Data.FastMutableIntMap
3234import Data.IORef
3335import Data.List
@@ -42,9 +44,18 @@ import Foreign.Ptr
4244import GHC.Foreign
4345import GHC.IO.Encoding
4446import GHC.Stack
47+import Reflex.Adjustable.Class
48+import Reflex.BehaviorWriter.Class
4549import Reflex.Class
50+import Reflex.DynamicWriter.Class
51+import Reflex.EventWriter.Class
4652import Reflex.Host.Class
53+import Reflex.NotReady.Class
4754import Reflex.PerformEvent.Class
55+import Reflex.PostBuild.Class
56+import Reflex.Query.Class
57+import Reflex.Requester.Class
58+import Reflex.TriggerEvent.Class
4859
4960import System.IO.Unsafe
5061
@@ -136,7 +147,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
136147 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
137148 pull = Behavior_Profiled . pull . coerce
138149 fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
139− mergeG :: forall (k :: z -> *) q v. GCompare k
150+ mergeG :: forall z (k :: z -> *) q v. GCompare k
140151 => (forall a. q a -> Event (ProfiledTimeline t) (v a))
141152 -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
142153 mergeG nt = Event_Profiled #. mergeG (coerce nt)
@@ -146,8 +157,8 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
146157 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
147158 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
148159 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
149− mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
150− mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
160+ mergeIncrementalG nt res = Event_Profiled $ mergeIncrementalG (coerce nt) (coerce res)
161+ mergeIncrementalWithMoveG nt res = Event_Profiled $ mergeIncrementalWithMoveG (coerce nt) (coerce res)
151162 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
152163 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
153164 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
@@ -173,10 +184,21 @@ instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
173184 holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
174185 buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
175186 headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
187+ now = ProfiledM $ Event_Profiled <$> now
176188
177189instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
178190 sample (Behavior_Profiled b) = ProfiledM $ sample b
179191
192+instance Adjustable t m => Adjustable (ProfiledTimeline t) (ProfiledM m) where
193+ runWithReplace a0 a' = (fmap . fmap) coerce . lift $
194+ runWithReplace (coerce a0) (coerce $ coerce <$> a')
195+ traverseIntMapWithKeyWithAdjust f dm0 dm' = (fmap . fmap) coerce . lift $
196+ traverseIntMapWithKeyWithAdjust (\k v -> coerce $ f k v) dm0 (coerce dm')
197+ traverseDMapWithKeyWithAdjust f dm0 dm' = (fmap . fmap) coerce . lift $
198+ traverseDMapWithKeyWithAdjust (\k v -> coerce $ f k v) dm0 (coerce dm')
199+ traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = (fmap . fmap) coerce . lift $
200+ traverseDMapWithKeyWithAdjustWithMove (\k v -> coerce $ f k v) dm0 (coerce dm')
201+
180202instance MonadTrans ProfiledM where
181203 lift = ProfiledM
182204
@@ -188,6 +210,39 @@ instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) whe
188210 performEvent_ = lift . performEvent_ . coerce
189211 performEvent = lift . fmap coerce . performEvent . coerce
190212
213+instance TriggerEvent t m => TriggerEvent (ProfiledTimeline t) (ProfiledM m) where
214+ newTriggerEvent = first coerce <$> lift newTriggerEvent
215+ newTriggerEventWithOnComplete = first coerce <$> lift newTriggerEventWithOnComplete
216+ newEventWithLazyTriggerWithOnComplete f = coerce <$> lift (newEventWithLazyTriggerWithOnComplete f)
217+
218+instance PostBuild t m => PostBuild (ProfiledTimeline t) (ProfiledM m) where
219+ getPostBuild = coerce <$> lift getPostBuild
220+
221+instance NotReady t m => NotReady (ProfiledTimeline t) (ProfiledM m) where
222+ notReady = lift notReady
223+ notReadyUntil = lift . notReadyUntil . coerce
224+
225+instance BehaviorWriter t w m => BehaviorWriter (ProfiledTimeline t) w (ProfiledM m) where
226+ tellBehavior = lift . tellBehavior . coerce
227+
228+instance DynamicWriter t w m => DynamicWriter (ProfiledTimeline t) w (ProfiledM m) where
229+ tellDyn = lift . tellDyn . coerce
230+
231+instance EventWriter t w m => EventWriter (ProfiledTimeline t) w (ProfiledM m) where
232+ tellEvent = lift . tellEvent . coerce
233+
234+instance MonadQuery t q m => MonadQuery (ProfiledTimeline t) q (ProfiledM m) where
235+ tellQueryIncremental = lift . tellQueryIncremental . coerce
236+ askQueryResult = coerce <$> lift askQueryResult
237+ queryIncremental = fmap coerce . lift . queryIncremental . coerce
238+
239+instance Requester t m => Requester (ProfiledTimeline t) (ProfiledM m) where
240+ type Request (ProfiledM m) = Request m
241+ type Response (ProfiledM m) = Response m
242+
243+ requesting = fmap coerce . lift . requesting . coerce
244+ requesting_ = lift . requesting_ . coerce
245+
191246instance MonadRef m => MonadRef (ProfiledM m) where
192247 type Ref (ProfiledM m) = Ref m
193248 newRef = lift . newRef
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/Query/Class.hs+5−5
@@ -125,7 +125,7 @@ instance Group SelectedCount where
125125
126126instance Additive SelectedCount
127127
128−-- | The Semigroup/Monoid/Group instances for a Query containing 'SelectedCount's should use
128+-- | The Semigroup\/Monoid\/Group instances for a Query containing 'SelectedCount's should use
129129-- this function which returns Nothing if the result is 0. This allows the pruning of leaves
130130-- of the 'Query' that are no longer wanted.
131131combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
@@ -133,12 +133,12 @@ combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j the
133133
134134-- | A class that allows sending of 'Query's and retrieval of 'QueryResult's. See 'queryDyn' for a commonly
135135-- used interface.
136−class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
136+class (Group q, Additive q, Query q, Monad m) => MonadQuery t q m | m -> q t where
137137 tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
138138 askQueryResult :: m (Dynamic t (QueryResult q))
139139 queryIncremental :: Incremental t (AdditivePatch q) -> m (Dynamic t (QueryResult q))
140140
141−instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
141+instance MonadQuery t q m => MonadQuery t q (ReaderT r m) where
142142 tellQueryIncremental = lift . tellQueryIncremental
143143 askQueryResult = lift askQueryResult
144144 queryIncremental = lift . queryIncremental
@@ -148,11 +148,11 @@ tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
148148tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
149149
150150-- | Retrieve 'Dynamic'ally updating 'QueryResult's for a 'Dynamic'ally updating 'Query'.
151−queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
151+queryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
152152queryDyn q = do
153153 tellQueryDyn q
154154 zipDynWith crop q <$> askQueryResult
155155
156156-- | Use a query morphism to operate on a smaller version of a query.
157−subQuery :: (Reflex t, MonadQuery t q2 m, Monad m) => QueryMorphism q1 q2 -> Dynamic t q1 -> m (Dynamic t (QueryResult q1))
157+subQuery :: (Reflex t, MonadQuery t q2 m) => QueryMorphism q1 q2 -> Dynamic t q1 -> m (Dynamic t (QueryResult q1))
158158subQuery (QueryMorphism f g) x = fmap g <$> queryDyn (fmap f x)
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−310
@@ -1,310 +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 TypeApplications #-}
14−{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE TypeOperators #-}
16−{-# LANGUAGE UndecidableInstances #-}
17−{-# LANGUAGE InstanceSigs #-}
18−#ifdef USE_REFLEX_OPTIMIZER
19−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20−#endif
21−module Reflex.Requester.Base.Internal where
22−
23−import Reflex.Class
24−import Reflex.Adjustable.Class
25−import Reflex.Host.Class
26−import Reflex.PerformEvent.Class
27−import Reflex.PostBuild.Class
28−import Reflex.Requester.Class
29−import Reflex.TriggerEvent.Class
30−import Reflex.EventWriter.Base
31−import Reflex.EventWriter.Class
32−
33−import Control.Applicative (liftA2)
34−import Control.Monad.Exception
35−import Control.Monad.Fail
36−import Control.Monad.Identity
37−import Control.Monad.Primitive
38−import Control.Monad.Reader
39−import Control.Monad.Ref
40−import Control.Monad.State
41−import Data.Coerce
42−import Data.Dependent.Map (DSum (..))
43−import qualified Data.List.NonEmpty as NonEmpty
44−import Data.List.NonEmpty.Deferred (NonEmptyDeferred)
45−import qualified Data.List.NonEmpty.Deferred as NonEmptyDeferred
46−import Data.Monoid ((<>))
47−import qualified Data.Semigroup as S
48−import Data.Some (Some(Some))
49−import Data.Type.Equality
50−import Data.TagMap (TagMap)
51−import qualified Data.TagMap as TagMap
52−import Reflex.FanTag
53−import Data.Unique.Tag.Local
54−import Data.GADT.Compare
55−import Data.Witherable
56−
57−import Unsafe.Coerce
58−
59−import Debug.Trace
60−
61−import Data.Map (Map)
62−import qualified Data.Map as Map
63−import qualified Data.IntMap as IntMap
64−import GHC.Exts (Any)
65−
66−data RequestData ps request = forall s. RequestData !(TagGen ps s) !(NonEmptyDeferred (RequestEnvelope s request))
67−
68−data RequestEnvelope s request = forall a. RequestEnvelope {-# UNPACK #-} !(Maybe (Tag s a)) !(request a)
69−
70−data ResponseData ps response = forall s. ResponseData !(TagGen ps s) !(TagMap s response)
71−
72−traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequestData (PrimState m) request -> m (ResponseData (PrimState m) response)
73−traverseRequesterData f (RequestData tg es) = ResponseData tg . TagMap.fromList <$> wither g (NonEmpty.toList $ NonEmptyDeferred.toNonEmpty es)
74− where g (RequestEnvelope mt req) = case mt of
75− Just t -> Just . (t :=>) <$> f req
76− Nothing -> Nothing <$ f req
77−
78−-- | 'traverseRequesterData' with its arguments flipped
79−forRequesterData :: forall request response m. Applicative m => RequestData (PrimState m) request -> (forall a. request a -> m (response a)) -> m (ResponseData (PrimState m) response)
80−forRequesterData r f = traverseRequesterData f r
81−
82−runRequesterT :: forall t request response m a
83− . ( Reflex t
84− , PrimMonad m
85− , MonadFix m
86− )
87− => RequesterT t request response m a
88− -> Event t (ResponseData (PrimState m) response)
89− -> m (a, Event t (RequestData (PrimState m) request))
90−runRequesterT (RequesterT a) wrappedResponses = withRequesterInternalT $ \requests -> do
91− (_, tg) <- RequesterInternalT ask
92− result <- a
93− let responses = fforMaybe wrappedResponses $ \(ResponseData tg' m) -> case tg `geq` tg' of
94− Nothing -> trace ("runRequesterT: bad TagGen: expected " <> show tg <> " but got " <> show tg') Nothing --TODO: Warn somehow
95− Just Refl -> Just m
96− pure (responses, (result, fmapCheap (RequestData tg) requests))
97−
98−instance MonadTrans (RequesterInternalT s t request response) where
99− lift = RequesterInternalT . lift . lift
100−
101−instance MonadTrans (RequesterT t request response) where
102− lift a = RequesterT $ RequesterInternalT $ lift $ lift a
103−
104−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
105−-- requests are made, and responses should be provided in the input 'Event'.
106−-- The 'Tag' keys will be used to return the responses to the same place the
107−-- requests were issued.
108−withRequesterInternalT
109− :: forall t m request response a.
110− ( Reflex t
111− , PrimMonad m
112− , MonadFix m
113− )
114− => (Event t (NonEmptyDeferred (RequestEnvelope FakeRequesterStatePhantom request)) -> RequesterInternalT FakeRequesterStatePhantom t request response m (Event t (TagMap FakeRequesterStatePhantom response), a))
115− -> m a
116−withRequesterInternalT f = do
117− stg <- newTagGen
118− let tg = case stg of
119− Some tg' -> (unsafeCoerce :: TagGen (PrimState m) s -> TagGen (PrimState m) FakeRequesterStatePhantom) tg'
120− 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
121− ((responses, result), requests) <- runEventWriterT $ runReaderT a (fanTag responses, tg)
122− pure result
123−
124−-- This is because using forall ruins inlining
125−data FakeRequesterStatePhantom
126−
127−newtype RequesterT t (request :: * -> *) (response :: * -> *) m a = RequesterT { unRequesterT :: RequesterInternalT FakeRequesterStatePhantom t request response m a }
128− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
129−
130−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 }
131− deriving
132− ( Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
133−#if MIN_VERSION_base(4,9,1)
134− , MonadAsyncException
135−#endif
136− )
137−
138−-- 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.
139−-- #if MIN_VERSION_base(4,9,1)
140−-- instance MonadAsyncException m => MonadAsyncException (RequesterT t request response m) where
141−-- mask f = RequesterT $ mask $ \unmask -> unRequesterT $ f $ \x -> RequesterT $ unmask $ unRequesterT x
142−-- #endif
143−
144−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
145−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
146−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
147−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
148−
149−deriving instance MonadSample t m => MonadSample t (RequesterInternalT s t request response m)
150−deriving instance MonadHold t m => MonadHold t (RequesterInternalT s t request response m)
151−deriving instance PostBuild t m => PostBuild t (RequesterInternalT s t request response m)
152−deriving instance TriggerEvent t m => TriggerEvent t (RequesterInternalT s t request response m)
153−
154−instance PrimMonad m => PrimMonad (RequesterT t request response m) where
155− type PrimState (RequesterT t request response m) = PrimState m
156− primitive = lift . primitive
157−
158−-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
159−instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
160− mempty = pure mempty
161− mappend = liftA2 mappend
162−
163−instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
164− (<>) = liftA2 (S.<>)
165−
166−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
167− type Performable (RequesterT t request response m) = Performable m
168− performEvent_ = lift . performEvent_
169− performEvent = lift . performEvent
170−
171−instance MonadRef m => MonadRef (RequesterT t request response m) where
172− type Ref (RequesterT t request response m) = Ref m
173− newRef = lift . newRef
174− readRef = lift . readRef
175− writeRef r = lift . writeRef r
176−
177−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
178− newEventWithTrigger = lift . newEventWithTrigger
179− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
180−
181−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
182− ask = lift ask
183− local f (RequesterT a) = RequesterT $ RequesterInternalT $ mapReaderT (mapEventWriterT $ local f) $ unRequesterInternalT a
184− reader = lift . reader
185−
186−instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
187− type Request (RequesterT t request response m) = request
188− type Response (RequesterT t request response m) = response
189− requesting e = RequesterT $ requesting e
190− requesting_ e = RequesterT $ requesting_ e
191−
192−instance (Reflex t, PrimMonad m) => Requester t (RequesterInternalT s t request response m) where
193− type Request (RequesterInternalT s t request response m) = request
194− type Response (RequesterInternalT s t request response m) = response
195− requesting e = RequesterInternalT $ do
196− (s, tg) <- ask
197− t <- lift $ lift $ newTag tg
198− tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope (Just t)) e
199− pure $ selectTag s t
200− requesting_ e = RequesterInternalT $ do
201− tellEvent $ fmapCheap (NonEmptyDeferred.singleton . RequestEnvelope Nothing) e
202−
203−instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterInternalT s t request response m) where
204− {-# INLINE runWithReplace #-}
205− runWithReplace (RequesterInternalT a0) a' = RequesterInternalT $ runWithReplace a0 (coerceEvent a')
206− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
207− traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseIntMapWithKeyWithAdjust (coerce . f) dm0 dm'
208− {-# INLINE traverseDMapWithKeyWithAdjust #-}
209− traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
210− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
211− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterInternalT $ traverseDMapWithKeyWithAdjustWithMove (coerce . f) dm0 dm'
212−
213−instance (Adjustable t m, MonadHold t m) => Adjustable t (RequesterT t request response m) where
214− {-# INLINE runWithReplace #-}
215− runWithReplace (RequesterT a0) a' = RequesterT $ runWithReplace a0 (fmapCheap unRequesterT a')
216− {-# INLINE traverseIntMapWithKeyWithAdjust #-}
217− traverseIntMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseIntMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
218− {-# INLINE traverseDMapWithKeyWithAdjust #-}
219− traverseDMapWithKeyWithAdjust f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) dm0 dm'
220− {-# INLINE traverseDMapWithKeyWithAdjustWithMove #-}
221− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) dm0 dm'
222−
223−
224−data Decoder rawResponse response =
225− forall s a. Decoder (Tag s a) (rawResponse -> response a)
226−
227−-- | Matches incoming responses with previously-sent requests
228−-- and uses the provided request "decoder" function to process
229−-- incoming responses.
230−matchResponsesWithRequests
231− :: forall t rawRequest rawResponse request response m.
232− ( MonadFix m
233− , MonadHold t m
234− , PrimMonad m
235− , Reflex t
236− )
237− => (forall a. request a -> (rawRequest, rawResponse -> response a))
238− -- ^ Given a request (from 'Requester'), produces the wire format of the
239− -- request and a function used to process the associated response
240− -> Event t (RequestData (PrimState m) request)
241− -- ^ The outgoing requests
242− -> Event t (Int, rawResponse)
243− -- ^ The incoming responses, tagged by an identifying key
244− -> m ( Event t (Map Int rawRequest)
245− , Event t (ResponseData (PrimState m) response)
246− )
247− -- ^ A map of outgoing wire-format requests and an event of responses keyed
248− -- by the 'RequesterData' key of the associated outgoing request
249−matchResponsesWithRequests f send recv = withTagGen $ \tagGen -> mdo
250− waitingFor <- holdIncremental mempty $
251− leftmost [ fst <$> outgoing, snd <$> incoming ]
252− let outgoing = processOutgoing send
253− incoming = processIncoming waitingFor tagGen recv
254− return (snd <$> outgoing, fst <$> incoming)
255− where
256−
257− -- Tags each outgoing request with an identifying integer key
258− -- and returns the next available key, a map of response decoders
259− -- for requests for which there are outstanding responses, and the
260− -- raw requests to be sent out.
261− processOutgoing
262− :: Event t (RequestData (PrimState m) request)
263− -- The outgoing request
264− -> Event t ( PatchMap Int (Decoder rawResponse response)
265− , Map Int rawRequest )
266− -- A map of requests expecting responses, and the tagged raw requests
267− processOutgoing eventOutgoingRequest = flip pushAlways eventOutgoingRequest $ \(RequestData _ requestEnvelopes) -> do
268− let results = ffor (requestEnvelopesToDSums requestEnvelopes) $ \(k :=> v) ->
269− let (rawRequest, responseDecoder) = f v
270− in (tagId k, rawRequest, Decoder k responseDecoder)
271− let patchWaitingFor = PatchMap $ Map.fromList $ (\(n, _, dec) -> (n, Just dec)) <$> results
272− let toSend = Map.fromList $ (\(n, rawRequest, _) -> (n, rawRequest)) <$> results
273− return (patchWaitingFor, toSend)
274−
275− -- Looks up the each incoming raw response in a map of response
276− -- decoders and returns the decoded response and a patch that can
277− -- be used to clear the ID of the consumed response out of the queue
278− -- of expected responses.
279− processIncoming
280− :: Incremental t (PatchMap Int (Decoder rawResponse response))
281− -- A map of outstanding expected responses
282− -> TagGen (PrimState m) s
283− -- A tag generator
284− -> Event t (Int, rawResponse)
285− -- A incoming response paired with its identifying key
286− -> Event t (ResponseData (PrimState m) response, PatchMap Int v)
287− -- The decoded response and a patch that clears the outstanding responses queue
288− processIncoming incWaitingFor tagGen inc = flip push inc $ \(n, rawRsp) -> do
289− waitingFor <- sample $ currentIncremental incWaitingFor
290− case Map.lookup n waitingFor of
291− Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
292− Just (Decoder tag responseDecoder) ->
293− return $ Just
294− ( singletonResponseData tagGen (unsafeTagFromId n) (responseDecoder rawRsp)
295− , PatchMap $ Map.singleton n Nothing
296− )
297−
298−requestEnvelopesToDSums :: forall s request. NonEmptyDeferred (RequestEnvelope s request) -> [DSum (Tag s) request]
299−requestEnvelopesToDSums requestEnvelopes = catMaybes $ f <$> NonEmptyDeferred.toList requestEnvelopes
300− where f :: (RequestEnvelope s request) -> Maybe (DSum (Tag s) request)
301− f (RequestEnvelope (Just tag) v) = Just (tag :=> v)
302− f (RequestEnvelope Nothing _) = Nothing
303−
304−singletonRequestData :: TagGen ps s -> Maybe (Tag s a) -> request a -> RequestData ps request
305−singletonRequestData tagGen maybeTag request =
306− RequestData tagGen $ NonEmptyDeferred.singleton $ RequestEnvelope maybeTag request
307−
308−singletonResponseData :: TagGen ps s -> Tag ps s -> response a -> ResponseData ps response
309−singletonResponseData tagGen tag response =
310− 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−14
@@ -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
@@ -450,16 +464,6 @@ data Subscriber x a = Subscriber
450464 , subscriberRecalculateHeight :: !(Height -> IO ())
451465 }
452466
453−--TODO: Move this comment to WeakBag
454−-- These function are constructor functions that are marked NOINLINE so they are
455−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
456−-- so any weak references that are attached to the constructors will have their
457−-- finalizer run. Using the opaque constructor, does not see the
458−-- constructor application, so it behaves like an IORef and cannot be fused away.
459−--
460−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
461−-- the weak pointer to it in some cases.
462−
463467newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
464468newSubscriberHold h = return $ Subscriber
465469 { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
@@ -603,6 +607,16 @@ eventSubscribedNever = EventSubscribed
603607 , eventSubscribedWhoCreated = return ["never"]
604608#endif
605609 }
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+ }
606620
607621eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
608622eventSubscribedFan !subscribed = EventSubscribed
@@ -2506,6 +2520,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Event
25062520 {-# INLINABLE headE #-}
25072521 headE = R.slowHeadE
25082522-- headE (SpiderEvent e) = SpiderEvent <$> Reflex.Spider.Internal.headE e
2523+ {-# INLINABLE now #-}
2524+ now = nowSpiderEventM
25092525
25102526instance Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25112527 {-# INLINABLE sample #-}
@@ -2527,6 +2543,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25272543 {-# INLINABLE headE #-}
25282544 headE = R.slowHeadE
25292545-- headE (SpiderEvent e) = SpiderPushM $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2546+ {-# INLINABLE now #-}
2547+ now = SpiderPushM nowSpiderEventM
2548+
25302549
25312550instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
25322551 {-# INLINE return #-}
@@ -2590,6 +2609,9 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
25902609 buildDynamic getV0 e = runFrame . runSpiderHostFrame $ Reflex.Class.buildDynamic getV0 e
25912610 {-# INLINABLE headE #-}
25922611 headE e = runFrame . runSpiderHostFrame $ Reflex.Class.headE e
2612+ {-# INLINABLE now #-}
2613+ now = runFrame . runSpiderHostFrame $ Reflex.Class.now
2614+
25932615
25942616instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
25952617 sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
@@ -2606,6 +2628,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Spide
26062628 {-# INLINABLE headE #-}
26072629 headE = R.slowHeadE
26082630-- headE (SpiderEvent e) = SpiderHostFrame $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2631+ {-# INLINABLE now #-}
2632+ now = SpiderHostFrame Reflex.Class.now
26092633
26102634instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
26112635 {-# INLINABLE sample #-}
@@ -2626,6 +2650,8 @@ instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Refle
26262650 buildDynamic getV0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.buildDynamic getV0 e
26272651 {-# INLINABLE headE #-}
26282652 headE e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.headE e
2653+ {-# INLINABLE now #-}
2654+ now = Reflex.Spider.Internal.ReadPhase Reflex.Class.now
26292655
26302656--------------------------------------------------------------------------------
26312657-- Deprecated items
@@ -2749,7 +2775,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
27492775 fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
27502776 {-# INLINABLE mergeG #-}
27512777 mergeG
2752− :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2778+ :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k
27532779 => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
27542780 -> DMap k q
27552781 -> 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 #-}
src/Reflex/TriggerEvent/Class.hs+6
@@ -12,6 +12,7 @@ import Reflex.Class
1212import Control.Monad.Reader
1313import Control.Monad.State
1414import qualified Control.Monad.State.Strict as Strict
15+import Control.Monad.Trans.Maybe (MaybeT)
1516
1617--TODO: Shouldn't have IO hard-coded
1718-- | 'TriggerEvent' represents actions that can create 'Event's that can be
@@ -47,3 +48,8 @@ instance TriggerEvent t m => TriggerEvent t (Strict.StateT s m) where
4748 newTriggerEvent = lift newTriggerEvent
4849 newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
4950 newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
51+
52+instance TriggerEvent t m => TriggerEvent t (MaybeT m) where
53+ newTriggerEvent = lift newTriggerEvent
54+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
55+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
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+13−8
@@ -21,11 +21,12 @@ import Reflex.Dynamic
2121import Reflex.Host.Class
2222import qualified Reflex.Pure as P
2323import qualified Reflex.Spider.Internal as S
24+import qualified Reflex.Profiled as Prof
2425
2526import Control.Arrow (second, (&&&))
2627import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
2728import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28−import Data.Dependent.Map (DSum (..))
29+import Data.Dependent.Sum (DSum (..))
2930import Data.Foldable
3031import Data.Map.Strict (Map)
3132import qualified Data.Map.Strict as Map
@@ -35,8 +36,6 @@ import Data.Traversable
3536import System.Exit
3637import System.Mem
3738
38−import System.IO.Unsafe
39−
4039mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
4140mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
4241 Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
@@ -73,11 +72,14 @@ instance MapMSignals (Event t a) (Event t' a) t t' where
7372instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
7473 mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
7574
76−testSpider :: (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> (Map Int c, Map Int d)
77−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
75+testTimeline
76+ :: (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d))
77+ -> (Map Int a, Map Int b)
78+ -> (forall m t. (MonadReflexHost t m, MonadIO m, MonadRef m, Ref m ~ Ref IO, MonadSample t m) => m (Map Int c, Map Int d))
79+testTimeline builder (bMap, eMap) = do
7880 (re, reTrigger) <- newEventWithTriggerRef
7981 (rb, rbTrigger) <- newEventWithTriggerRef
80− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
82+ b <- runHostFrame $ hold (error "testTimeline: No value for input behavior yet") rb
8183 (b', e') <- runHostFrame $ builder (b, re)
8284 e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
8385 let times = relevantTestingTimes (bMap, eMap)
@@ -102,9 +104,11 @@ testAgreement :: (Eq c, Eq d, Show c, Show d) => (forall m t. TestCaseConstraint
102104testAgreement builder inputs = do
103105 let identityResult = testPure builder inputs
104106 tracePerf "---------" $ return ()
105− let spiderResult = testSpider builder inputs
107+ spiderResult <- S.runSpiderHost $ testTimeline builder inputs
108+ tracePerf "---------" $ return ()
109+ profiledResult <- S.runSpiderHost $ Prof.runProfiledM $ testTimeline builder inputs
106110 tracePerf "---------" $ return ()
107− let resultsAgree = identityResult == spiderResult
111+ let resultsAgree = identityResult == spiderResult && identityResult == profiledResult
108112 if resultsAgree
109113 then do --putStrLn "Success:"
110114 --print identityResult
@@ -112,6 +116,7 @@ testAgreement builder inputs = do
112116 else do putStrLn "Failure:"
113117 putStrLn $ "Pure result: " <> show identityResult
114118 putStrLn $ "Spider result: " <> show spiderResult
119+ putStrLn $ "Profiled result: " <> show profiledResult
115120 return resultsAgree
116121
117122type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
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+27−56
@@ -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,20 +14,22 @@ 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
2522import qualified Data.Dependent.Map as DMap
2623import Data.Dependent.Sum
2724import Data.Functor.Misc
25+import Data.List (words)
2826import Data.Map (Map)
2927import qualified Data.Map as M
28+#if !MIN_VERSION_these(4,11,0)
29+import Data.Semigroup ((<>))
30+#endif
31+import Data.Text (Text)
3032import Data.These
31−import Data.List.NonEmpty.Deferred
3233import Text.Read (readMaybe)
3334
3435#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
@@ -40,8 +41,6 @@ import Reflex.Requester.Base
4041import Reflex.Requester.Class
4142import Test.Run
4243
43−import Debug.Trace hiding (traceEvent)
44−
4544data RequestInt a where
4645 RequestInt :: Int -> RequestInt Int
4746
@@ -50,7 +49,7 @@ main = do
5049 os1 <- runApp' (unwrapApp testOrdering) $
5150 [ Just ()
5251 ]
53− --print os1
52+ print os1
5453 os2 <- runApp' (unwrapApp testSimultaneous) $ map Just $
5554 [ This ()
5655 , That ()
@@ -66,36 +65,31 @@ main = do
6665 print os5
6766 os6 <- runApp' (unwrapApp delayedPulse) [Just ()]
6867 print os6
69− os7 <- runApp' testMatchRequestsWithResponses $ map Just [ TestRequest_Increment 1, TestRequest_Increment 2 ]
68+ os7 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Increment 1 ]
7069 print os7
71− os8 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "abcd" ]
70+ os8 <- runApp' testMatchRequestsWithResponses [ Just $ TestRequest_Reverse "yoyo" ]
7271 print os8
73− os9 <- runApp' testMoribundPerformEvent $ map Just [ 1 .. 3 ]
74− print os9
75− let ![[Just [10,9,8,7,6,5,4,3,2,1]]] = os1
76− 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
7774 let ![[Nothing, Just [2]]] = os3
7875 let ![[Nothing, Just [2]]] = os4
7976 let ![[Nothing, Just [1, 2]]] = os5
80− let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
81− let !(Just [(-9223372036854775808,"2")]) = M.toList <$> head (head os7)
82− let !(Just [(-9223372036854775808,"dcba")]) = M.toList <$> head (head os8)
83− let ![[Nothing,Just "0:1"],[Nothing,Just "1:2"],[Nothing,Just "2:3"]] = os9
77+ -- let ![[Nothing, Nothing]] = os6 -- TODO re-enable this test after issue #233 has been resolved
78+ let !(Just [(1,"2")]) = M.toList <$> head (head os7)
79+ let !(Just [(1,"oyoy")]) = M.toList <$> head (head os8)
80+
8481 return ()
8582
86−unwrapApp :: forall t m a.
87− ( Reflex t
88− , MonadFix m
89− , PrimMonad m
90− )
83+unwrapRequest :: DSum tag RequestInt -> Int
84+unwrapRequest (_ :=> RequestInt i) = i
85+
86+unwrapApp :: ( Reflex t, Monad m )
9187 => (a -> RequesterT t RequestInt Identity m ())
9288 -> a
9389 -> m (Event t [Int])
9490unwrapApp x appIn = do
9591 ((), e) <- runRequesterT (x appIn) never
96− let unwrapRequests :: forall x. RequestData (PrimState m) RequestInt -> [Int]
97− unwrapRequests (RequestData _ es) = fmap (\(RequestEnvelope _ (RequestInt i)) -> i) $ toList es
98− return $ fmap unwrapRequests e
92+ return $ fmap (map unwrapRequest . requesterDataToList) e
9993
10094testOrdering :: ( Response m ~ Identity
10195 , Request m ~ RequestInt
@@ -206,11 +200,6 @@ data TestRequest a where
206200 TestRequest_Reverse :: String -> TestRequest String
207201 TestRequest_Increment :: Int -> TestRequest Int
208202
209−instance Show (TestRequest a) where
210− show = \case
211− TestRequest_Reverse str -> "reverse " <> str
212− TestRequest_Increment i -> "increment " <> show i
213−
214203testMatchRequestsWithResponses
215204 :: forall m t req a
216205 . ( MonadFix m
@@ -220,21 +209,17 @@ testMatchRequestsWithResponses
220209 , MonadIO (Performable m)
221210 , ForallF Show req
222211 , Has Read req
223− , PrimMonad m
224− , Show (req a)
225− , Show a
226− , MonadIO m
227212 )
228213 => Event t (req a) -> m (Event t (Map Int String))
229214testMatchRequestsWithResponses pulse = mdo
230215 (_, requests) <- runRequesterT (requesting pulse) responses
231− let rawResponseMap = M.map (\v ->
216+ let rawResponses = M.map (\v ->
232217 case words v of
233218 ["reverse", str] -> reverse str
234219 ["increment", i] -> show $ succ $ (read i :: Int)
235220 ) <$> rawRequestMap
236− (rawRequestMap, responses) <- matchResponsesWithRequests reqEncoder requests (head . M.toList <$> rawResponseMap)
237− pure rawResponseMap
221+ (rawRequestMap, responses) <- matchResponsesWithRequests reqEncoder requests (head . M.toList <$> rawResponses)
222+ pure rawResponses
238223 where
239224 reqEncoder :: forall a. req a -> (String, String -> Maybe a)
240225 reqEncoder r =
@@ -242,23 +227,9 @@ testMatchRequestsWithResponses pulse = mdo
242227 , \x -> has @Read r $ readMaybe x
243228 )
244229
245−-- If a widget is destroyed, and simultaneously it tries to use performEvent, the event does not get performed.
246−-- TODO Determine whether this is actually the behavior we want.
247−testMoribundPerformEvent
248− :: forall t m
249− . ( Adjustable t m
250− , PerformEvent t m
251− , MonadHold t m
252− , Reflex t
253− )
254− => Event t Int -> m (Event t String)
255−testMoribundPerformEvent pulse = do
256− (outputInitial, outputReplaced) <- runWithReplace (performPrint 0 pulse) $ ffor pulse $ \i -> performPrint i pulse
257− switchHold outputInitial outputReplaced
258− where
259− performPrint i evt =
260− performEvent $ ffor evt $ \output ->
261− return $ show i <> ":" <> show output
262−
263−
264230deriveArgDict ''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