on develop help sign in
Merge branch 'develop' into no-hlint-exceptions
commitf61a68d7a57dfe256464097cf8bde3a1b836fb5a authorJohn Ericson <John.Ericson@Obsidian.Systems> dateSat 14 Dec 2019, 13:55 (7 years ago) parent089d4e1ba7efb1
54 files changed+981−535
.travis.yml+171 CONTRIBUTING.md+9 ChangeLog.md+60 Quickref.md+14−11 cabal.haskell-ci+16 cabal.project+1 cabal.project.freeze+1 default.nix−39 dep/reflex-platform/default.nix+7 dep/reflex-platform/github.json+7 hydra.json−29 jobsets.nix−70 reflex-platform.nix−8 reflex.cabal+63−32 release.nix+53−7 src/Control/Monad/ReaderIO.hs+60 src/Data/AppendMap.hs+5−1 src/Data/FastWeakBag.hs+9−4 src/Data/Functor/Misc.hs+2−12 src/Data/WeakBag.hs+8−3 src/Reflex/Adjustable/Class.hs−1 src/Reflex/BehaviorWriter/Class.hs−1 src/Reflex/Class.hs+112−28 src/Reflex/Collection.hs+8−1 src/Reflex/Dynamic/TH.hs+1−1 src/Reflex/DynamicWriter/Class.hs−2 src/Reflex/EventWriter/Base.hs+1−1 src/Reflex/EventWriter/Class.hs−1 src/Reflex/Host/Class.hs−1 src/Reflex/Network.hs+1−1 src/Reflex/NotReady/Class.hs−1 src/Reflex/Patch/DMapWithMove.hs+6−7 src/Reflex/Patch/IntMap.hs−8 src/Reflex/Patch/Map.hs−8 src/Reflex/Patch/MapWithMove.hs+1−1 src/Reflex/PerformEvent/Base.hs+1−1 src/Reflex/PerformEvent/Class.hs−1 src/Reflex/PostBuild/Class.hs−1 src/Reflex/Profiled.hs+24−16 src/Reflex/Pure.hs+21−17 src/Reflex/Query/Base.hs+5−7 src/Reflex/Query/Class.hs−1 src/Reflex/Requester/Base.hs+2−3 src/Reflex/Requester/Class.hs−1 src/Reflex/Spider/Internal.hs+274−199 src/Reflex/TriggerEvent/Class.hs−1 src/Reflex/Workflow.hs+1−1 test/EventWriterT.hs+6−4 test/GC.hs+4−3 test/QueryT.hs+4 test/Reflex/Bench/Focused.hs+12 test/Reflex/Test/CrossImpl.hs+5 test/RequesterT.hs+5 test/hlint.hs+1
.travis.ymlnew file+171
@@ -0,0 +1,171 @@
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.3.20190814
8+#
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
15+cache:
16+ directories:
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
28+matrix:
29+ include:
30+ - compiler: ghcjs-8.4
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32+ - compiler: ghc-8.6.5
33+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
34+ - compiler: ghc-8.4.4
35+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
36+ - compiler: ghc-8.2.2
37+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
38+ - compiler: ghc-8.0.2
39+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
40+before_install:
41+ - |
42+ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
43+ sudo add-apt-repository -y ppa:hvr/ghcjs;
44+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
45+ sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
46+ sudo apt-get update;
47+ sudo apt-get install $CC cabal-install-3.0 nodejs;
48+ fi
49+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
50+ - WITHCOMPILER="-w $HC"
51+ - |
52+ if echo $CC | grep -q ghcjs; then
53+ GHCJS=true
54+ HC=${HC}js
55+ WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
56+ else
57+ GHCJS=false;
58+ fi
59+ - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
60+ - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
61+ - HCPKG="$HC-pkg"
62+ - unset CC
63+ - CABAL=/opt/ghc/bin/cabal
64+ - CABALHOME=$HOME/.cabal
65+ - export PATH="$CABALHOME/bin:$PATH"
66+ - TOP=$(pwd)
67+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
68+ - echo $HCNUMVER
69+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
70+ - set -o pipefail
71+ - |
72+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
73+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
74+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
75+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
76+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
77+ echo ' if (state == "cabal") {' >> .colorful.awk
78+ echo ' print blue($0)' >> .colorful.awk
79+ echo ' } else {' >> .colorful.awk
80+ echo ' print $0' >> .colorful.awk
81+ echo ' }' >> .colorful.awk
82+ echo '}' >> .colorful.awk
83+ - cat .colorful.awk
84+ - |
85+ color_cabal_output () {
86+ awk -f $TOP/.colorful.awk
87+ }
88+ - echo text | color_cabal_output
89+install:
90+ - ${CABAL} --version
91+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
92+ - node --version
93+ - echo $GHCJS
94+ - TEST=--enable-tests
95+ - BENCH=--enable-benchmarks
96+ - BENCH=--disable-benchmarks
97+ - HEADHACKAGE=false
98+ - rm -f $CABALHOME/config
99+ - |
100+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
101+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
102+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
103+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
104+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
105+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
106+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
107+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
108+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
109+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
110+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
111+ echo "install-dirs user" >> $CABALHOME/config
112+ echo " prefix: $CABALHOME" >> $CABALHOME/config
113+ echo "repository hackage.haskell.org" >> $CABALHOME/config
114+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
115+ - cat $CABALHOME/config
116+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
117+ - travis_retry ${CABAL} v2-update -v
118+ - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
119+ # Generate cabal.project
120+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
121+ - touch cabal.project
122+ - |
123+ echo "packages: ." >> cabal.project
124+ - |
125+ - cat cabal.project || true
126+ - cat cabal.project.local || true
127+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
128+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
129+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
130+ - rm cabal.project.freeze
131+script:
132+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
133+ # Packaging...
134+ - ${CABAL} v2-sdist all | color_cabal_output
135+ # Unpacking...
136+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
137+ - cd ${DISTDIR} || false
138+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
139+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
140+ - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')"
141+ # Generate cabal.project
142+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
143+ - touch cabal.project
144+ - |
145+ echo "packages: ${PKGDIR_reflex}" >> cabal.project
146+ - |
147+ - cat cabal.project || true
148+ - cat cabal.project.local || true
149+ # Building...
150+ # this builds all libraries and executables (without tests/benchmarks)
151+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
152+ # Building with tests and benchmarks...
153+ # build & run tests, build benchmarks
154+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
155+ # Testing...
156+ - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
157+ # cabal check...
158+ - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check)
159+ # haddock...
160+ - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
161+ # Constraint sets
162+ - rm -rf cabal.project.local
163+ # Constraint set no-th
164+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output
165+ # Constraint set old-these
166+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output
167+ # Constraint set old-witherable
168+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
169+
170+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
171+# EOF
CONTRIBUTING.md+9
@@ -5,6 +5,7 @@ Contributions and issue reports are encouraged and appreciated!
55- [Opening Issues](#opening-issues)
66- [Submitting Changes](#submitting-changes)
77 - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
89 - [Code Quality](#code-quality)
910 - [Documentation](#documentation)
1011
@@ -30,6 +31,10 @@ One way to think about it is that your commit message should be able to complete
3031#### Body
3132For breaking changes, new features, refactors, or other major changes, the body of the commit message should describe the motivation behind the change in greater detail and may include references to the issue tracker. The body shouldn't repeat code/comments from the diff.
3233
34+### Guidelines for Pull Requests
35+
36+Wherever possible, pull requests should add a single feature or fix a single bug. Pull requests should not bundle several unrelated changes.
37+
3338### Code Quality
3439
3540#### Warnings
@@ -40,6 +45,10 @@ Your pull request should add no new warnings to the project. It should also gene
4045
4146Make sure the project builds and that the tests pass! This will generally also be checked by CI before merge, but trying it yourself first means you'll catch problems earlier and your contribution can be merged that much sooner!
4247
48+#### Dependencies
49+
50+Include version bounds whenever adding a dependency to the library stanza of the cabal file. Note that libraries added to reflex.cabal also need to be added to default.nix.
51+
4352### Documentation
4453
4554#### In the code
ChangeLog.md+60
@@ -2,20 +2,80 @@
22
33## Unreleased
44
5+* Data.WeakBag.traverse and Data.FastWeakBag.traverse have been
6+ renamed to Data.WeakBag.traverse_ and Data.FastWeakBag.traverse_
7+ respectively.
8+
9+* Fixes a bug in `Reflex.Patch.MapWithMove.patchThatSortsMapWith` that was producing invalid `PatchMapWithMove`.
10+
11+* Add missing `NotReady` instances:
12+ - `instance NotReady (SpiderTimeline x) (SpiderHost x)`
13+ - `instance HasSpiderTimeline x => NotReady (SpiderTimeline x) (PerformEventT (SpiderTimeline x) (SpiderHost x))`
14+
15+## 0.6.2.4
16+
17+* Update to monoidal-containers 0.6
18+
19+## 0.6.2.3
20+
21+* Add an upper-bound to witherable
22+
23+## 0.6.2.2
24+
25+* Support these >= 1. Add `split-these` flag to control whether to use new these/semialign combination or not.
26+
27+* Update version bounds to fix some CI failures
28+
29+* Add travis CI configuration
30+
31+## 0.6.2.1
32+
33+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
34+ functor:
35+ * `fan` to `fanG`
36+ * `EventSelectorG` for `fanG` result selector.
37+
38+* Reduce the amount of unsafeCoerce in coercing newtypes under Event/Dynamic/Behavior.
39+ * Add fused ReaderIO for the purpose of coercion (ReaderT's third argument has nominal role preventing automated coerce)
40+ * Add incrementalCoercion/coerceIncremental to go with dynamicCoercion/coerceDynamic
41+
42+* Generalize merging functions:
43+ `merge` to `mergeG`,
44+ `mergeIncremental` to `mergeIncrementalG`,
45+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
46+
47+* Generalize distribute function:
48+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
49+
50+## 0.6.2.0
51+
552* Fix `holdDyn` so that it is lazy in its event argument
53+ These produce `DMap`s whose values needn't be `Identity`.
54+
55+* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
56+
57+* Fix `holdDyn` so that it is lazy in its event argument.
658
759## 0.6.1.0
860
961* Re-export all of `Data.Map.Monoidal`
62+
1063* Fix `QueryT` and `RequesterT` tests
1164
1265## 0.6.0.0 -- 2019-03-20
1366
1467* Deprecate `FunctorMaybe` in favor of `Data.Witherable.Filterable`. We still export `fmapMaybe`, `ffilter`, etc., but they all rely on `Filterable` now.
68+
1569* Rename `MonadDynamicWriter` to `DynamicWriter` and add a deprecation for the old name.
70+
1671* Remove many deprecated functions.
72+
1773* Add a `Num` instance for `Dynamic`.
74+
1875* Add `matchRequestsWithResponses` to make it easier to use `Requester` with protocols that don't do this matching for you.
76+
1977* Add `withRequesterT` to map functions over the request and response of a `RequesterT`.
78+
2079* Suppress nil patches in `QueryT` as an optimization. The `Query` type must now have an `Eq` instance.
80+
2181* Add `throttleBatchWithLag` to `Reflex.Time`. See that module for details.
Quickref.md+14−11
@@ -198,7 +198,7 @@ For Events, the returned Event fires whenever the latest Event supplied by the w
198198The functions mentioned above are used to create a static FRP network.
199199There are additional typeclasses that can be used to modify the FRP network, to have it interact with IO action, or to introspect the building of the network.
200200
201−Th typeclasses and their associated annotations include:
201+The typeclasses and their associated annotations include:
202202
203203- `PostBuild`
204204 Fire an Event when an FRP network has been set up.
@@ -206,7 +206,7 @@ Th typeclasses and their associated annotations include:
206206 [B] -- Function runs in any monad supporting PostBuild
207207 ```
208208
209−- `Adjustable`
209+- `Adjustable`
210210 Use Events to add or remove pieces of an FRP network.
211211 ```haskell
212212 [A] -- Function runs in any monad supporting Adjustable
@@ -271,7 +271,7 @@ Th typeclasses and their associated annotations include:
271271```haskell
272272-- Run side-effecting actions in Event when it occurs; returned Event contains
273273-- results. Side effects run in the (Performable m) monad which is associated with
274−-- the (PerformEvent t m) typeclass constraint.
274+-- the (PerformEvent t m) typeclass constraint.
275275-- This allows for working with IO when a ((MonadIO (Performable m)) constraint is available.
276276[P] performEvent :: Event (Performable m a ) -> m (Event a)
277277
@@ -295,17 +295,20 @@ Th typeclasses and their associated annotations include:
295295## Networks
296296
297297```haskell
298−-- Functions from Reflex.Network used to deal with Dynamics/Events carrying (m a)
298+-- Functions from Reflex.Adjustable / Reflex.Network used to deal with Dynamics/Events carrying (m a)
299299
300−-- Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
301−-- The returned Event of network results occurs when the Dynamic does. Note: Often, the type a is an Event,
300+[A] runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
301+
302+-- Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
303+-- The returned Event of network results occurs when the Dynamic does. Note: Often, the type a is an Event,
302304-- in which case the return value is an Event-of-Events that would typically be flattened (via switchHold).
303−[P,A] networkView :: Dynamic (m a) -> m (Event a)
305+[P,A] networkView :: Dynamic (m a) -> m (Event a)
304306
305−-- Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the
306−-- Event fires. The returned Dynamic of network results occurs when the Event does. Note: Often, the type a is an
307+-- Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the
308+-- Event fires. The returned Dynamic of network results occurs when the Event does. Note: Often, the type a is an
307309-- Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
308−[H,A] networkHold :: m a -> Event (m a) -> m (Dynamic a)
310+[H,A] networkHold :: m a -> Event (m a) -> m (Dynamic a)
309311
310312-- Render a placeholder network to be shown while another network is not yet done building
311−[P,A] untilReady :: m a -> m b -> m (a, Event b)
313+[P,A] untilReady :: m a -> m b -> m (a, Event b)
314+```
cabal.haskell-cinew file+16
@@ -0,0 +1,16 @@
1+distribution: xenial
2+benchmarks: False
3+unconstrained: False
4+installed: -all
5+
6+-- https://github.com/haskell/cabal/issues/6106
7+install-dependencies: False
8+
9+constraint-set no-th
10+ constraints: reflex -use-template-haskell
11+
12+constraint-set old-these
13+ constraints: these <1
14+
15+constraint-set old-witherable
16+ constraints: witherable <0.3.2
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
cabal.project.freezenew file+1
@@ -0,0 +1 @@
1+constraints: any.text < 1.2.4.0
default.nixdeleted−39
@@ -1,39 +0,0 @@
1−{ mkDerivation, ghc, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl, primitive, random, ref-tf
5−, semigroupoids , semigroups, split, stdenv, stm, syb
6−, template-haskell , these, time, transformers
7−, transformers-compat, unbounded-delays, prim-uniq
8−, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers, witherable
10−, useTemplateHaskell ? true
11−}:
12−mkDerivation {
13− pname = "reflex";
14− version = "0.6.1";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16− libraryHaskellDepends = [
17− base bifunctors containers dependent-map dependent-sum
18− exception-transformers lens
19− MemoTrie monad-control mtl primitive ref-tf semigroupoids
20− semigroups stm syb template-haskell these transformers
21− transformers-compat prim-uniq
22− base bifunctors containers deepseq dependent-map dependent-sum
23− mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers witherable
25− ] ++ (if ghc.isGhcjs or false then [
26− ghcjs-base
27− ] else []) ++ (if !useTemplateHaskell then [] else [
28− haskell-src-exts haskell-src-meta
29− ]);
30− testHaskellDepends = if ghc.isGhcjs or false then [] else [
31− hlint filepath directory filemanip
32− ];
33− configureFlags = if useTemplateHaskell then [] else [
34− "-f-use-template-haskell"
35− ];
36− homepage = "https://github.com/reflex-frp/reflex";
37− description = "Higher-order Functional Reactive Programming";
38− license = stdenv.lib.licenses.bsd3;
39−}
dep/reflex-platform/default.nixnew file+7
@@ -0,0 +1,7 @@
1+# DO NOT HAND-EDIT THIS FILE
2+import ((import <nixpkgs> {}).fetchFromGitHub (
3+ let json = builtins.fromJSON (builtins.readFile ./github.json);
4+ in { inherit (json) owner repo rev sha256;
5+ private = json.private or false;
6+ }
7+))
dep/reflex-platform/github.jsonnew file+7
@@ -0,0 +1,7 @@
1+{
2+ "owner": "reflex-frp",
3+ "repo": "reflex-platform",
4+ "branch": "develop",
5+ "rev": "e7b76dd552a10916c7d8702c11292dac4f4299ea",
6+ "sha256": "0s1183arrwldcs50qhzgnv94v24n9bgq6dfq64wp0a3q2nzyvgwh"
7+}
hydra.jsondeleted−29
@@ -1,29 +0,0 @@
1−{
2− "enabled": 1,
3− "hidden": true,
4− "description": "Jobsets",
5− "nixexprinput": "src",
6− "nixexprpath": "jobsets.nix",
7− "checkinterval": 300,
8− "schedulingshares": 100,
9− "enableemail": false,
10− "emailoverride": "",
11− "keepnr": 10,
12− "inputs": {
13− "src": {
14− "type": "git",
15− "value": "https://github.com/reflex-frp/reflex.git develop",
16− "emailresponsible": false
17− },
18− "nixpkgs": {
19− "type": "git",
20− "value": "https://github.com/NixOS/nixpkgs-channels nixos-unstable",
21− "emailresponsible": false
22− },
23− "prs": {
24− "type": "githubpulls",
25− "value": "reflex-frp reflex",
26− "emailresponsible": false
27− }
28− }
29−}
jobsets.nixdeleted−70
@@ -1,70 +0,0 @@
1−{ prs }:
2−
3−let
4− pkgs = (import ./reflex-platform.nix {}).nixpkgs;
5− mkFetchGithub = value: {
6− inherit value;
7− type = "git";
8− emailresponsible = false;
9− };
10−in
11−with pkgs.lib;
12−let
13− defaults = jobs: {
14− inherit (jobs) description;
15− enabled = 1;
16− hidden = false;
17− keepnr = 10;
18− schedulingshares = 100;
19− checkinterval = 120;
20− enableemail = false;
21− emailoverride = "";
22− nixexprinput = "reflex";
23− nixexprpath = "release.nix";
24− inputs = jobs.inputs // {
25− nixpkgs = {
26− type = "git";
27− value = "https://github.com/NixOS/nixpkgs-channels nixos-unstable";
28− emailresponsible = false;
29− };
30− config = {
31− type = "nix";
32− value = "{ android_sdk.accept_license = true; }";
33− emailresponsible = false;
34− };
35− };
36− };
37− branchJobset = branch: defaults {
38− description = "reflex-${branch}";
39− inputs = {
40− reflex = {
41− value = "https://github.com/reflex-frp/reflex ${branch}";
42− type = "git";
43− emailresponsible = false;
44− };
45− };
46− };
47− makePr = num: info: {
48− name = "reflex-pr-${num}";
49− value = defaults {
50− description = "#${num}: ${info.title}";
51− inputs = {
52− reflex = {
53− #NOTE: This should really use "pull/${num}/merge"; however, GitHub's
54− #status checks only operate on PR heads. This creates a race
55− #condition, which can currently only be solved by requiring PRs to be
56− #up to date before they're merged. See
57− #https://github.com/isaacs/github/issues/1002
58− value = "https://github.com/reflex-frp/reflex pull/${num}/head";
59− type = "git";
60− emailresponsible = false;
61− };
62− };
63− };
64− };
65− processedPrs = mapAttrs' makePr (builtins.fromJSON (builtins.readFile prs));
66− jobsetsAttrs = processedPrs //
67− genAttrs ["develop"] branchJobset;
68−in {
69− jobsets = pkgs.writeText "spec.json" (builtins.toJSON jobsetsAttrs);
70−}
reflex-platform.nixdeleted−8
@@ -1,8 +0,0 @@
1−let
2− reflex-platform-src = (import <nixpkgs> {}).fetchFromGitHub {
3− owner = "reflex-frp";
4− repo = "reflex-platform";
5− rev = "384cd850f3adf1d404bced2424b5f6efb0f415f2";
6− sha256 = "1ws77prqx8khmp8j6br1ij4k2v4dlgv170r9fmg0p1jivfbn8y9d";
7− };
8−in import reflex-platform-src
reflex.cabal+63−32
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.6.1
2+Version: 0.6.2.4
33Synopsis: Higher-order Functional Reactive Programming
44Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
55License: BSD3
@@ -17,6 +17,10 @@ extra-source-files:
1717 Quickref.md
1818 ChangeLog.md
1919
20+tested-with:
21+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5,
22+ GHCJS ==8.4
23+
2024flag use-reflex-optimizer
2125 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
2226 default: False
@@ -37,38 +41,50 @@ flag fast-weak
3741 default: False
3842 manual: True
3943
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
4049library
4150 hs-source-dirs: src
4251 build-depends:
4352 MemoTrie == 0.6.*,
4453 base >= 4.9 && < 4.13,
4554 bifunctors >= 5.2 && < 5.6,
46− comonad,
47− containers >= 0.5 && < 0.7,
55+ comonad >= 5.0.4 && < 5.1,
56+ constraints-extras >= 0.3 && < 0.4,
57+ containers >= 0.6 && < 0.7,
4858 data-default >= 0.5 && < 0.8,
49− dependent-map >= 0.2.4 && < 0.3,
59+ dependent-map >= 0.3 && < 0.4,
5060 exception-transformers == 0.4.*,
5161 lens >= 4.7 && < 5,
5262 monad-control >= 1.0.1 && < 1.1,
53− monoidal-containers == 0.4.*,
5463 mtl >= 2.1 && < 2.3,
5564 prim-uniq >= 0.1.0.1 && < 0.2,
56− primitive >= 0.5 && < 0.7,
65+ primitive >= 0.5 && < 0.8,
66+ profunctors >= 5.3 && < 5.5,
5767 random == 1.1.*,
5868 ref-tf == 0.4.*,
5969 reflection == 2.1.*,
6070 semigroupoids >= 4.0 && < 6,
61− semigroups >= 0.16 && < 0.19,
6271 stm >= 2.4 && < 2.6,
6372 syb >= 0.5 && < 0.8,
64− these >= 0.4 && < 0.9,
6573 time >= 1.4 && < 1.9,
66− transformers >= 0.2,
67− transformers-compat >= 0.3,
74+ transformers >= 0.5.6.0 && < 0.6,
6875 unbounded-delays >= 0.1.0.0 && < 0.2,
69− witherable >= 0.2 && < 0.4
76+ witherable >= 0.3 && < 0.3.2
77+
78+ if flag(split-these)
79+ build-depends: these >= 1 && <1.1,
80+ semialign >=1 && <1.2,
81+ monoidal-containers >= 0.6 && < 0.7
82+ else
83+ build-depends: these >= 0.4 && <0.9,
84+ monoidal-containers == 0.4.0.0
7085
7186 exposed-modules:
87+ Control.Monad.ReaderIO
7288 Data.AppendMap,
7389 Data.FastMutableIntMap,
7490 Data.FastWeakBag,
@@ -133,7 +149,7 @@ library
133149 if flag(use-template-haskell)
134150 cpp-options: -DUSE_TEMPLATE_HASKELL
135151 build-depends:
136− dependent-sum >= 0.3 && < 0.5,
152+ dependent-sum >= 0.6 && < 0.7,
137153 haskell-src-exts >= 1.16 && < 1.22,
138154 haskell-src-meta >= 0.6 && < 0.9,
139155 template-haskell >= 2.9 && < 2.15
@@ -142,7 +158,7 @@ library
142158 other-extensions: TemplateHaskell
143159 else
144160 build-depends:
145− dependent-sum == 0.4.*
161+ dependent-sum >= 0.6 && < 0.7
146162
147163 if flag(fast-weak) && impl(ghcjs)
148164 cpp-options: -DGHCJS_FAST_WEAK
@@ -159,14 +175,14 @@ test-suite semantics
159175 base,
160176 bifunctors,
161177 containers,
162− deepseq >= 1.3 && < 1.5,
178+ deepseq,
163179 dependent-map,
164180 dependent-sum,
165181 mtl,
166182 ref-tf,
167183 reflex,
168184 split,
169− transformers >= 0.3
185+ transformers
170186 other-modules:
171187 Reflex.Bench.Focused
172188 Reflex.Plan.Pure
@@ -185,7 +201,7 @@ test-suite CrossImpl
185201 containers,
186202 dependent-map,
187203 dependent-sum,
188− deepseq >= 1.3 && < 1.5,
204+ deepseq,
189205 mtl,
190206 transformers,
191207 ref-tf,
@@ -204,7 +220,7 @@ test-suite hlint
204220 , directory
205221 , filepath
206222 , filemanip
207− , hlint
223+ , hlint < 2.1 || >= 2.2.2
208224 if impl(ghcjs)
209225 buildable: False
210226
@@ -214,7 +230,7 @@ test-suite EventWriterT
214230 hs-source-dirs: test
215231 build-depends: base
216232 , containers
217− , deepseq >= 1.3 && < 1.5
233+ , deepseq
218234 , dependent-map
219235 , dependent-sum
220236 , lens
@@ -223,6 +239,10 @@ test-suite EventWriterT
223239 , transformers
224240 , reflex
225241 , ref-tf
242+
243+ if flag(split-these)
244+ build-depends: these-lens
245+
226246 other-modules:
227247 Reflex.Test
228248 Reflex.TestPlan
@@ -230,13 +250,14 @@ test-suite EventWriterT
230250 Reflex.Plan.Pure
231251 Test.Run
232252
253+
233254test-suite RequesterT
234255 type: exitcode-stdio-1.0
235256 main-is: RequesterT.hs
236257 hs-source-dirs: test
237258 build-depends: base
238259 , containers
239− , deepseq >= 1.3 && < 1.5
260+ , deepseq
240261 , dependent-sum
241262 , dependent-map
242263 , lens
@@ -245,9 +266,14 @@ test-suite RequesterT
245266 , transformers
246267 , reflex
247268 , ref-tf
269+
270+ if flag(split-these)
271+ build-depends: these-lens
272+
248273 other-modules:
249274 Reflex.TestPlan
250275 Reflex.Plan.Pure
276+ Test.Run
251277
252278test-suite QueryT
253279 type: exitcode-stdio-1.0
@@ -257,15 +283,18 @@ test-suite QueryT
257283 , containers
258284 , dependent-map
259285 , dependent-sum
260− , deepseq >= 1.3 && < 1.5
286+ , deepseq
261287 , lens
262288 , monoidal-containers
263289 , mtl
264290 , ref-tf
265291 , reflex
266− , semigroups
267292 , these
268293 , transformers
294+
295+ if flag(split-these)
296+ build-depends: semialign, these-lens
297+
269298 other-modules:
270299 Test.Run
271300 Reflex.TestPlan
@@ -280,14 +309,16 @@ test-suite GC-Semantics
280309 , containers
281310 , dependent-sum
282311 , dependent-map
283− , deepseq >= 1.3 && < 1.5
312+ , deepseq
284313 , mtl
285314 , these
286315 , transformers
287316 , reflex
288317 , ref-tf
289− if impl(ghc < 8)
290− build-depends: semigroups
318+
319+ if flag(split-these)
320+ build-depends: semialign
321+
291322 other-modules:
292323 Reflex.Plan.Pure
293324 Reflex.Plan.Reflex
@@ -300,7 +331,7 @@ test-suite rootCleanup
300331 hs-source-dirs: test
301332 build-depends: base
302333 , containers
303− , deepseq >= 1.3 && < 1.5
334+ , deepseq
304335 , dependent-sum
305336 , mtl
306337 , reflex
@@ -319,8 +350,8 @@ benchmark spider-bench
319350 build-depends:
320351 base,
321352 containers,
322− criterion >= 1.1 && < 1.6,
323− deepseq >= 1.3 && < 1.5,
353+ criterion,
354+ deepseq,
324355 dependent-map,
325356 dependent-sum,
326357 ref-tf,
@@ -329,7 +360,7 @@ benchmark spider-bench
329360 reflex,
330361 split,
331362 stm,
332− transformers >= 0.3
363+ transformers
333364 other-modules:
334365 Reflex.TestPlan
335366 Reflex.Plan.Reflex
@@ -343,9 +374,9 @@ benchmark saulzar-bench
343374 ghc-options: -Wall -O2 -rtsopts -threaded
344375 build-depends:
345376 base,
346− containers >= 0.5 && < 0.7,
347− criterion >= 1.1 && < 1.6,
348− deepseq >= 1.3 && < 1.5,
377+ containers,
378+ criterion,
379+ deepseq,
349380 dependent-map,
350381 dependent-sum,
351382 loch-th,
@@ -357,7 +388,7 @@ benchmark saulzar-bench
357388 split,
358389 stm,
359390 time,
360− transformers >= 0.3
391+ transformers
361392 other-modules:
362393 Reflex.TestPlan
363394 Reflex.Plan.Reflex
release.nix+53−7
@@ -1,9 +1,55 @@
1−{ rp ? import ./reflex-platform.nix {}
1+{ reflex-platform-fun ? import ./dep/reflex-platform
22}:
3+
34let
4− inherit (rp.nixpkgs) lib;
5− compilers = ["ghc8_4" "ghc8_0" "ghcjs8_4" "ghcjs8_0"];
6−in lib.genAttrs compilers (ghc: {
7− reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; };
8− reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; };
9−})
5+ native-reflex-platform = reflex-platform-fun {};
6+ inherit (native-reflex-platform.nixpkgs) lib;
7+ systems = ["x86_64-linux" "x86_64-darwin"];
8+
9+ perPlatform = lib.genAttrs systems (system: let
10+ reflex-platform = reflex-platform-fun { inherit system; };
11+ compilers = [
12+ "ghc"
13+ "ghcjs"
14+ ] ++ lib.optionals (reflex-platform.androidSupport) [
15+ "ghcAndroidAarch64"
16+ "ghcAndroidAarch32"
17+ ] ++ lib.optionals (reflex-platform.iosSupport) [
18+ "ghcIosAarch64"
19+ ];
20+ variations = map (v: "reflex" + v) [
21+ "-dontUseTemplateHaskell"
22+ ""
23+ ];
24+ compilerPkgs = lib.genAttrs compilers (ghc: let
25+ variationPkgs = lib.genAttrs variations (variation: let
26+ reflex-platform = reflex-platform-fun {
27+ inherit system;
28+ __useTemplateHaskell = variation == "reflex"; # TODO hack
29+ haskellOverlays = [
30+ # Use this package's source for reflex
31+ (self: super: {
32+ _dep = super._dep // {
33+ reflex = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
34+ "release.nix"
35+ ".git"
36+ "dist"
37+ ])) ./.;
38+ };
39+ })
40+ ];
41+ };
42+ in reflex-platform.${ghc}.reflex);
43+ in variationPkgs // {
44+ cache = reflex-platform.pinBuildInputs "reflex-${system}-${ghc}"
45+ (builtins.attrValues variationPkgs);
46+ });
47+ in compilerPkgs // {
48+ cache = reflex-platform.pinBuildInputs "reflex-${system}"
49+ (map (a: a.cache) (builtins.attrValues compilerPkgs));
50+ });
51+
52+ metaCache = native-reflex-platform.pinBuildInputs "reflex-everywhere"
53+ (map (a: a.cache) (builtins.attrValues perPlatform));
54+
55+in perPlatform // { inherit metaCache; }
src/Control/Monad/ReaderIO.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# language RoleAnnotations #-}
2+{-# language MultiParamTypeClasses #-}
3+{-# language FlexibleInstances #-}
4+{-# language CPP #-}
5+module Control.Monad.ReaderIO
6+ (
7+ ReaderIO (..)
8+ )
9+ where
10+
11+import Control.Monad.Fix
12+#if MIN_VERSION_base(4,10,0)
13+import Control.Applicative
14+#endif
15+import Control.Monad
16+import Control.Monad.Reader.Class
17+import Control.Monad.IO.Class
18+
19+-- | An approximate clone of @RIO@ from the @rio@ package, but not based on
20+-- @ReaderT@. The trouble with @ReaderT@ is that its third type argument has a
21+-- @nominal@ role, so we can't coerce through it when it's wrapped in some
22+-- other @data@ type. Ugh.
23+newtype ReaderIO e a = ReaderIO { runReaderIO :: e -> IO a }
24+type role ReaderIO representational representational
25+
26+instance Functor (ReaderIO e) where
27+ fmap = liftM
28+ {-# INLINE fmap #-}
29+ a <$ m = m >> pure a
30+ {-# INLINE (<$) #-}
31+
32+instance Applicative (ReaderIO e) where
33+ pure a = ReaderIO $ \_ -> pure a
34+ {-# INLINE pure #-}
35+ (<*>) = ap
36+ {-# INLINE (<*>) #-}
37+#if MIN_VERSION_base(4,10,0)
38+ liftA2 = liftM2
39+ {-# INLINE liftA2 #-}
40+#endif
41+ (*>) = (>>)
42+ {-# INLINE (*>) #-}
43+
44+instance Monad (ReaderIO e) where
45+ ReaderIO q >>= f = ReaderIO $ \e -> q e >>= \a -> runReaderIO (f a) e
46+ {-# INLINE (>>=) #-}
47+
48+instance MonadFix (ReaderIO e) where
49+ mfix f = ReaderIO $ \e -> mfix $ \r -> runReaderIO (f r) e
50+ {-# INLINE mfix #-}
51+
52+instance MonadIO (ReaderIO e) where
53+ liftIO m = ReaderIO $ \_ -> m
54+ {-# INLINE liftIO #-}
55+
56+instance MonadReader e (ReaderIO e) where
57+ ask = ReaderIO pure
58+ {-# INLINE ask #-}
59+ local f (ReaderIO m) = ReaderIO (m . f)
60+ {-# INLINE local #-}
src/Data/AppendMap.hs+5−1
@@ -32,6 +32,8 @@ import qualified Data.Map as Map (showTree, showTreeWith)
3232#endif
3333import qualified Data.Witherable as W
3434import Data.Map.Monoidal
35+import qualified Data.Map.Monoidal as MonoidalMap
36+
3537
3638{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
3739-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
@@ -46,8 +48,10 @@ _unAppendMap = getMonoidalMap
4648pattern AppendMap :: Map k v -> MonoidalMap k v
4749pattern AppendMap m = MonoidalMap m
4850
51+#if !MIN_VERSION_witherable(0,3,2)
4952instance W.Filterable (MonoidalMap k) where
50− mapMaybe = mapMaybe
53+ mapMaybe = MonoidalMap.mapMaybe
54+#endif
5155
5256-- | Deletes a key, returning 'Nothing' if the result is empty.
5357nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
src/Data/FastWeakBag.hs+9−4
@@ -18,6 +18,7 @@ module Data.FastWeakBag
1818 , isEmpty
1919 , insert
2020 , traverse
21+ , traverse_
2122 , remove
2223 -- * Internal functions
2324 -- These will not always be available.
@@ -116,14 +117,14 @@ foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.len
116117isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117118#endif
118119
119−{-# INLINE traverse #-}
120+{-# INLINE traverse_ #-}
120121-- | Visit every node in the given list. If new nodes are appended during the
121122-- traversal, they will not be visited. Every live node that was in the list
122123-- when the traversal began will be visited exactly once; however, no guarantee
123124-- is made about the order of the traversal.
124−traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125+traverse_ :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125126#ifdef GHCJS_FAST_WEAK
126−traverse wb f = do
127+traverse_ wb f = do
127128 let go cursor = when (not $ js_isNull cursor) $ do
128129 val <- liftIO $ js_getTicketValue cursor
129130 f $ unsafeFromRawJSVal val
@@ -134,13 +135,17 @@ foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO
134135--TODO: Fix the race condition where if a cursor is deleted (presumably using 'remove', below) while we're holding it, it can't find its way back to the correct bag
135136foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
136137#else
137−traverse (FastWeakBag _ children) f = {-# SCC "traverse" #-} do
138+traverse_ (FastWeakBag _ children) f = {-# SCC "traverse_" #-} do
138139 cs <- liftIO $ readIORef children
139140 forM_ cs $ \c -> do
140141 ma <- liftIO $ deRefWeak c
141142 mapM_ f ma
142143#endif
143144
145+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
146+traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
147+traverse = traverse_
148+
144149-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
145150-- on the same 'FastWeakBagTicket'.
146151{-# INLINE remove #-}
src/Data/Functor/Misc.hs+2−12
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE LambdaCase #-}
98{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE PatternSynonyms #-}
@@ -52,8 +51,7 @@ import Data.IntMap (IntMap)
5251import qualified Data.IntMap as IntMap
5352import Data.Map (Map)
5453import qualified Data.Map as Map
55−import Data.Some (Some)
56−import qualified Data.Some as Some
54+import Data.Some (Some(Some))
5755import Data.These
5856import Data.Typeable hiding (Refl)
5957
@@ -79,9 +77,6 @@ deriving instance Read k => Read (Const2 k v v)
7977instance Show k => GShow (Const2 k v) where
8078 gshowsPrec n x@(Const2 _) = showsPrec n x
8179
82−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
83− showTaggedPrec (Const2 _) = showsPrec
84−
8580instance Eq k => GEq (Const2 k v) where
8681 geq (Const2 a) (Const2 b) =
8782 if a == b
@@ -124,7 +119,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
124119-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
125120-- the keys, using a function to remove the wrapping 'Functor'
126121weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
127−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f v)) . DMap.toAscList
122+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some k, f v)) . DMap.toAscList
128123
129124--------------------------------------------------------------------------------
130125-- WrapArg
@@ -214,11 +209,6 @@ instance GShow (EitherTag l r) where
214209 LeftTag -> showString "LeftTag"
215210 RightTag -> showString "RightTag"
216211
217−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
218− showTaggedPrec t n (Identity a) = case t of
219− LeftTag -> showsPrec n a
220− RightTag -> showsPrec n a
221−
222212-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
223213eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
224214eitherToDSum = \case
src/Data/WeakBag.hs+8−3
@@ -15,6 +15,7 @@ module Data.WeakBag
1515 , singleton
1616 , insert
1717 , traverse
18+ , traverse_
1819 , remove
1920 -- * Internal functions
2021 -- These will not always be available.
@@ -99,18 +100,22 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99100 ticket <- insert a bag wbRef finalizer
100101 return (bag, ticket)
101102
102−{-# INLINE traverse #-}
103+{-# INLINE traverse_ #-}
103104-- | Visit every node in the given list. If new nodes are appended during the
104105-- traversal, they will not be visited. Every live node that was in the list
105106-- when the traversal began will be visited exactly once; however, no guarantee
106107-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
108+traverse_ :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109+traverse_ (WeakBag _ children) f = {-# SCC "traverse" #-} do
109110 cs <- liftIO $ readIORef children
110111 forM_ cs $ \c -> do
111112 ma <- liftIO $ deRefWeak c
112113 mapM_ f ma
113114
115+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
116+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
117+traverse = traverse_
118+
114119-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115120-- on the same 'WeakBagTicket'.
116121{-# INLINE remove #-}
src/Reflex/Adjustable/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
src/Reflex/BehaviorWriter/Class.hs−1
@@ -5,7 +5,6 @@ Description: This module defines the 'MonadBehaviorWriter' class
55{-# LANGUAGE CPP #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE UndecidableInstances #-}
109#ifdef USE_REFLEX_OPTIMIZER
1110{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Class.hs+112−28
@@ -7,7 +7,6 @@
77{-# LANGUAGE FunctionalDependencies #-}
88{-# LANGUAGE GADTs #-}
99{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
1110{-# LANGUAGE NoMonomorphismRestriction #-}
1211{-# LANGUAGE PolyKinds #-}
1312{-# LANGUAGE RankNTypes #-}
@@ -17,6 +16,7 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
2020#ifdef USE_REFLEX_OPTIMIZER
2121{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2222#endif
@@ -36,16 +36,21 @@ module Reflex.Class
3636 , coerceBehavior
3737 , coerceEvent
3838 , coerceDynamic
39+ , coerceIncremental
3940 , MonadSample (..)
4041 , MonadHold (..)
4142 -- ** 'fan' related types
4243 , EventSelector (..)
44+ , EventSelectorG (..)
4345 , EventSelectorInt (..)
4446 -- * Convenience functions
4547 , constDyn
4648 , pushAlways
4749 -- ** Combining 'Event's
4850 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4954 , mergeMap
5055 , mergeIntMap
5156 , mergeMapIncremental
@@ -60,6 +65,7 @@ module Reflex.Class
6065 , alignEventWithMaybe
6166 -- ** Breaking up 'Event's
6267 , splitE
68+ , fan
6369 , fanEither
6470 , fanThese
6571 , fanMap
@@ -84,6 +90,7 @@ module Reflex.Class
8490 , gate
8591 -- ** Combining 'Dynamic's
8692 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
8794 , distributeListOverDyn
8895 , distributeListOverDynWith
8996 , zipDyn
@@ -166,6 +173,17 @@ module Reflex.Class
166173 , slowHeadE
167174 ) where
168175
176+#ifdef MIN_VERSION_semialign
177+import Prelude hiding (zip, zipWith)
178+
179+#if MIN_VERSION_these(0,8,0)
180+import Data.These.Combinators (justThese)
181+#endif
182+#if MIN_VERSION_semialign(1,1,0)
183+import Data.Zip (Zip (..))
184+#endif
185+#endif
186+
169187import Control.Applicative
170188import Control.Monad.Identity
171189import Control.Monad.Reader
@@ -193,8 +211,7 @@ import qualified Data.IntMap.Strict as IntMap
193211import Data.List.NonEmpty (NonEmpty (..))
194212import Data.Map (Map)
195213import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
196−import Data.Some (Some)
197−import qualified Data.Some as Some
214+import Data.Some (Some(Some))
198215import Data.String
199216import Data.These
200217import Data.Type.Coercion
@@ -256,11 +273,16 @@ class ( MonadHold t (PushM t)
256273 -- | Merge a collection of events; the resulting 'Event' will only occur if at
257274 -- least one input event is occurring, and will contain all of the input keys
258275 -- that are occurring simultaneously
259− merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
276+
277+ --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
278+ mergeG :: GCompare k => (forall a. q a -> Event t (v a))
279+ -> DMap k q -> Event t (DMap k v)
280+
260281 -- | Efficiently fan-out an event to many destinations. You should save the
261− -- result in a @let@-binding, and then repeatedly 'select' on the result to
282+ -- result in a @let@-binding, and then repeatedly 'selectG' on the result to
262283 -- create child events
263− fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k
284+ fanG :: GCompare k => Event t (DMap k v) -> EventSelectorG t k v
285+
264286 -- | Create an 'Event' that will occur whenever the currently-selected input
265287 -- 'Event' occurs
266288 switch :: Behavior t (Event t a) -> Event t a
@@ -278,9 +300,14 @@ class ( MonadHold t (PushM t)
278300 -- that value.
279301 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
280302 -- | Create a merge whose parents can change over time
281− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
303+ mergeIncrementalG :: GCompare k
304+ => (forall a. q a -> Event t (v a))
305+ -> Incremental t (PatchDMap k q)
306+ -> Event t (DMap k v)
282307 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
283− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
308+ mergeIncrementalWithMoveG :: GCompare k
309+ => (forall a. q a -> Event t (v a))
310+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
284311 -- | Extract the 'Behavior' component of an 'Incremental'
285312 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
286313 -- | Extract the 'Event' component of an 'Incremental'
@@ -296,9 +323,25 @@ class ( MonadHold t (PushM t)
296323 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
297324 -- occurrence type
298325 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
326+ -- | Construct a 'Coercion' for an 'Incremental' given 'Coercion's for its
327+ -- patch target and patch types.
328+ incrementalCoercion
329+ :: Coercion (PatchTarget a) (PatchTarget b) -> Coercion a b -> Coercion (Incremental t a) (Incremental t b)
299330 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
300331 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
301332
333+-- | Efficiently fan-out an event to many destinations. You should save the
334+-- result in a @let@-binding, and then repeatedly 'select' on the result to
335+-- create child events
336+fan :: forall t k. (Reflex t, GCompare k)
337+ => Event t (DMap k Identity) -> EventSelector t k
338+ --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
339+fan e = EventSelector (fixup (selectG (fanG e) :: k a -> Event t (Identity a)) :: forall a. k a -> Event t a)
340+ where
341+ fixup :: forall a. (k a -> Event t (Identity a)) -> k a -> Event t a
342+ fixup = case eventCoercion Coercion :: Coercion (Event t (Identity a)) (Event t a) of
343+ Coercion -> coerce
344+
302345--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
303346-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
304347-- keys simultaneously.
@@ -317,6 +360,12 @@ coerceEvent = coerceWith $ eventCoercion Coercion
317360coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
318361coerceDynamic = coerceWith $ dynamicCoercion Coercion
319362
363+-- | Coerce an 'Incremental' between representationally-equivalent value types
364+coerceIncremental
365+ :: (Reflex t, Coercible a b, Coercible (PatchTarget a) (PatchTarget b))
366+ => Incremental t a -> Incremental t b
367+coerceIncremental = coerceWith $ incrementalCoercion Coercion Coercion
368+
320369-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
321370-- __must__ change when and only when the 'Event' fires, such that the
322371-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
@@ -486,6 +535,17 @@ newtype EventSelector t k = EventSelector
486535 select :: forall a. k a -> Event t a
487536 }
488537
538+newtype EventSelectorG t k v = EventSelectorG
539+ { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
540+ -- determined by the type of the key, so this can be used to fan-out
541+ -- 'Event's whose sub-'Event's have different types.
542+ --
543+ -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
544+ -- (but equivalent to) using 'mapMaybe' to select only the relevant
545+ -- occurrences of an 'Event'.
546+ selectG :: forall a. k a -> Event t (v a)
547+ }
548+
489549-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
490550-- filtering by key.
491551newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
@@ -630,11 +690,7 @@ instance (Num a, Reflex t) => Num (Dynamic t a) where
630690instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
631691 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
632692 sconcat = pull . fmap sconcat . mapM sample
633−#if MIN_VERSION_semigroups(0,17,0)
634693 stimes n = fmap $ stimes n
635−#else
636− times1p n = fmap $ times1p n
637−#endif
638694
639695-- | Alias for 'mapMaybe'
640696fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
@@ -661,9 +717,12 @@ filterRight = mapMaybe (either (const Nothing) Just)
661717instance Reflex t => Alt (Event t) where
662718 ev1 <!> ev2 = leftmost [ev1, ev2]
663719
664−-- | 'Event' intersection (convenient interface to 'coincidence').
720+-- | 'Event' intersection. Only occurs when both events are co-incident.
665721instance Reflex t => Apply (Event t) where
666− evf <.> evx = coincidence (fmap (<$> evx) evf)
722+ evf <.> evx = mapMaybe f (align evf evx) where
723+ f (These g a) = Just (g a)
724+ f _ = Nothing
725+
667726
668727-- | 'Event' intersection (convenient interface to 'coincidence').
669728instance Reflex t => Bind (Event t) where
@@ -832,11 +891,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
832891instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
833892 (<>) = alignWith (mergeThese (<>))
834893 sconcat = fmap sconcat . mergeList . toList
835−#if MIN_VERSION_semigroups(0,17,0)
836894 stimes n = fmap $ stimes n
837−#else
838− times1p n = fmap $ times1p n
839−#endif
840895
841896instance (Semigroup a, Reflex t) => Monoid (Event t a) where
842897 mempty = never
@@ -1025,6 +1080,13 @@ instance Reflex t => Semialign (Event t) where
10251080#endif
10261081 align = alignEventWithMaybe Just
10271082
1083+#ifdef MIN_VERSION_semialign
1084+#if MIN_VERSION_semialign(1,1,0)
1085+instance Reflex t => Zip (Event t) where
1086+#endif
1087+ zip x y = mapMaybe justThese $ align x y
1088+#endif
1089+
10281090
10291091-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
10301092-- the 'Behavior' is true at the time of occurrence.
@@ -1065,11 +1127,7 @@ zipDynWith f da db =
10651127
10661128instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
10671129 (<>) = zipDynWith (<>)
1068−#if MIN_VERSION_semigroups(0,17,0)
10691130 stimes n = fmap $ stimes n
1070−#else
1071− times1p n = fmap $ times1p n
1072−#endif
10731131
10741132instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
10751133 mconcat = distributeListOverDynWith mconcat
@@ -1080,12 +1138,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
10801138-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
10811139-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
10821140distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1083−distributeDMapOverDynPure dm = case DMap.toList dm of
1141+distributeDMapOverDynPure = distributeDMapOverDynPureG coerceDynamic
1142+
1143+-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
1144+-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1145+-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1146+distributeDMapOverDynPureG
1147+ :: forall t k q v. (Reflex t, GCompare k)
1148+ => (forall a. q a -> Dynamic t (v a))
1149+ -> DMap k q -> Dynamic t (DMap k v)
1150+distributeDMapOverDynPureG nt dm = case DMap.toList dm of
10841151 [] -> constDyn DMap.empty
1085− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1152+ [k :=> v] -> DMap.singleton k <$> nt v
10861153 _ ->
1087− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1088− edmPre = merge $ DMap.map updated dm
1154+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
1155+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
10891156 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
10901157 olds <- sample $ current result
10911158 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -1155,9 +1222,9 @@ factorEvent
11551222 -> Event t (DSum k v)
11561223 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
11571224factorEvent k0 kv' = do
1158− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1225+ key :: Behavior t (Some k) <- hold (Some k0) $ fmapCheap (\(k :=> _) -> Some k) kv'
11591226 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1160− Some.This oldKey -> case newKey `geq` oldKey of
1227+ Some oldKey -> case newKey `geq` oldKey of
11611228 Just Refl -> return Nothing
11621229 Nothing -> do
11631230 newInner <- filterEventKey newKey kv'
@@ -1558,6 +1625,23 @@ fmapCheap f = pushCheap $ return . Just . f
15581625tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
15591626tagCheap b = pushAlwaysCheap $ \_ -> sample b
15601627
1628+-- | Merge a collection of events; the resulting 'Event' will only occur if at
1629+-- least one input event is occurring, and will contain all of the input keys
1630+-- that are occurring simultaneously
1631+merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
1632+merge = mergeG coerceEvent
1633+{-# INLINE merge #-}
1634+
1635+-- | Create a merge whose parents can change over time
1636+mergeIncremental :: (Reflex t, GCompare k)
1637+ => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
1638+mergeIncremental = mergeIncrementalG coerceEvent
1639+
1640+-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
1641+mergeIncrementalWithMove :: (Reflex t, GCompare k)
1642+ => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
1643+mergeIncrementalWithMove = mergeIncrementalWithMoveG coerceEvent
1644+
15611645-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
15621646{-# INLINE mergeWithCheap #-}
15631647mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
src/Reflex/Collection.hs+8−1
@@ -27,6 +27,13 @@ module Reflex.Collection
2727 , simpleList
2828 ) where
2929
30+#ifdef MIN_VERSION_semialign
31+import Prelude hiding (zip, zipWith)
32+#if MIN_VERSION_semialign(1,1,0)
33+import Data.Zip (Zip (..))
34+#endif
35+#endif
36+
3037import Control.Monad.Identity
3138import Data.Align
3239import Data.Functor.Misc
@@ -123,7 +130,7 @@ listWithKeyShallowDiff initialVals valsChanged mkChild = do
123130 Nothing -> Just Nothing
124131
125132 -- We don't want to let spurious re-creations of items through
126− Just _ -> Nothing
133+ Just _ -> Nothing
127134 listHoldWithKey
128135 initialVals
129136 (attachWith (flip (Map.differenceWith relevantPatch))
src/Reflex/Dynamic/TH.hs+1−1
@@ -81,7 +81,7 @@ mkDynPure = QuasiQuoter
8181 }
8282
8383mkDynExp :: String -> Q Exp
84−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
84+mkDynExp s = case Hs.parseExpWithMode Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] } s of
8585 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
8686 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
8787 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
src/Reflex/DynamicWriter/Class.hs−2
@@ -3,7 +3,6 @@
33{-# LANGUAGE CPP #-}
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE UndecidableInstances #-}
87#ifdef USE_REFLEX_OPTIMIZER
98{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
@@ -27,4 +26,3 @@ class (Monad m, Monoid w) => DynamicWriter t w m | m -> t w where
2726
2827instance DynamicWriter t w m => DynamicWriter t w (ReaderT r m) where
2928 tellDyn = lift . tellDyn
30−
src/Reflex/EventWriter/Base.hs+1−1
@@ -52,7 +52,7 @@ import qualified Data.Map as Map
5252import Data.Semigroup
5353import Data.Some (Some)
5454import Data.Tuple
55−import Data.Type.Equality hiding (apply)
55+import Data.Type.Equality
5656
5757import Unsafe.Coerce
5858
src/Reflex/EventWriter/Class.hs−1
@@ -2,7 +2,6 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76#ifdef USE_REFLEX_OPTIMIZER
87{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE RankNTypes #-}
98{-# LANGUAGE RoleAnnotations #-}
109{-# LANGUAGE ScopedTypeVariables #-}
src/Reflex/Network.hs+1−1
@@ -20,7 +20,7 @@ import Reflex.NotReady.Class
2020import Reflex.PostBuild.Class
2121
2222-- | A 'Dynamic' "network": Takes a 'Dynamic' of network-creating actions and replaces the network whenever the 'Dynamic' updates.
23−-- The returned Event of network results fires when the 'Dynamic' updates.
23+-- The returned Event of network results fires at post-build time and when the 'Dynamic' updates.
2424-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events, where the outer 'Event' fires
2525-- when switching networks. Such an 'Event' would typically be flattened (via 'switchPromptly').
2626networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
src/Reflex/NotReady/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleContexts #-}
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
98#ifdef USE_REFLEX_OPTIMIZER
src/Reflex/Patch/DMapWithMove.hs+6−7
@@ -19,9 +19,9 @@ import Reflex.Patch.Class
1919import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
2020import qualified Reflex.Patch.MapWithMove as MapWithMove
2121
22+import Data.Constraint.Extras
2223import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
2324import qualified Data.Dependent.Map as DMap
24−import Data.Dependent.Sum (EqTag (..))
2525import Data.Functor.Constant
2626import Data.Functor.Misc
2727import Data.Functor.Product
@@ -30,8 +30,7 @@ import Data.GADT.Show (GShow, gshow)
3030import qualified Data.Map as Map
3131import Data.Maybe
3232import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some)
34−import qualified Data.Some as Some
33+import Data.Some (Some(Some))
3534import Data.These
3635
3736-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
@@ -105,8 +104,8 @@ validationErrorsForPatchDMapWithMove m =
105104 unbalancedMove _ = Nothing
106105
107106-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
108−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
109− PatchDMapWithMove a == PatchDMapWithMove b = a == b
107+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
108+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
110109
111110-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
112111data Pair1 f g a = Pair1 (f a) (g a)
@@ -311,8 +310,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
311310 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
312311 From_Insert v -> MapWithMove.From_Insert $ f v
313312 From_Delete -> MapWithMove.From_Delete
314− From_Move k -> MapWithMove.From_Move $ Some.This k
315− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
313+ From_Move k -> MapWithMove.From_Move $ Some k
314+ , MapWithMove._nodeInfo_to = Some <$> getComposeMaybe (_nodeInfo_to ni)
316315 }
317316
318317-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
src/Reflex/Patch/IntMap.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE DeriveFoldable #-}
32{-# LANGUAGE DeriveFunctor #-}
43{-# LANGUAGE DeriveTraversable #-}
@@ -33,14 +32,7 @@ instance Patch (PatchIntMap a) where
3332instance Semigroup (PatchIntMap v) where
3433 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3534 -- PatchMap is idempotent, so stimes n is id for every n
36−#if MIN_VERSION_semigroups(0,17,0)
3735 stimes = stimesIdempotentMonoid
38−#else
39− times1p n x = case compare n 0 of
40− LT -> error "stimesIdempotentMonoid: negative multiplier"
41− EQ -> mempty
42− GT -> x
43−#endif
4436
4537-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
4638-- (that is, all inserts/updates), producing a @PatchIntMap b@.
src/Reflex/Patch/Map.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
21{-# LANGUAGE LambdaCase #-}
32{-# LANGUAGE TypeFamilies #-}
43-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
@@ -35,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
3534instance Ord k => Semigroup (PatchMap k v) where
3635 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3736 -- PatchMap is idempotent, so stimes n is id for every n
38−#if MIN_VERSION_semigroups(0,17,0)
3937 stimes = stimesIdempotentMonoid
40−#else
41− times1p n x = case compare n 0 of
42− LT -> error "stimesIdempotentMonoid: negative multiplier"
43− EQ -> mempty
44− GT -> x
45−#endif
4638
4739-- | The empty 'PatchMap' contains no insertions or deletions
4840instance Ord k => Monoid (PatchMap k v) where
src/Reflex/Patch/MapWithMove.hs+1−1
@@ -155,7 +155,7 @@ patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipW
155155 Just (from, to)
156156 reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
157157 g (to, _) (from, _) = if to == from then Nothing else
158− let Just movingTo = Map.lookup from reverseMapping
158+ let Just movingTo = Map.lookup to reverseMapping
159159 in Just (to, NodeInfo (From_Move from) $ Just movingTo)
160160
161161-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
src/Reflex/PerformEvent/Base.hs+1−1
@@ -141,7 +141,7 @@ hostPerformEventT a = do
141141 case mToPerform of
142142 Nothing -> return [result']
143143 Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
144+ responses <- runHostFrame $ traverseRequesterData (fmap Identity) toPerform
145145 mrt <- readRef responseTrigger
146146 let followupEventTriggers = case mrt of
147147 Just rt -> [rt :=> Identity responses]
src/Reflex/PerformEvent/Class.hs−1
@@ -5,7 +5,6 @@
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE RankNTypes #-}
109{-# LANGUAGE ScopedTypeVariables #-}
1110{-# LANGUAGE TypeFamilies #-}
src/Reflex/PostBuild/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE DataKinds #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
109{-# LANGUAGE TypeOperators #-}
src/Reflex/Profiled.hs+24−16
@@ -8,6 +8,9 @@
88{-# LANGUAGE StandaloneDeriving #-}
99{-# LANGUAGE TypeFamilies #-}
1010{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE RankNTypes #-}
1114-- |
1215-- Module:
1316-- Reflex.Profiled
@@ -16,7 +19,6 @@
1619-- profiling/cost-center information.
1720module Reflex.Profiled where
1821
19−import Control.Lens hiding (children)
2022import Control.Monad
2123import Control.Monad.Exception
2224import Control.Monad.Fix
@@ -33,6 +35,7 @@ import Data.Map (Map)
3335import qualified Data.Map.Strict as Map
3436import Data.Monoid ((<>))
3537import Data.Ord
38+import Data.Profunctor.Unsafe ((#.))
3639import qualified Data.Semigroup as S
3740import Data.Type.Coercion
3841import Foreign.Ptr
@@ -77,9 +80,9 @@ getCostCentreStack = go []
7780 go (cc : l) parent
7881
7982toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
80−toCostCentreTree ccs n = do
81− ccList <- getCostCentreStack ccs
82− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
83+toCostCentreTree ccs n =
84+ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
85+ <$> getCostCentreStack ccs
8386
8487getCostCentreTree :: IO CostCentreTree
8588getCostCentreTree = do
@@ -133,27 +136,32 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
133136 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
134137 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
135138 pull = Behavior_Profiled . pull . coerce
136− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
137− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
138− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
139+ fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
140+ mergeG :: forall (k :: z -> *) q v. GCompare k
141+ => (forall a. q a -> Event (ProfiledTimeline t) (v a))
142+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
143+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
139144 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
140145 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
141146 current (Dynamic_Profiled d) = coerce $ current d
142147 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
143148 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
144149 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
145− mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
146− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
150+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
151+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
147152 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
148153 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
149154 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
150− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
151− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
152− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
153− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
154− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
155− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
156− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
155+ behaviorCoercion c =
156+ Coercion `trans` behaviorCoercion @t c `trans` Coercion
157+ eventCoercion c =
158+ Coercion `trans` eventCoercion @t c `trans` Coercion
159+ dynamicCoercion c =
160+ Coercion `trans` dynamicCoercion @t c `trans` Coercion
161+ incrementalCoercion c d =
162+ Coercion `trans` incrementalCoercion @t c d `trans` Coercion
163+ mergeIntIncremental = Event_Profiled . mergeIntIncremental .
164+ coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
157165 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
158166
159167deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
src/Reflex/Pure.hs+21−17
@@ -5,6 +5,9 @@
55{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
811#ifdef USE_REFLEX_OPTIMIZER
912{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1013#endif
@@ -28,14 +31,9 @@ module Reflex.Pure
2831 , Incremental (..)
2932 ) where
3033
31−#if !MIN_VERSION_base(4,8,0)
32−import Control.Applicative
33−#endif
34−
3534import Control.Monad
3635import Data.Dependent.Map (DMap, GCompare)
3736import qualified Data.Dependent.Map as DMap
38−import Data.Functor.Identity
3937import Data.IntMap (IntMap)
4038import qualified Data.IntMap as IntMap
4139import Data.Maybe
@@ -43,10 +41,11 @@ import Data.MemoTrie
4341import Data.Monoid
4442import Data.Type.Coercion
4543import Reflex.Class
44+import Data.Kind (Type)
4645
4746-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
4847-- with the type @/t/@.
49−data Pure t
48+data Pure (t :: Type)
5049
5150-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
5251-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
@@ -79,17 +78,19 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
7978 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
8079 -- warning because the GCompare instance is not used; however, removing the
8180 -- GCompare instance produces a different warning, due to that constraint
82− -- being present in the original class definition
81+ -- being present in the original class definition.
8382
84− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
85− merge events = Event $ memo $ \t ->
86− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
83+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
84+ -- -> DMap k q -> Event (Pure t) (DMap k v)
85+ mergeG nt events = Event $ memo $ \t ->
86+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
8787 in if DMap.null currentOccurrences
8888 then Nothing
8989 else Just currentOccurrences
9090
91− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
92− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
91+-- The instance signature doeesn't compile, leave commented for documentation
92+-- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
93+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
9394
9495 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
9596 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
@@ -112,8 +113,8 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
112113 --a) -> Incremental (Pure t) p a
113114 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114115
115− mergeIncremental = mergeIncrementalImpl
116− mergeIncrementalWithMove = mergeIncrementalImpl
116+ mergeIncrementalG = mergeIncrementalImpl
117+ mergeIncrementalWithMoveG = mergeIncrementalImpl
117118
118119 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119120
@@ -128,14 +129,17 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
128129 behaviorCoercion Coercion = Coercion
129130 eventCoercion Coercion = Coercion
130131 dynamicCoercion Coercion = Coercion
132+ incrementalCoercion Coercion Coercion = Coercion
131133
132134 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
133135
134136 mergeIntIncremental = mergeIntIncrementalImpl
135137
136−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
137−mergeIncrementalImpl i = Event $ \t ->
138− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
138+mergeIncrementalImpl :: (PatchTarget p ~ DMap k q, GCompare k)
139+ => (forall a. q a -> Event (Pure t) (v a))
140+ -> Incremental (Pure t) p -> Event (Pure t) (DMap k v)
141+mergeIncrementalImpl nt i = Event $ \t ->
142+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
139143 in if DMap.null results
140144 then Nothing
141145 else Just results
src/Reflex/Query/Base.hs+5−7
@@ -1,7 +1,6 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE GeneralizedNewtypeDeriving #-}
33{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
54{-# LANGUAGE LambdaCase #-}
65{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
@@ -38,8 +37,7 @@ import Data.Map (Map)
3837import qualified Data.Map as Map
3938import Data.Monoid ((<>))
4039import qualified Data.Semigroup as S
41−import Data.Some (Some)
42−import qualified Data.Some as Some
40+import Data.Some (Some(Some))
4341import Data.These
4442
4543import Reflex.Class
@@ -145,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
145143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
146144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
147145 liftedBs0 :: Map (Some k) [Behavior t q]
148− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
146+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
149147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
150148 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
151− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
149+ Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
152150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
153151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
154152 accumBehaviors :: forall m'. MonadHold t m'
@@ -189,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
189187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
190188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
191189 liftedBs0 :: Map (Some k) [Behavior t q]
192− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
190+ liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
193191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
194192 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
195− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
193+ Map.fromDistinctAscList $ (\(k :=> mr) -> (Some k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
196194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
197195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
198196 accumBehaviors' :: forall m'. MonadHold t m'
src/Reflex/Query/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
98-- |
src/Reflex/Requester/Base.hs+2−3
@@ -66,8 +66,7 @@ import qualified Data.Map as Map
6666import Data.Monoid ((<>))
6767import Data.Proxy
6868import qualified Data.Semigroup as S
69−import Data.Some (Some)
70−import qualified Data.Some as Some
69+import Data.Some (Some(Some))
7170import Data.Type.Equality
7271import Data.Unique.Tag
7372
@@ -441,7 +440,7 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
441440 pack = Entry
442441 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
443442 f' k (Compose (n, v)) = do
444− (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
443+ (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
445444 return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
446445 ndm' <- numberOccurrencesFrom 1 dm'
447446 (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
src/Reflex/Requester/Class.hs−1
@@ -6,7 +6,6 @@
66{-# LANGUAGE CPP #-}
77{-# LANGUAGE FlexibleInstances #-}
88{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE RecursiveDo #-}
1110{-# LANGUAGE TypeFamilies #-}
1211{-# LANGUAGE UndecidableInstances #-}
src/Reflex/Spider/Internal.hs+274−199
@@ -9,7 +9,6 @@
99{-# LANGUAGE GADTs #-}
1010{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1111{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
1312{-# LANGUAGE MultiWayIf #-}
1413{-# LANGUAGE RankNTypes #-}
1514{-# LANGUAGE RoleAnnotations #-}
@@ -17,9 +16,15 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
2024#ifdef USE_REFLEX_OPTIMIZER
2125{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2226#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
2328-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2429-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2530module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -33,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
3338import Control.Monad.Exception
3439import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
3540import Control.Monad.Primitive
36−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
41+--import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
42+import Control.Monad.Reader.Class
43+import Control.Monad.IO.Class
44+import Control.Monad.ReaderIO
3745import Control.Monad.Ref
3846import Data.Align
3947import Data.Coerce
@@ -49,6 +57,7 @@ import Data.GADT.Compare
4957import Data.IntMap.Strict (IntMap)
5058import qualified Data.IntMap.Strict as IntMap
5159import Data.IORef
60+import Data.Kind (Type)
5261import Data.Maybe hiding (mapMaybe)
5362import Data.Monoid ((<>))
5463import Data.Proxy
@@ -63,6 +72,15 @@ import System.IO.Unsafe
6372import System.Mem.Weak
6473import Unsafe.Coerce
6574
75+#ifdef MIN_VERSION_semialign
76+#if MIN_VERSION_these(0,8,0)
77+import Data.These.Combinators (justThese)
78+#endif
79+#if MIN_VERSION_semialign(1,1,0)
80+import Data.Zip (Zip (..))
81+#endif
82+#endif
83+
6684#ifdef DEBUG_CYCLES
6785import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
6886import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -74,9 +92,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
7492import Data.FastWeakBag (FastWeakBag)
7593import qualified Data.FastWeakBag as FastWeakBag
7694import Data.Reflection
77−import Data.Some (Some)
78−import qualified Data.Some as Some
95+import Data.Some (Some(Some))
7996import Data.Type.Coercion
97+import Data.Profunctor.Unsafe ((#.), (.#))
8098import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
8199import qualified Data.WeakBag as WeakBag
82100import qualified Reflex.Class
@@ -85,6 +103,7 @@ import qualified Reflex.Host.Class
85103import Reflex.NotReady.Class
86104import Reflex.Patch
87105import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
106+import Reflex.PerformEvent.Base (PerformEventT)
88107
89108#ifdef DEBUG_TRACE_EVENTS
90109import qualified Data.ByteString.Char8 as BS8
@@ -193,12 +212,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
193212
194213newNodeId :: IO Int
195214newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202215#endif
203216
204217--------------------------------------------------------------------------------
@@ -302,9 +315,10 @@ cacheEvent e =
302315#else
303316 Event $
304317#endif
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
318+ unsafePerformIO $ do
319+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
320+ <- newIORef emptyFastWeak
321+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
308322#ifdef DEBUG_TRACE_EVENTS
309323 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310324#endif
@@ -319,9 +333,9 @@ cacheEvent e =
319333 scheduleClear occRef
320334 propagateFast a subscribers
321335 , subscriberInvalidateHeight = \old -> do
322− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
336+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
323337 , subscriberRecalculateHeight = \new -> do
324− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
338+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
325339 }
326340 when (isJust occ) $ do
327341 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
@@ -370,7 +384,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
370384eventNever :: Event x a
371385eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372386
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
387+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
374388eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375389
376390eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -422,14 +436,14 @@ newSubscriberHold h = return $ Subscriber
422436 , subscriberRecalculateHeight = \_ -> return ()
423437 }
424438
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
439+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
426440newSubscriberFan subscribed = return $ Subscriber
427441 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428442 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429443 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430444 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431445 scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
446+ let f _ (Pair v subsubs) = do
433447 propagate v $ _fanSubscribedChildren_list subsubs
434448 return $ Constant ()
435449 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -437,12 +451,12 @@ newSubscriberFan subscribed = return $ Subscriber
437451 , subscriberInvalidateHeight = \old -> do
438452 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
439453 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
440− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
454+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
441455 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
442456 , subscriberRecalculateHeight = \new -> do
443457 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
444458 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
445− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
459+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
446460 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
447461 }
448462
@@ -458,7 +472,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
458472 oldHeight <- readIORef $ switchSubscribedHeight subscribed
459473 when (oldHeight /= invalidHeight) $ do
460474 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
461− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
475+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
462476 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
463477 , subscriberRecalculateHeight = \new -> do
464478 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -481,8 +495,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
481495 Nothing -> do
482496 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
483497 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
485− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
499+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
486500 Just o -> do -- Since it's already firing, no need to adjust height
487501 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
488502 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -529,14 +543,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
529543propagate a subscribers = withIncreasedDepth $ do
530544 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
531545 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
532− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
546+ WeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
533547
534548-- | Propagate everything at the current height
535549propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
536550propagateFast a subscribers = withIncreasedDepth $ do
537551 -- Note: in the following traversal, we do not visit nodes that are added to the list during our traversal; they are new events, which will necessarily have full information already, so there is no need to traverse them
538552 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
539− FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
553+ FastWeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
540554
541555--------------------------------------------------------------------------------
542556-- EventSubscribed
@@ -545,6 +559,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
545559toAny :: a -> Any
546560toAny = unsafeCoerce
547561
562+-- Why do we use Any here, instead of just giving eventSubscribedRetained an
563+-- existential type? Sadly, GHC does not currently know how to unbox types
564+-- with existentially quantified fields. So instead we just coerce values
565+-- to type Any on the way in. Since we never coerce them back, this is
566+-- perfectly safe.
548567data EventSubscribed x = EventSubscribed
549568 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550569 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -577,12 +596,12 @@ eventSubscribedNever = EventSubscribed
577596#endif
578597 }
579598
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
599+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
581600eventSubscribedFan !subscribed = EventSubscribed
582601 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583602 , eventSubscribedRetained = toAny subscribed
584603#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
604+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586605 , eventSubscribedHasOwnHeightRef = False
587606 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588607#endif
@@ -595,7 +614,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
595614#ifdef DEBUG_CYCLES
596615 , eventSubscribedGetParents = do
597616 s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
617+ return [Some $ _eventSubscription_subscribed s]
599618 , eventSubscribedHasOwnHeightRef = True
600619 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601620#endif
@@ -608,8 +627,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
608627#ifdef DEBUG_CYCLES
609628 , eventSubscribedGetParents = do
610629 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
630+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
631+ innerParents = maybeToList $ fmap Some innerSubscription
613632 return $ outerParent : innerParents
614633 , eventSubscribedHasOwnHeightRef = True
615634 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -625,13 +644,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
625644
626645walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627646walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
647+ subscribers <- flip execStateT mempty $ ($ Some s0) $ fix $ \loop (Some s) -> do
629648 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630649 when (h == invalidHeight) $ do
631650 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
651+ modify (Some s :)
633652 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
653+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635654 return subscribers
636655#endif
637656
@@ -659,7 +678,7 @@ behaviorPull !p = Behavior $ do
659678 val <- liftIO $ readIORef $ pullValue p
660679 case val of
661680 Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedPull subscribed)) :))
681+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
663682 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664683 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665684 return $ pullSubscribedValue subscribed
@@ -668,7 +687,7 @@ behaviorPull !p = Behavior $ do
668687 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669688 parentsRef <- liftIO $ newIORef []
670689 holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
690+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672691 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673692 parents <- liftIO $ readIORef parentsRef
674693 let subscribed = PullSubscribed
@@ -678,7 +697,7 @@ behaviorPull !p = Behavior $ do
678697 , pullSubscribedParents = parents
679698 }
680699 liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedPull subscribed)) :))
700+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
682701 return a
683702
684703behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -689,7 +708,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689708readHoldTracked h = do
690709 result <- liftIO $ readIORef $ holdValue h
691710 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedHold h)) :))
711+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
693712 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694713 return result
695714
@@ -703,42 +722,46 @@ readBehaviorUntracked b = do
703722-- Dynamic
704723--------------------------------------------------------------------------------
705724
706−data Dynamic x p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
725+type DynamicS x p = Dynamic x (PatchTarget p) p
726+
727+data Dynamic x target p = Dynamic
728+ { dynamicCurrent :: !(Behavior x target)
708729 , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
709730 }
710731
711−dynamicHold :: Hold x p -> Dynamic x p
732+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
733+
734+
735+
736+
737+dynamicHold :: Hold x p -> DynamicS x p
712738dynamicHold !h = Dynamic
713739 { dynamicCurrent = behaviorHold h
714740 , dynamicUpdated = eventHold h
715741 }
716742
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
743+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
718744dynamicHoldIdentity = dynamicHold
719745
720−dynamicConst :: PatchTarget p -> Dynamic x p
746+dynamicConst :: PatchTarget p -> DynamicS x p
721747dynamicConst !a = Dynamic
722748 { dynamicCurrent = behaviorConst a
723749 , dynamicUpdated = eventNever
724750 }
725751
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
752+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
727753dynamicDyn !d = Dynamic
728754 { dynamicCurrent = behaviorDyn d
729755 , dynamicUpdated = eventDyn d
730756 }
731757
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
758+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
733759dynamicDynIdentity = dynamicDyn
734760
735761--------------------------------------------------------------------------------
736762-- Combinators
737763--------------------------------------------------------------------------------
738764
739−--TODO: Figure out why certain things are not 'representational', then make them
740−--representational so we can use coerce
741−
742765--type role Hold representational
743766data Hold x p
744767 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -759,20 +782,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759782
760783-- | Stores all global data relevant to a particular Spider timeline; only one
761784-- value should exist for each type @x@
762−data SpiderTimelineEnv x = SpiderTimelineEnv
785+newtype SpiderTimelineEnv x = STE {unSTE :: SpiderTimelineEnv' x}
786+-- We implement SpiderTimelineEnv with a newtype wrapper so
787+-- we can get the coercions we want safely.
788+type role SpiderTimelineEnv nominal
789+
790+data SpiderTimelineEnv' x = SpiderTimelineEnv
763791 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764792 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765793#ifdef DEBUG
766794 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767795#endif
768796 }
769−type role SpiderTimelineEnv nominal
797+type role SpiderTimelineEnv' phantom
770798
771799instance Eq (SpiderTimelineEnv x) where
772800 _ == _ = True -- Since only one exists of each type
773801
774802instance GEq SpiderTimelineEnv where
775− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
803+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
776804 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777805 else Nothing
778806
@@ -795,7 +823,7 @@ runEventM :: EventM x a -> IO a
795823runEventM = unEventM
796824
797825asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
798−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
826+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
799827
800828class MonadIO m => Defer a m where
801829 getDeferralQueue :: m (IORef [a])
@@ -862,7 +890,7 @@ instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
862890
863891{-# INLINE scheduleClear #-}
864892scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ Some.This $ Clear r
893+scheduleClear r = defer $ Some $ Clear r
866894
867895instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
868896 {-# INLINE getDeferralQueue #-}
@@ -870,7 +898,7 @@ instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
870898
871899{-# INLINE scheduleIntClear #-}
872900scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ Some.This $ IntClear r
901+scheduleIntClear r = defer $ Some $ IntClear r
874902
875903instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
876904 {-# INLINE getDeferralQueue #-}
@@ -878,7 +906,7 @@ instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
878906
879907{-# INLINE scheduleRootClear #-}
880908scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ Some.This $ RootClear r
909+scheduleRootClear r = defer $ Some $ RootClear r
882910
883911instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884912 {-# INLINE getDeferralQueue #-}
@@ -933,9 +961,9 @@ getHoldEventSubscription h = do
933961
934962type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935963
936−--type role BehaviorM representational
937964-- BehaviorM can sample behaviors
938−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
965+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderIO (BehaviorEnv x) a }
966+ deriving (Functor, Applicative, MonadIO, MonadFix, MonadReader (BehaviorEnv x))
939967
940968instance Monad (BehaviorM x) where
941969 {-# INLINE (>>=) #-}
@@ -986,7 +1014,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
9861014#endif
9871015 }
9881016
989−data Root x (k :: * -> *)
1017+data Root x k
9901018 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
9911019 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
9921020 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1056,25 +1084,25 @@ heightBagVerify b@(HeightBag s c) = if
10561084heightBagVerify = id
10571085#endif
10581086
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1087+data FanSubscribedChildren x k v a = FanSubscribedChildren
1088+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1089+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1090+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
10631091 }
10641092
1065−data FanSubscribed (x :: *) k
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1093+data FanSubscribed x k v
1094+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1095+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1096+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
10691097 , fanSubscribedParent :: !(EventSubscription x)
10701098#ifdef DEBUG_NODEIDS
10711099 , fanSubscribedNodeId :: Int
10721100#endif
10731101 }
10741102
1075−data Fan x k
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1103+data Fan x k v
1104+ = Fan { fanParent :: !(Event x (DMap k v))
1105+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
10781106 }
10791107
10801108data SwitchSubscribed x a
@@ -1136,19 +1164,27 @@ instance HasSpiderTimeline x => Align (Event x) where
11361164#if MIN_VERSION_these(0, 8, 0)
11371165instance HasSpiderTimeline x => Semialign (Event x) where
11381166#endif
1139− align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1167+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1168+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1169+
1170+#ifdef MIN_VERSION_semialign
1171+#if MIN_VERSION_semialign(1,1,0)
1172+instance HasSpiderTimeline x => Zip (Event x) where
1173+#endif
1174+ zip x y = mapMaybe justThese $ align x y
1175+#endif
11401176
11411177data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11421178 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11431179 | HoldDyn !(Hold x p)
11441180
1145−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1181+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11461182
1147−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1183+newMapDyn :: HasSpiderTimeline x => (a -> b) -> DynamicS x (Identity a) -> DynamicS x (Identity b)
11481184newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
11491185
11501186--TODO: Avoid the duplication between this and R.zipDynWith
1151−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1187+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> DynamicS x (Identity a) -> DynamicS x (Identity b) -> DynamicS x (Identity c)
11521188zipDynWith f da db =
11531189 let eab = align (dynamicUpdated da) (dynamicUpdated db)
11541190 ec = flip push eab $ \o -> do
@@ -1171,18 +1207,12 @@ buildDynamic readV0 v' = do
11711207 return d
11721208
11731209unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1174−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1175− where x = (readV0, v')
1210+unsafeBuildDynamic readV0 v' =
1211+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11761212
11771213-- ResultM can read behaviors and events
11781214type ResultM = EventM
11791215
1180−{-# NOINLINE unsafeNewIORef #-}
1181−unsafeNewIORef :: a -> b -> IORef b
1182−unsafeNewIORef a b = unsafePerformIO $ do
1183− touch a
1184− newIORef b
1185−
11861216instance HasSpiderTimeline x => Functor (Event x) where
11871217 fmap f = push $ return . Just . f
11881218
@@ -1195,33 +1225,42 @@ push f e = cacheEvent (pushCheap f e)
11951225
11961226{-# INLINABLE pull #-}
11971227pull :: BehaviorM x a -> Behavior x a
1198−pull a = behaviorPull $ Pull
1199− { pullCompute = a
1200− , pullValue = unsafeNewIORef a Nothing
1228+pull a = unsafePerformIO $ do
1229+ ref <- newIORef Nothing
12011230#ifdef DEBUG_NODEIDS
1202− , pullNodeId = unsafeNodeId a
1231+ nid <- newNodeId
12031232#endif
1204− }
1233+ pure $ behaviorPull $ Pull
1234+ { pullCompute = a
1235+ , pullValue = ref
1236+#ifdef DEBUG_NODEIDS
1237+ , pullNodeId = nid
1238+#endif
1239+ }
12051240
12061241{-# INLINABLE switch #-}
12071242switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1208−switch a = eventSwitch $ Switch
1209− { switchParent = a
1210− , switchSubscribed = unsafeNewIORef a Nothing
1211− }
1243+switch a = unsafePerformIO $ do
1244+ ref <- newIORef Nothing
1245+ pure $ eventSwitch $ Switch
1246+ { switchParent = a
1247+ , switchSubscribed = ref
1248+ }
12121249
12131250coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1214−coincidence a = eventCoincidence $ Coincidence
1215− { coincidenceParent = a
1216− , coincidenceSubscribed = unsafeNewIORef a Nothing
1217− }
1251+coincidence a = unsafePerformIO $ do
1252+ ref <- newIORef Nothing
1253+ pure $ eventCoincidence $ Coincidence
1254+ { coincidenceParent = a
1255+ , coincidenceSubscribed = ref
1256+ }
12181257
12191258-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12201259run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
12211260run roots after = do
12221261 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
12231262 let t = spiderTimeline :: SpiderTimelineEnv x
1224− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1263+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
12251264 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
12261265 occBefore <- liftIO $ do
12271266 occBefore <- readIORef occRef
@@ -1285,9 +1324,9 @@ type WeakList a = [Weak a]
12851324withIncreasedDepth :: CanTrace x m => m a -> m a
12861325withIncreasedDepth a = do
12871326 spiderTimeline <- askSpiderTimelineEnv
1288− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1327+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
12891328 result <- a
1290− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1329+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
12911330 return result
12921331#else
12931332withIncreasedDepth :: m a -> m a
@@ -1314,7 +1353,7 @@ traceMWhen _ b getMessage = when b $ do
13141353 message <- getMessage
13151354#ifdef DEBUG
13161355 spiderTimeline <- askSpiderTimelineEnv
1317− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1356+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
13181357#else
13191358 let d = 0
13201359#endif
@@ -1355,25 +1394,25 @@ propagateSubscriberHold h a = do
13551394data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
13561395
13571396runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1358−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1397+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
13591398
13601399askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
13611400askInvalidator = do
1362− (!m, _) <- BehaviorM ask
1401+ (!m, _) <- ask
13631402 case m of
13641403 Nothing -> return Nothing
13651404 Just (!wi, _) -> return $ Just wi
13661405
13671406askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
13681407askParentsRef = do
1369− (!m, _) <- BehaviorM ask
1408+ (!m, _) <- ask
13701409 case m of
13711410 Nothing -> return Nothing
13721411 Just (_, !p) -> return $ Just p
13731412
13741413askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
13751414askBehaviorHoldInits = do
1376− (_, !his) <- BehaviorM ask
1415+ (_, !his) <- ask
13771416 return his
13781417
13791418{-# INLINE getDynHold #-}
@@ -1418,6 +1457,9 @@ getRootSubscribed k r sub = do
14181457 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14191458 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14201459 writeIORef uninitRef $! uninit
1460+#ifdef DEBUG_NODEIDS
1461+ nid <- newNodeId
1462+#endif
14211463 let !subscribed = RootSubscribed
14221464 { rootSubscribedKey = k
14231465 , rootSubscribedCachedSubscribed = cached
@@ -1426,7 +1468,7 @@ getRootSubscribed k r sub = do
14261468 , rootSubscribedUninit = uninit
14271469 , rootSubscribedWeakSelf = weakSelf
14281470#ifdef DEBUG_NODEIDS
1429− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1471+ , rootSubscribedNodeId = nid
14301472#endif
14311473 }
14321474 -- If we die at the same moment that all our children die, they will
@@ -1475,16 +1517,10 @@ newFanInt = do
14751517 , _fanInt_occRef = occRef
14761518 }
14771519
1478−{-# NOINLINE unsafeNewFanInt #-}
1479−unsafeNewFanInt :: b -> FanInt x a
1480−unsafeNewFanInt b = unsafePerformIO $ do
1481− touch b
1482− newFanInt
1483−
14841520fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1485−fanInt p =
1486− let self = unsafeNewFanInt p
1487− in EventSelectorInt $ \k -> Event $ \sub -> do
1521+fanInt p = unsafePerformIO $ do
1522+ self <- newFanInt
1523+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14881524 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14891525 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14901526 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1492,15 +1528,15 @@ fanInt p =
14921528 liftIO $ writeIORef (_fanInt_occRef self) m
14931529 scheduleIntClear $ _fanInt_occRef self
14941530 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1495− FastWeakBag.traverse b $ \s -> do
1531+ FastWeakBag.traverse_ b $ \s -> do
14961532 subscriberPropagate s v
14971533 , subscriberInvalidateHeight = \old -> do
14981534 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1499− FastWeakBag.traverse b $ \s -> do
1535+ FastWeakBag.traverse_ b $ \s -> do
15001536 subscriberInvalidateHeight s old
15011537 , subscriberRecalculateHeight = \new -> do
15021538 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1503− FastWeakBag.traverse b $ \s -> do
1539+ FastWeakBag.traverse_ b $ \s -> do
15041540 subscriberRecalculateHeight s new
15051541 }
15061542 liftIO $ do
@@ -1520,7 +1556,7 @@ fanInt p =
15201556 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15211557
15221558{-# INLINABLE getFanSubscribed #-}
1523−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1559+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k v -> Subscriber x (v a) -> EventM x (WeakBagTicket, FanSubscribed x k v, Maybe (v a))
15241560getFanSubscribed k f sub = do
15251561 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15261562 case mSubscribed of
@@ -1538,13 +1574,16 @@ getFanSubscribed k f sub = do
15381574 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15391575 occRef <- liftIO $ newIORef parentOcc
15401576 when (isJust parentOcc) $ scheduleClear occRef
1577+#ifdef DEBUG_NODEIDS
1578+ nid <- liftIO newNodeId
1579+#endif
15411580 let subscribed = FanSubscribed
15421581 { fanSubscribedCachedSubscribed = fanSubscribed f
15431582 , fanSubscribedOccurrence = occRef
15441583 , fanSubscribedParent = subscription
15451584 , fanSubscribedSubscribers = subscribersRef
15461585#ifdef DEBUG_NODEIDS
1547− , fanSubscribedNodeId = unsafeNodeId f
1586+ , fanSubscribedNodeId = nid
15481587#endif
15491588 }
15501589 let !self = (k, subscribed)
@@ -1554,7 +1593,7 @@ getFanSubscribed k f sub = do
15541593 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15551594 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15561595
1557−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1596+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15581597cleanupFanSubscribed (k, subscribed) = do
15591598 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15601599 let reducedSubscribers = DMap.delete k subscribers
@@ -1566,7 +1605,7 @@ cleanupFanSubscribed (k, subscribed) = do
15661605 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15671606
15681607{-# INLINE subscribeFanSubscribed #-}
1569−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1608+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
15701609subscribeFanSubscribed k subscribed sub = do
15711610 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15721611 case DMap.lookup k subscribers of
@@ -1604,6 +1643,9 @@ getSwitchSubscribed s sub = do
16041643 when (isJust parentOcc) $ scheduleClear occRef
16051644 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16061645 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1646+#ifdef DEBUG_NODEIDS
1647+ nid <- liftIO newNodeId
1648+#endif
16071649 let !subscribed = SwitchSubscribed
16081650 { switchSubscribedCachedSubscribed = switchSubscribed s
16091651 , switchSubscribedOccurrence = occRef
@@ -1616,7 +1658,7 @@ getSwitchSubscribed s sub = do
16161658 , switchSubscribedCurrentParent = subscriptionRef
16171659 , switchSubscribedWeakSelf = weakSelf
16181660#ifdef DEBUG_NODEIDS
1619− , switchSubscribedNodeId = unsafeNodeId s
1661+ , switchSubscribedNodeId = nid
16201662#endif
16211663 }
16221664 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1661,6 +1703,9 @@ getCoincidenceSubscribed c sub = do
16611703 scheduleClear innerSubdRef
16621704 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16631705 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1706+#ifdef DEBUG_NODEIDS
1707+ nid <- liftIO newNodeId
1708+#endif
16641709 let subscribed = CoincidenceSubscribed
16651710 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16661711 , coincidenceSubscribedOccurrence = occRef
@@ -1671,7 +1716,7 @@ getCoincidenceSubscribed c sub = do
16711716 , coincidenceSubscribedInnerParent = innerSubdRef
16721717 , coincidenceSubscribedWeakSelf = weakSelf
16731718#ifdef DEBUG_NODEIDS
1674− , coincidenceSubscribedNodeId = unsafeNodeId c
1719+ , coincidenceSubscribedNodeId = nid
16751720#endif
16761721 }
16771722 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1688,26 +1733,34 @@ cleanupCoincidenceSubscribed subscribed = do
16881733subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16891734subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16901735
1691−{-# INLINE merge #-}
1692−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1693−merge d = cacheEvent (mergeCheap d)
1736+{-# INLINE mergeG #-}
1737+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1738+ => (forall a. q a -> Event x (v a))
1739+ -> DynamicS x (PatchDMap k q) -> Event x (DMap k v)
1740+mergeG nt d = cacheEvent (mergeCheap nt d)
16941741
16951742{-# INLINE mergeWithMove #-}
1696−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1697−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1743+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1744+ => (forall a. q a -> Event x (v a))
1745+ -> DynamicS x (PatchDMapWithMove k q) -> Event x (DMap k v)
1746+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
16981747
16991748{-# INLINE [1] mergeCheap #-}
1700−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1701−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1749+mergeCheap
1750+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1751+ => (forall a. q a -> Event x (v a))
1752+ -> DynamicS x (PatchDMap k q)
1753+ -> Event x (DMap k v)
1754+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
17021755 where
1703− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1756+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17041757 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17051758 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17061759 (mOldSubd, newPs) <- case me of
17071760 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17081761 Just e -> do
17091762 let s = subscriber $ return k
1710− subscription@(EventSubscription _ subd) <- subscribe e s
1763+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17111764 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17121765 let newParent = MergeSubscribedParent subscription
17131766 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1717,28 +1770,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17171770 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17181771 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17191772 foldM f ([], oldParents) $ DMap.toList p
1720− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1773+
1774+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17211775 getInitialSubscribers initialParents subscriber = do
17221776 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17231777 let s = subscriber $ return k
1724− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1778+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17251779 height <- liftIO $ getEventSubscribedHeight parentSubd
1726− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1780+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
17271781 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17281782 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17291783 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17301784 )
1785+
17311786 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17321787 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17331788
17341789{-# INLINE [1] mergeCheapWithMove #-}
1735−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1736−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1790+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1791+ => (forall a. q a -> Event x (v a))
1792+ -> DynamicS x (PatchDMapWithMove k q)
1793+ -> Event x (DMap k v)
1794+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
17371795 where
1738− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1796+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17391797 updateMe subscriber heightBagRef oldParents p = do
17401798 -- Prepare new parents for insertion
1741− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1799+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
17421800 subscribeParent k e = do
17431801 keyRef <- liftIO $ newIORef k
17441802 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1747,9 +1805,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17471805 newParentHeight <- getEventSubscribedHeight subd
17481806 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17491807 return $ MergeSubscribedParentWithMove subscription keyRef
1750− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1808+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17511809 -- Collect old parents for deletion and update the keys of moved parents
1752− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1810+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
17531811 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17541812 Nothing -> do
17551813 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1760,46 +1818,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17601818 return Nothing
17611819 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17621820 return (toDelete, applyAlways p' oldParents)
1763− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1821+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17641822 getInitialSubscribers initialParents subscriber = do
17651823 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17661824 keyRef <- liftIO $ newIORef k
17671825 let s = subscriber $ liftIO $ readIORef keyRef
1768− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1826+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17691827 height <- liftIO $ getEventSubscribedHeight parentSubd
1770− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1828+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17711829 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17721830 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17731831 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17741832 )
1833+
17751834 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17761835 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17771836
1778−type MergeUpdateFunc k x p s
1779− = (forall a. EventM x (k a) -> Subscriber x a)
1837+type MergeUpdateFunc k v x p s
1838+ = (forall a. EventM x (k a) -> Subscriber x (v a))
17801839 -> IORef HeightBag
17811840 -> DMap k s
17821841 -> p
17831842 -> EventM x ([EventSubscription x], DMap k s)
17841843
1785−type MergeInitFunc k x s
1786− = DMap k (Event x)
1787− -> (forall a. EventM x (k a) -> Subscriber x a)
1788− -> EventM x (DMap k Identity, [Height], DMap k s)
1844+type MergeInitFunc k v q x s
1845+ = DMap k q
1846+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1847+ -> EventM x (DMap k v, [Height], DMap k s)
17891848
17901849type MergeDestroyFunc k s
17911850 = DMap k s
17921851 -> IO ()
17931852
1794−data Merge x k s = Merge
1853+data Merge x k v s = Merge
17951854 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17961855 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17971856 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1798− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1799− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1857+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1858+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
18001859 }
18011860
1802−invalidateMergeHeight :: Merge x k s -> IO ()
1861+invalidateMergeHeight :: Merge x k v s -> IO ()
18031862invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18041863
18051864invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1809,8 +1868,7 @@ invalidateMergeHeight' heightRef sub = do
18091868 writeIORef heightRef $! invalidHeight
18101869 subscriberInvalidateHeight sub oldHeight
18111870
1812−
1813−revalidateMergeHeight :: Merge x k s -> IO ()
1871+revalidateMergeHeight :: Merge x k v s -> IO ()
18141872revalidateMergeHeight m = do
18151873 currentHeight <- readIORef $ _merge_heightRef m
18161874 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1826,19 +1884,19 @@ revalidateMergeHeight m = do
18261884 subscriberRecalculateHeight (_merge_sub m) height
18271885 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18281886
1829−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1887+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18301888scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18311889 vals <- liftIO $ readIORef $ _merge_accumRef m
18321890 liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
18331891 --TODO: Assert that m is not empty
18341892 subscriberPropagate (_merge_sub m) vals
18351893
1836−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1894+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
18371895mergeSubscriber m getKey = Subscriber
18381896 { subscriberPropagate = \a -> do
18391897 oldM <- liftIO $ readIORef $ _merge_accumRef m
18401898 k <- getKey
1841− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1899+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
18421900 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18431901 liftIO $ writeIORef (_merge_accumRef m) $! newM
18441902 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1853,7 +1911,7 @@ mergeSubscriber m getKey = Subscriber
18531911 else liftIO $ do
18541912#ifdef DEBUG_CYCLES
18551913 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1856− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1914+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
18571915 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
18581916#else
18591917 let cycleInfo = ""
@@ -1869,7 +1927,7 @@ mergeSubscriber m getKey = Subscriber
18691927 }
18701928
18711929--TODO: Be able to run as much of this as possible promptly
1872−updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1930+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
18731931updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18741932 where updateMe = do
18751933 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1877,9 +1935,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18771935 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18781936 return subscriptionsToKill
18791937
1880−{-# INLINE mergeCheap' #-}
1881−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> Dynamic x p -> Event x (DMap k Identity)
1882−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1938+{-# INLINE mergeGCheap' #-}
1939+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
1940+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1941+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
18831942 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18841943 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18851944 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1927,11 +1986,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
19271986 , occ
19281987 )
19291988
1930−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1989+mergeInt :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19311990mergeInt = cacheEvent . mergeIntCheap
19321991
19331992{-# INLINABLE mergeIntCheap #-}
1934−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1993+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19351994mergeIntCheap d = Event $ \sub -> do
19361995 initialParents <- readBehaviorUntracked $ dynamicCurrent d
19371996 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2028,14 +2087,16 @@ mergeIntCheap d = Event $ \sub -> do
20282087 )
20292088
20302089newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2090+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
20312091
2032−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2033−fan e =
2092+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2093+fanG e = unsafePerformIO $ do
2094+ ref <- newIORef Nothing
20342095 let f = Fan
20352096 { fanParent = e
2036− , fanSubscribed = unsafeNewIORef e Nothing
2097+ , fanSubscribed = ref
20372098 }
2038− in EventSelector $ \k -> eventFan k f
2099+ pure $ EventSelectorG $ \k -> eventFan k f
20392100
20402101runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20412102runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2089,18 +2150,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
20892150-- | Run an event action outside of a frame
20902151runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
20912152runFrame a = SpiderHost $ do
2092− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2153+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
20932154 let go = do
20942155 result <- a
20952156 runHoldInits (eventEnvHoldInits env) (eventEnvDynInits env) (eventEnvMergeInits env) -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
20962157 return result
20972158 result <- runEventM go
20982159 toClear <- readIORef $ eventEnvClears env
2099− forM_ toClear $ \(Some.This (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
2160+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
21002161 toClearInt <- readIORef $ eventEnvIntClears env
2101− forM_ toClearInt $ \(Some.This (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2162+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
21022163 toClearRoot <- readIORef $ eventEnvRootClears env
2103− forM_ toClearRoot $ \(Some.This (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2164+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
21042165 toAssign <- readIORef $ eventEnvAssignments env
21052166 toReconnectRef <- newIORef []
21062167 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2147,7 +2208,7 @@ runFrame a = SpiderHost $ do
21472208 myHeight <- readIORef $ switchSubscribedHeight subscribed
21482209 when (parentHeight /= myHeight) $ do
21492210 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2150− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2211+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
21512212 mapM_ _someMergeUpdate_invalidateHeight mergeUpdates --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
21522213 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
21532214 unsubscribe subscription
@@ -2188,7 +2249,7 @@ invalidateCoincidenceHeight subscribed = do
21882249 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
21892250 when (oldHeight /= invalidHeight) $ do
21902251 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2191− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2252+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
21922253
21932254updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
21942255updateSwitchHeight new subscribed = do
@@ -2196,7 +2257,7 @@ updateSwitchHeight new subscribed = do
21962257 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21972258 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21982259 writeIORef (switchSubscribedHeight subscribed) $! new
2199− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2260+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
22002261
22012262recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
22022263recalculateCoincidenceHeight subscribed = do
@@ -2205,7 +2266,7 @@ recalculateCoincidenceHeight subscribed = do
22052266 height <- calculateCoincidenceHeight subscribed
22062267 when (height /= invalidHeight) $ do
22072268 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2208− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2269+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
22092270
22102271calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
22112272calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2300,7 +2361,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
23002361 fail _ = error "Dynamic does not support 'fail'"
23012362
23022363{-# INLINABLE newJoinDyn #-}
2303−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
2364+newJoinDyn :: HasSpiderTimeline x => DynamicS x (Identity (DynamicS x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
23042365newJoinDyn d =
23052366 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
23062367 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2450,7 +2511,7 @@ unsafeNewSpiderTimelineEnv = do
24502511#ifdef DEBUG
24512512 depthRef <- newIORef 0
24522513#endif
2453− return $ SpiderTimelineEnv
2514+ return $ STE $ SpiderTimelineEnv
24542515 { _spiderTimeline_lock = lock
24552516 , _spiderTimeline_eventEnv = env
24562517#ifdef DEBUG
@@ -2460,19 +2521,19 @@ unsafeNewSpiderTimelineEnv = do
24602521
24612522-- | Create a new SpiderTimelineEnv
24622523newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2463−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2524+newSpiderTimeline = withSpiderTimeline (pure . Some)
24642525
2465−data LocalSpiderTimeline x s
2526+data LocalSpiderTimeline (x :: Type) s
24662527
24672528instance Reifies s (SpiderTimelineEnv x) =>
24682529 HasSpiderTimeline (LocalSpiderTimeline x s) where
2469− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2530+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
24702531
24712532localSpiderTimeline
2472− :: Proxy s
2533+ :: proxy s
24732534 -> SpiderTimelineEnv x
24742535 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2475−localSpiderTimeline _ = unsafeCoerce
2536+localSpiderTimeline _ = coerce
24762537
24772538-- | Pass a new timeline to the given function.
24782539withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2480,18 +2541,18 @@ withSpiderTimeline k = do
24802541 env <- unsafeNewSpiderTimelineEnv
24812542 reify env $ \s -> k $ localSpiderTimeline s env
24822543
2483−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2544+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24842545
24852546type ComputeM = EventM
24862547
2487−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2548+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24882549
24892550instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24902551 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
24912552 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
24922553 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2493− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2494− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2554+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2555+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
24952556 type PullM (SpiderTimeline x) = SpiderPullM x
24962557 type PushM (SpiderTimeline x) = SpiderPushM x
24972558 {-# INLINABLE never #-}
@@ -2504,10 +2565,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25042565 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
25052566 {-# INLINABLE pull #-}
25062567 pull = SpiderBehavior . pull . coerce
2507− {-# INLINABLE merge #-}
2508− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2509− {-# INLINABLE fan #-}
2510− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2568+ {-# INLINABLE fanG #-}
2569+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2570+ {-# INLINABLE mergeG #-}
2571+ mergeG
2572+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2573+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2574+ -> DMap k q
2575+ -> R.Event (SpiderTimeline x) (DMap k v)
2576+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
25112577 {-# INLINABLE switch #-}
25122578 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25132579 {-# INLINABLE coincidence #-}
@@ -2515,15 +2581,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25152581 {-# INLINABLE current #-}
25162582 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
25172583 {-# INLINABLE updated #-}
2518− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2584+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
25192585 {-# INLINABLE unsafeBuildDynamic #-}
25202586 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25212587 {-# INLINABLE unsafeBuildIncremental #-}
25222588 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2523− {-# INLINABLE mergeIncremental #-}
2524− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2525− {-# INLINABLE mergeIncrementalWithMove #-}
2526− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2589+ {-# INLINABLE mergeIncrementalG #-}
2590+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2591+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2592+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
25272593 {-# INLINABLE currentIncremental #-}
25282594 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25292595 {-# INLINABLE updatedIncremental #-}
@@ -2534,9 +2600,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25342600 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
25352601 eventCoercion Coercion = Coercion
25362602 behaviorCoercion Coercion = Coercion
2537− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2603+ dynamicCoercion Coercion = Coercion
2604+ incrementalCoercion Coercion Coercion = Coercion
25382605 {-# INLINABLE mergeIntIncremental #-}
2539− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2606+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
25402607 {-# INLINABLE fanInt #-}
25412608 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
25422609
@@ -2561,7 +2628,7 @@ instance MonadAtomicRef (EventM x) where
25612628 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25622629
25632630-- | The monad for actions that manipulate a Spider timeline identified by @x@
2564−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2631+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25652632
25662633instance Monad (SpiderHost x) where
25672634 {-# INLINABLE (>>=) #-}
@@ -2583,7 +2650,7 @@ runSpiderHost (SpiderHost a) = a
25832650runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25842651runSpiderHostForTimeline (SpiderHost a) _ = a
25852652
2586−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2653+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25872654 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25882655
25892656instance Monad (SpiderHostFrame x) where
@@ -2639,3 +2706,11 @@ instance MonadAtomicRef (SpiderHostFrame x) where
26392706instance PrimMonad (SpiderHostFrame x) where
26402707 type PrimState (SpiderHostFrame x) = PrimState IO
26412708 primitive = SpiderHostFrame . EventM . primitive
2709+
2710+instance NotReady (SpiderTimeline x) (SpiderHost x) where
2711+ notReadyUntil _ = return ()
2712+ notReady = return ()
2713+
2714+instance HasSpiderTimeline x => NotReady (SpiderTimeline x) (PerformEventT (SpiderTimeline x) (SpiderHost x)) where
2715+ notReadyUntil _ = return ()
2716+ notReady = return ()
src/Reflex/TriggerEvent/Class.hs−1
@@ -2,7 +2,6 @@
22-- new 'Event's that can be triggered from 'IO'.
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76module Reflex.TriggerEvent.Class
87 ( TriggerEvent (..)
src/Reflex/Workflow.hs+1−1
@@ -34,7 +34,7 @@ workflow w0 = do
3434 rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
3535 return $ fmap fst eResult
3636
37−-- | Similar to 'workflow', but outputs an 'Event' that fires whenever the current 'Workflow' is replaced by the next 'Workflow'.
37+-- | Similar to 'workflow', but outputs an 'Event' that fires at post-build time and whenever the current 'Workflow' is replaced by the next 'Workflow'.
3838workflowView :: forall t m a. (Reflex t, NotReady t m, Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
3939workflowView w0 = do
4040 rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
test/EventWriterT.hs+6−4
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE BangPatterns #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE GADTs #-}
@@ -13,6 +14,10 @@ import Data.Functor.Misc
1314import qualified Data.Map as M
1415import Data.These
1516
17+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
18+import Data.These.Lens
19+#endif
20+
1621import Reflex
1722import Reflex.EventWriter.Base
1823import Test.Run
@@ -48,16 +53,13 @@ unwrapApp x appIn = do
4853 return e
4954
5055testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
51−testOrdering pulse = do
52− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
53− return ()
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
5457
5558testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
5659testSimultaneous pulse = do
5760 let e0 = fmapMaybe (^? here) pulse
5861 e1 = fmapMaybe (^? there) pulse
5962 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
60− return ()
6163
6264-- | Test that a widget telling and event which fires at the same time it has been replaced
6365-- doesn't count along with the new widget.
test/GC.hs+4−3
@@ -27,6 +27,7 @@ import qualified Reflex.Spider.Internal as S
2727
2828import System.Exit
2929import System.Mem
30+import Data.Coerce
3031
3132main :: IO ()
3233main = do
@@ -46,7 +47,7 @@ hostPerf ref = S.runSpiderHost $ do
4647 eventToPerform <- Host.runHostFrame $ do
4748 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
4849 <- S.SpiderHostFrame
49− $ fmap ( S.merge
50+ $ fmap ( S.mergeG coerce
5051 . S.dynamicHold)
5152 $ S.hold DMap.empty
5253 -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
@@ -55,8 +56,8 @@ hostPerf ref = S.runSpiderHost $ do
5556 { S.subscriberPropagate = S.subscriberPropagate sub
5657 }
5758 return (s, o))
58− $ runIdentity <$> S.select
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
59+ $ runIdentity . runIdentity <$> S.selectG
60+ (S.fanG $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
6061 (WrapArg Request)
6162 return $ alignWith (mergeThese (<>))
6263 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
test/QueryT.hs+4
@@ -19,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
1919import Data.Semigroup
2020import Data.These
2121
22+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
23+import Data.These.Lens
24+#endif
25+
2226import Reflex
2327import Reflex.Patch.MapWithMove
2428import Test.Run
test/Reflex/Bench/Focused.hs+12
@@ -130,6 +130,16 @@ mapDynChain = iterM (return . fmap (+1))
130130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132132
133+holdDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134+holdDynChain = iterM (\d -> sample (current d) >>= flip holdDyn (updated d))
135+
136+buildDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
137+buildDynChain = iterM (\d -> do
138+ let b = fmap (+1) (current d)
139+ e = fmap (*2) (updated d)
140+ buildDynamic (sample b) e)
141+
142+
133143combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134144combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135145
@@ -308,6 +318,8 @@ dynamics :: Word -> [(String, TestCase)]
308318dynamics n =
309319 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310320 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
321+ , testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d
322+ , testE "buildDynChain" $ fmap updated $ buildDynChain n =<< d
311323 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312324 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313325 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
test/Reflex/Test/CrossImpl.hs+5
@@ -218,6 +218,11 @@ testCases =
218218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219219 let b' = pull $ sample =<< sample bb
220220 return (b', e)
221+ , (,) "foldDynWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
222+ d <- foldDyn (:) [] $
223+ pushAlways (\a -> foldDyn (:) [a] e) e
224+ let b = current (join (fmap distributeListOverDynPure d))
225+ return (b, e)
221226 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222227 bb <- hold "b" e
223228 bd <- hold never . fmap (const e) =<< headE e
test/RequesterT.hs+5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE BangPatterns #-}
23{-# LANGUAGE FlexibleContexts #-}
34{-# LANGUAGE GADTs #-}
@@ -15,6 +16,10 @@ import Data.Functor.Misc
1516import qualified Data.Map as M
1617import Data.These
1718
19+#if defined(MIN_VERSION_these_lens) || (MIN_VERSION_these(0,8,0) && !MIN_VERSION_these(0,9,0))
20+import Data.These.Lens
21+#endif
22+
1823import Reflex
1924import Reflex.Requester.Base
2025import Reflex.Requester.Class
test/hlint.hs+1
@@ -22,6 +22,7 @@ main = do
2222 , "--ignore=Use unless"
2323 , "--ignore=Reduce duplication"
2424 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
2526 ]
2627 recurseInto = and <$> sequence
2728 [ fileType ==? Directory