on develop help sign in
Merge branch 'develop' into hlint
commit9a2aa06288eb60296e55dc58141649914e86cdf4 authorJohn Ericson <John.Ericson@Obsidian.Systems> dateFri 13 Dec 2019, 10:43 (7 years ago) parent244c2a841b9eaa
42 files changed+981−518
.travis.yml+171 CONTRIBUTING.md+9 ChangeLog.md+61−1 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+64−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−11 src/Data/WeakBag.hs+8−3 src/Reflex/Class.hs+112−27 src/Reflex/Collection.hs+7 src/Reflex/Dynamic/TH.hs+1−1 src/Reflex/EventWriter/Base.hs+1−1 src/Reflex/Network.hs+1−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/Profiled.hs+24−16 src/Reflex/Pure.hs+21−17 src/Reflex/Query/Base.hs+5−6 src/Reflex/Requester/Base.hs+2−3 src/Reflex/Spider/Internal.hs+274−198 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+61−1
@@ -2,20 +2,80 @@
22
33## Unreleased
44
5−* Fix `holdDyn` so that it is lazy in its event argument
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+
52+* 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+64−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,51 @@ 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.*,
61+ profunctors >= 5.3 && < 5.5,
5162 lens >= 4.7 && < 5,
5263 monad-control >= 1.0.1 && < 1.1,
53− monoidal-containers == 0.4.*,
5464 mtl >= 2.1 && < 2.3,
5565 prim-uniq >= 0.1.0.1 && < 0.2,
56− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
5768 random == 1.1.*,
5869 ref-tf == 0.4.*,
5970 reflection == 2.1.*,
6071 semigroupoids >= 4.0 && < 6,
61− semigroups >= 0.16 && < 0.19,
6272 stm >= 2.4 && < 2.6,
6373 syb >= 0.5 && < 0.8,
64− these >= 0.4 && < 0.9,
6574 time >= 1.4 && < 1.9,
66− transformers >= 0.2,
67− transformers-compat >= 0.3,
75+ transformers >= 0.5.6.0 && < 0.6,
6876 unbounded-delays >= 0.1.0.0 && < 0.2,
69− witherable >= 0.2 && < 0.4
77+ witherable >= 0.3 && < 0.3.2
78+
79+ if flag(split-these)
80+ build-depends: these >= 1 && <1.1,
81+ semialign >=1 && <1.2,
82+ monoidal-containers >= 0.6 && < 0.7
83+ else
84+ build-depends: these >= 0.4 && <0.9,
85+ monoidal-containers == 0.4.0.0
7086
7187 exposed-modules:
88+ Control.Monad.ReaderIO
7289 Data.AppendMap,
7390 Data.FastMutableIntMap,
7491 Data.FastWeakBag,
@@ -133,7 +150,7 @@ library
133150 if flag(use-template-haskell)
134151 cpp-options: -DUSE_TEMPLATE_HASKELL
135152 build-depends:
136− dependent-sum >= 0.3 && < 0.5,
153+ dependent-sum >= 0.6 && < 0.7,
137154 haskell-src-exts >= 1.16 && < 1.22,
138155 haskell-src-meta >= 0.6 && < 0.9,
139156 template-haskell >= 2.9 && < 2.15
@@ -142,7 +159,7 @@ library
142159 other-extensions: TemplateHaskell
143160 else
144161 build-depends:
145− dependent-sum == 0.4.*
162+ dependent-sum >= 0.6 && < 0.7
146163
147164 if flag(fast-weak) && impl(ghcjs)
148165 cpp-options: -DGHCJS_FAST_WEAK
@@ -159,14 +176,14 @@ test-suite semantics
159176 base,
160177 bifunctors,
161178 containers,
162− deepseq >= 1.3 && < 1.5,
179+ deepseq,
163180 dependent-map,
164181 dependent-sum,
165182 mtl,
166183 ref-tf,
167184 reflex,
168185 split,
169− transformers >= 0.3
186+ transformers
170187 other-modules:
171188 Reflex.Bench.Focused
172189 Reflex.Plan.Pure
@@ -185,7 +202,7 @@ test-suite CrossImpl
185202 containers,
186203 dependent-map,
187204 dependent-sum,
188− deepseq >= 1.3 && < 1.5,
205+ deepseq,
189206 mtl,
190207 transformers,
191208 ref-tf,
@@ -204,7 +221,7 @@ test-suite hlint
204221 , directory
205222 , filepath
206223 , filemanip
207− , hlint
224+ , hlint < 2.1 || >= 2.2.2
208225 if impl(ghcjs)
209226 buildable: False
210227
@@ -214,7 +231,7 @@ test-suite EventWriterT
214231 hs-source-dirs: test
215232 build-depends: base
216233 , containers
217− , deepseq >= 1.3 && < 1.5
234+ , deepseq
218235 , dependent-map
219236 , dependent-sum
220237 , lens
@@ -223,6 +240,10 @@ test-suite EventWriterT
223240 , transformers
224241 , reflex
225242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
226247 other-modules:
227248 Reflex.Test
228249 Reflex.TestPlan
@@ -230,13 +251,14 @@ test-suite EventWriterT
230251 Reflex.Plan.Pure
231252 Test.Run
232253
254+
233255test-suite RequesterT
234256 type: exitcode-stdio-1.0
235257 main-is: RequesterT.hs
236258 hs-source-dirs: test
237259 build-depends: base
238260 , containers
239− , deepseq >= 1.3 && < 1.5
261+ , deepseq
240262 , dependent-sum
241263 , dependent-map
242264 , lens
@@ -245,9 +267,14 @@ test-suite RequesterT
245267 , transformers
246268 , reflex
247269 , ref-tf
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
248274 other-modules:
249275 Reflex.TestPlan
250276 Reflex.Plan.Pure
277+ Test.Run
251278
252279test-suite QueryT
253280 type: exitcode-stdio-1.0
@@ -257,15 +284,18 @@ test-suite QueryT
257284 , containers
258285 , dependent-map
259286 , dependent-sum
260− , deepseq >= 1.3 && < 1.5
287+ , deepseq
261288 , lens
262289 , monoidal-containers
263290 , mtl
264291 , ref-tf
265292 , reflex
266− , semigroups
267293 , these
268294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
269299 other-modules:
270300 Test.Run
271301 Reflex.TestPlan
@@ -280,14 +310,16 @@ test-suite GC-Semantics
280310 , containers
281311 , dependent-sum
282312 , dependent-map
283− , deepseq >= 1.3 && < 1.5
313+ , deepseq
284314 , mtl
285315 , these
286316 , transformers
287317 , reflex
288318 , ref-tf
289− if impl(ghc < 8)
290− build-depends: semigroups
319+
320+ if flag(split-these)
321+ build-depends: semialign
322+
291323 other-modules:
292324 Reflex.Plan.Pure
293325 Reflex.Plan.Reflex
@@ -300,7 +332,7 @@ test-suite rootCleanup
300332 hs-source-dirs: test
301333 build-depends: base
302334 , containers
303− , deepseq >= 1.3 && < 1.5
335+ , deepseq
304336 , dependent-sum
305337 , mtl
306338 , reflex
@@ -319,8 +351,8 @@ benchmark spider-bench
319351 build-depends:
320352 base,
321353 containers,
322− criterion >= 1.1 && < 1.6,
323− deepseq >= 1.3 && < 1.5,
354+ criterion,
355+ deepseq,
324356 dependent-map,
325357 dependent-sum,
326358 ref-tf,
@@ -329,7 +361,7 @@ benchmark spider-bench
329361 reflex,
330362 split,
331363 stm,
332− transformers >= 0.3
364+ transformers
333365 other-modules:
334366 Reflex.TestPlan
335367 Reflex.Plan.Reflex
@@ -343,9 +375,9 @@ benchmark saulzar-bench
343375 ghc-options: -Wall -O2 -rtsopts -threaded
344376 build-depends:
345377 base,
346− containers >= 0.5 && < 0.7,
347− criterion >= 1.1 && < 1.6,
348− deepseq >= 1.3 && < 1.5,
378+ containers,
379+ criterion,
380+ deepseq,
349381 dependent-map,
350382 dependent-sum,
351383 loch-th,
@@ -357,7 +389,7 @@ benchmark saulzar-bench
357389 split,
358390 stm,
359391 time,
360− transformers >= 0.3
392+ transformers
361393 other-modules:
362394 Reflex.TestPlan
363395 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−11
@@ -51,8 +51,7 @@ import Data.IntMap (IntMap)
5151import qualified Data.IntMap as IntMap
5252import Data.Map (Map)
5353import qualified Data.Map as Map
54−import Data.Some (Some)
55−import qualified Data.Some as Some
54+import Data.Some (Some(Some))
5655import Data.These
5756import Data.Typeable hiding (Refl)
5857
@@ -78,9 +77,6 @@ deriving instance Read k => Read (Const2 k v v)
7877instance Show k => GShow (Const2 k v) where
7978 gshowsPrec n x@(Const2 _) = showsPrec n x
8079
81−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
82− showTaggedPrec (Const2 _) = showsPrec
83−
8480instance Eq k => GEq (Const2 k v) where
8581 geq (Const2 a) (Const2 b) =
8682 if a == b
@@ -123,7 +119,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
123119-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
124120-- the keys, using a function to remove the wrapping 'Functor'
125121weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
126−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
127123
128124--------------------------------------------------------------------------------
129125-- WrapArg
@@ -213,11 +209,6 @@ instance GShow (EitherTag l r) where
213209 LeftTag -> showString "LeftTag"
214210 RightTag -> showString "RightTag"
215211
216−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
217− showTaggedPrec t n (Identity a) = case t of
218− LeftTag -> showsPrec n a
219− RightTag -> showsPrec n a
220−
221212-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
222213eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
223214eitherToDSum = \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/Class.hs+112−27
@@ -16,6 +16,7 @@
1616{-# LANGUAGE TypeFamilies #-}
1717{-# LANGUAGE TypeOperators #-}
1818{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
1920#ifdef USE_REFLEX_OPTIMIZER
2021{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2122#endif
@@ -35,16 +36,21 @@ module Reflex.Class
3536 , coerceBehavior
3637 , coerceEvent
3738 , coerceDynamic
39+ , coerceIncremental
3840 , MonadSample (..)
3941 , MonadHold (..)
4042 -- ** 'fan' related types
4143 , EventSelector (..)
44+ , EventSelectorG (..)
4245 , EventSelectorInt (..)
4346 -- * Convenience functions
4447 , constDyn
4548 , pushAlways
4649 -- ** Combining 'Event's
4750 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4854 , mergeMap
4955 , mergeIntMap
5056 , mergeMapIncremental
@@ -59,6 +65,7 @@ module Reflex.Class
5965 , alignEventWithMaybe
6066 -- ** Breaking up 'Event's
6167 , splitE
68+ , fan
6269 , fanEither
6370 , fanThese
6471 , fanMap
@@ -83,6 +90,7 @@ module Reflex.Class
8390 , gate
8491 -- ** Combining 'Dynamic's
8592 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
8694 , distributeListOverDyn
8795 , distributeListOverDynWith
8896 , zipDyn
@@ -165,6 +173,17 @@ module Reflex.Class
165173 , slowHeadE
166174 ) where
167175
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+
168187import Control.Applicative
169188import Control.Monad.Identity
170189import Control.Monad.Reader
@@ -192,8 +211,7 @@ import qualified Data.IntMap.Strict as IntMap
192211import Data.List.NonEmpty (NonEmpty (..))
193212import Data.Map (Map)
194213import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
195−import Data.Some (Some)
196−import qualified Data.Some as Some
214+import Data.Some (Some(Some))
197215import Data.String
198216import Data.These
199217import Data.Type.Coercion
@@ -255,11 +273,16 @@ class ( MonadHold t (PushM t)
255273 -- | Merge a collection of events; the resulting 'Event' will only occur if at
256274 -- least one input event is occurring, and will contain all of the input keys
257275 -- that are occurring simultaneously
258− 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+
259281 -- | Efficiently fan-out an event to many destinations. You should save the
260− -- 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
261283 -- create child events
262− 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+
263286 -- | Create an 'Event' that will occur whenever the currently-selected input
264287 -- 'Event' occurs
265288 switch :: Behavior t (Event t a) -> Event t a
@@ -277,9 +300,14 @@ class ( MonadHold t (PushM t)
277300 -- that value.
278301 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
279302 -- | Create a merge whose parents can change over time
280− 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)
281307 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
282− 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)
283311 -- | Extract the 'Behavior' component of an 'Incremental'
284312 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
285313 -- | Extract the 'Event' component of an 'Incremental'
@@ -295,9 +323,25 @@ class ( MonadHold t (PushM t)
295323 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
296324 -- occurrence type
297325 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)
298330 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
299331 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
300332
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+
301345--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
302346-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
303347-- keys simultaneously.
@@ -316,6 +360,12 @@ coerceEvent = coerceWith $ eventCoercion Coercion
316360coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
317361coerceDynamic = coerceWith $ dynamicCoercion Coercion
318362
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+
319369-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
320370-- __must__ change when and only when the 'Event' fires, such that the
321371-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
@@ -485,6 +535,17 @@ newtype EventSelector t k = EventSelector
485535 select :: forall a. k a -> Event t a
486536 }
487537
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+
488549-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
489550-- filtering by key.
490551newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
@@ -629,11 +690,7 @@ instance (Num a, Reflex t) => Num (Dynamic t a) where
629690instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
630691 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
631692 sconcat = pull . fmap sconcat . mapM sample
632−#if MIN_VERSION_semigroups(0,17,0)
633693 stimes n = fmap $ stimes n
634−#else
635− times1p n = fmap $ times1p n
636−#endif
637694
638695-- | Alias for 'mapMaybe'
639696fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
@@ -660,9 +717,12 @@ filterRight = mapMaybe (either (const Nothing) Just)
660717instance Reflex t => Alt (Event t) where
661718 ev1 <!> ev2 = leftmost [ev1, ev2]
662719
663−-- | 'Event' intersection (convenient interface to 'coincidence').
720+-- | 'Event' intersection. Only occurs when both events are co-incident.
664721instance Reflex t => Apply (Event t) where
665− 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+
666726
667727-- | 'Event' intersection (convenient interface to 'coincidence').
668728instance Reflex t => Bind (Event t) where
@@ -831,11 +891,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
831891instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
832892 (<>) = alignWith (mergeThese (<>))
833893 sconcat = fmap sconcat . mergeList . toList
834−#if MIN_VERSION_semigroups(0,17,0)
835894 stimes n = fmap $ stimes n
836−#else
837− times1p n = fmap $ times1p n
838−#endif
839895
840896instance (Semigroup a, Reflex t) => Monoid (Event t a) where
841897 mempty = never
@@ -1024,6 +1080,13 @@ instance Reflex t => Semialign (Event t) where
10241080#endif
10251081 align = alignEventWithMaybe Just
10261082
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+
10271090
10281091-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
10291092-- the 'Behavior' is true at the time of occurrence.
@@ -1064,11 +1127,7 @@ zipDynWith f da db =
10641127
10651128instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
10661129 (<>) = zipDynWith (<>)
1067−#if MIN_VERSION_semigroups(0,17,0)
10681130 stimes n = fmap $ stimes n
1069−#else
1070− times1p n = fmap $ times1p n
1071−#endif
10721131
10731132instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
10741133 mconcat = distributeListOverDynWith mconcat
@@ -1079,12 +1138,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
10791138-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
10801139-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
10811140distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1082−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
10831151 [] -> constDyn DMap.empty
1084− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1152+ [k :=> v] -> DMap.singleton k <$> nt v
10851153 _ ->
1086− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1087− 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
10881156 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
10891157 olds <- sample $ current result
10901158 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -1154,9 +1222,9 @@ factorEvent
11541222 -> Event t (DSum k v)
11551223 -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
11561224factorEvent k0 kv' = do
1157− 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'
11581226 let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1159− Some.This oldKey -> case newKey `geq` oldKey of
1227+ Some oldKey -> case newKey `geq` oldKey of
11601228 Just Refl -> return Nothing
11611229 Nothing -> do
11621230 newInner <- filterEventKey newKey kv'
@@ -1557,6 +1625,23 @@ fmapCheap f = pushCheap $ return . Just . f
15571625tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
15581626tagCheap b = pushAlwaysCheap $ \_ -> sample b
15591627
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+
15601645-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
15611646{-# INLINE mergeWithCheap #-}
15621647mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
src/Reflex/Collection.hs+7
@@ -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
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/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/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/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/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−6
@@ -37,8 +37,7 @@ import Data.Map (Map)
3737import qualified Data.Map as Map
3838import Data.Monoid ((<>))
3939import qualified Data.Semigroup as S
40−import Data.Some (Some)
41−import qualified Data.Some as Some
40+import Data.Some (Some(Some))
4241import Data.These
4342
4443import Reflex.Class
@@ -144,10 +143,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
144143 liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
145144 mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
146145 liftedBs0 :: Map (Some k) [Behavior t q]
147− 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
148147 liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
149148 liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
150− 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
151150 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
152151 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
153152 accumBehaviors :: forall m'. MonadHold t m'
@@ -188,10 +187,10 @@ instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t
188187 let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
189188 liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
190189 liftedBs0 :: Map (Some k) [Behavior t q]
191− 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
192191 liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
193192 liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
194− 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 -}
195194 sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
196195 sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
197196 accumBehaviors' :: forall m'. MonadHold t m'
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/Spider/Internal.hs+274−198
@@ -16,9 +16,15 @@
1616{-# LANGUAGE TypeFamilies #-}
1717{-# LANGUAGE TypeOperators #-}
1818{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
1924#ifdef USE_REFLEX_OPTIMIZER
2025{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2126#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
2228-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2329-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2430module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -32,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
3238import Control.Monad.Exception
3339import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
3440import Control.Monad.Primitive
35−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
3645import Control.Monad.Ref
3746import Data.Align
3847import Data.Coerce
@@ -48,6 +57,7 @@ import Data.GADT.Compare
4857import Data.IntMap.Strict (IntMap)
4958import qualified Data.IntMap.Strict as IntMap
5059import Data.IORef
60+import Data.Kind (Type)
5161import Data.Maybe hiding (mapMaybe)
5262import Data.Monoid ((<>))
5363import Data.Proxy
@@ -62,6 +72,15 @@ import System.IO.Unsafe
6272import System.Mem.Weak
6373import Unsafe.Coerce
6474
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+
6584#ifdef DEBUG_CYCLES
6685import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
6786import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -73,9 +92,9 @@ import Data.Tree (Forest, Tree (..), drawForest)
7392import Data.FastWeakBag (FastWeakBag)
7493import qualified Data.FastWeakBag as FastWeakBag
7594import Data.Reflection
76−import Data.Some (Some)
77−import qualified Data.Some as Some
95+import Data.Some (Some(Some))
7896import Data.Type.Coercion
97+import Data.Profunctor.Unsafe ((#.), (.#))
7998import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
8099import qualified Data.WeakBag as WeakBag
81100import qualified Reflex.Class
@@ -84,6 +103,7 @@ import qualified Reflex.Host.Class
84103import Reflex.NotReady.Class
85104import Reflex.Patch
86105import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
106+import Reflex.PerformEvent.Base (PerformEventT)
87107
88108#ifdef DEBUG_TRACE_EVENTS
89109import qualified Data.ByteString.Char8 as BS8
@@ -192,12 +212,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
192212
193213newNodeId :: IO Int
194214newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
195−
196−{-# NOINLINE unsafeNodeId #-}
197−unsafeNodeId :: a -> Int
198−unsafeNodeId a = unsafePerformIO $ do
199− touch a
200− newNodeId
201215#endif
202216
203217--------------------------------------------------------------------------------
@@ -301,9 +315,10 @@ cacheEvent e =
301315#else
302316 Event $
303317#endif
304− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
305− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
306− in \sub -> {-# SCC "cacheEvent" #-} do
318+ unsafePerformIO $ do
319+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
320+ <- newIORef emptyFastWeak
321+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
307322#ifdef DEBUG_TRACE_EVENTS
308323 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
309324#endif
@@ -318,9 +333,9 @@ cacheEvent e =
318333 scheduleClear occRef
319334 propagateFast a subscribers
320335 , subscriberInvalidateHeight = \old -> do
321− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
336+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
322337 , subscriberRecalculateHeight = \new -> do
323− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
338+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
324339 }
325340 when (isJust occ) $ do
326341 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
@@ -369,7 +384,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
369384eventNever :: Event x a
370385eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
371386
372−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)
373388eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
374389
375390eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -421,14 +436,14 @@ newSubscriberHold h = return $ Subscriber
421436 , subscriberRecalculateHeight = \_ -> return ()
422437 }
423438
424−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))
425440newSubscriberFan subscribed = return $ Subscriber
426441 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
427442 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
428443 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
429444 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
430445 scheduleClear $ fanSubscribedOccurrence subscribed
431− let f _ (Pair (Identity v) subsubs) = do
446+ let f _ (Pair v subsubs) = do
432447 propagate v $ _fanSubscribedChildren_list subsubs
433448 return $ Constant ()
434449 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -436,12 +451,12 @@ newSubscriberFan subscribed = return $ Subscriber
436451 , subscriberInvalidateHeight = \old -> do
437452 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
438453 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
439− 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
440455 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
441456 , subscriberRecalculateHeight = \new -> do
442457 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
443458 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
444− 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
445460 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
446461 }
447462
@@ -457,7 +472,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
457472 oldHeight <- readIORef $ switchSubscribedHeight subscribed
458473 when (oldHeight /= invalidHeight) $ do
459474 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
460− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
475+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
461476 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
462477 , subscriberRecalculateHeight = \new -> do
463478 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -480,8 +495,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
480495 Nothing -> do
481496 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
482497 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
483− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
499+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
485500 Just o -> do -- Since it's already firing, no need to adjust height
486501 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
487502 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -528,14 +543,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
528543propagate a subscribers = withIncreasedDepth $ do
529544 -- 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
530545 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
531− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
546+ WeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
532547
533548-- | Propagate everything at the current height
534549propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
535550propagateFast a subscribers = withIncreasedDepth $ do
536551 -- 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
537552 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
538− FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
553+ FastWeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
539554
540555--------------------------------------------------------------------------------
541556-- EventSubscribed
@@ -544,6 +559,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
544559toAny :: a -> Any
545560toAny = unsafeCoerce
546561
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.
547567data EventSubscribed x = EventSubscribed
548568 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
549569 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -576,12 +596,12 @@ eventSubscribedNever = EventSubscribed
576596#endif
577597 }
578598
579−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
599+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
580600eventSubscribedFan !subscribed = EventSubscribed
581601 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
582602 , eventSubscribedRetained = toAny subscribed
583603#ifdef DEBUG_CYCLES
584− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
604+ , eventSubscribedGetParents = return [Some $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
585605 , eventSubscribedHasOwnHeightRef = False
586606 , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
587607#endif
@@ -594,7 +614,7 @@ eventSubscribedSwitch !subscribed = EventSubscribed
594614#ifdef DEBUG_CYCLES
595615 , eventSubscribedGetParents = do
596616 s <- readIORef $ switchSubscribedCurrentParent subscribed
597− return [Some.This $ _eventSubscription_subscribed s]
617+ return [Some $ _eventSubscription_subscribed s]
598618 , eventSubscribedHasOwnHeightRef = True
599619 , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
600620#endif
@@ -607,8 +627,8 @@ eventSubscribedCoincidence !subscribed = EventSubscribed
607627#ifdef DEBUG_CYCLES
608628 , eventSubscribedGetParents = do
609629 innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
610− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
611− innerParents = maybeToList $ fmap Some.This innerSubscription
630+ let outerParent = Some $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
631+ innerParents = maybeToList $ fmap Some innerSubscription
612632 return $ outerParent : innerParents
613633 , eventSubscribedHasOwnHeightRef = True
614634 , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
@@ -624,13 +644,13 @@ whoCreatedEventSubscribed = eventSubscribedWhoCreated
624644
625645walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
626646walkInvalidHeightParents s0 = do
627− 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
628648 h <- liftIO $ readIORef $ eventSubscribedHeightRef s
629649 when (h == invalidHeight) $ do
630650 when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
631− modify (Some.This s :)
651+ modify (Some s :)
632652 mapM_ loop =<< liftIO (eventSubscribedGetParents s)
633− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
653+ forM_ subscribers $ \(Some s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
634654 return subscribers
635655#endif
636656
@@ -658,7 +678,7 @@ behaviorPull !p = Behavior $ do
658678 val <- liftIO $ readIORef $ pullValue p
659679 case val of
660680 Just subscribed -> do
661− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedPull subscribed)) :))
681+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
662682 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
663683 liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
664684 return $ pullSubscribedValue subscribed
@@ -667,7 +687,7 @@ behaviorPull !p = Behavior $ do
667687 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
668688 parentsRef <- liftIO $ newIORef []
669689 holdInits <- askBehaviorHoldInits
670− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
690+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
671691 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
672692 parents <- liftIO $ readIORef parentsRef
673693 let subscribed = PullSubscribed
@@ -677,7 +697,7 @@ behaviorPull !p = Behavior $ do
677697 , pullSubscribedParents = parents
678698 }
679699 liftIO $ writeIORef (pullValue p) $ Just subscribed
680− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedPull subscribed)) :))
700+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedPull subscribed)) :))
681701 return a
682702
683703behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
@@ -688,7 +708,7 @@ readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
688708readHoldTracked h = do
689709 result <- liftIO $ readIORef $ holdValue h
690710 askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
691− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some.This (BehaviorSubscribedHold h)) :))
711+ askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (Some (BehaviorSubscribedHold h)) :))
692712 liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
693713 return result
694714
@@ -702,42 +722,46 @@ readBehaviorUntracked b = do
702722-- Dynamic
703723--------------------------------------------------------------------------------
704724
705−data Dynamic x p = Dynamic
706− { 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)
707729 , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
708730 }
709731
710−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
711738dynamicHold !h = Dynamic
712739 { dynamicCurrent = behaviorHold h
713740 , dynamicUpdated = eventHold h
714741 }
715742
716−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
743+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
717744dynamicHoldIdentity = dynamicHold
718745
719−dynamicConst :: PatchTarget p -> Dynamic x p
746+dynamicConst :: PatchTarget p -> DynamicS x p
720747dynamicConst !a = Dynamic
721748 { dynamicCurrent = behaviorConst a
722749 , dynamicUpdated = eventNever
723750 }
724751
725−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
752+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
726753dynamicDyn !d = Dynamic
727754 { dynamicCurrent = behaviorDyn d
728755 , dynamicUpdated = eventDyn d
729756 }
730757
731−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
758+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
732759dynamicDynIdentity = dynamicDyn
733760
734761--------------------------------------------------------------------------------
735762-- Combinators
736763--------------------------------------------------------------------------------
737764
738−--TODO: Figure out why certain things are not 'representational', then make them
739−--representational so we can use coerce
740−
741765--type role Hold representational
742766data Hold x p
743767 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -758,20 +782,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
758782
759783-- | Stores all global data relevant to a particular Spider timeline; only one
760784-- value should exist for each type @x@
761−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
762791 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
763792 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
764793#ifdef DEBUG
765794 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
766795#endif
767796 }
768−type role SpiderTimelineEnv nominal
797+type role SpiderTimelineEnv' phantom
769798
770799instance Eq (SpiderTimelineEnv x) where
771800 _ == _ = True -- Since only one exists of each type
772801
773802instance GEq SpiderTimelineEnv where
774− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
803+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
775804 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
776805 else Nothing
777806
@@ -794,7 +823,7 @@ runEventM :: EventM x a -> IO a
794823runEventM = unEventM
795824
796825asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
797−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
826+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
798827
799828class MonadIO m => Defer a m where
800829 getDeferralQueue :: m (IORef [a])
@@ -861,7 +890,7 @@ instance HasSpiderTimeline x => Defer (Some Clear) (EventM x) where
861890
862891{-# INLINE scheduleClear #-}
863892scheduleClear :: Defer (Some Clear) m => IORef (Maybe a) -> m ()
864−scheduleClear r = defer $ Some.This $ Clear r
893+scheduleClear r = defer $ Some $ Clear r
865894
866895instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
867896 {-# INLINE getDeferralQueue #-}
@@ -869,7 +898,7 @@ instance HasSpiderTimeline x => Defer (Some IntClear) (EventM x) where
869898
870899{-# INLINE scheduleIntClear #-}
871900scheduleIntClear :: Defer (Some IntClear) m => IORef (IntMap a) -> m ()
872−scheduleIntClear r = defer $ Some.This $ IntClear r
901+scheduleIntClear r = defer $ Some $ IntClear r
873902
874903instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
875904 {-# INLINE getDeferralQueue #-}
@@ -877,7 +906,7 @@ instance HasSpiderTimeline x => Defer (Some RootClear) (EventM x) where
877906
878907{-# INLINE scheduleRootClear #-}
879908scheduleRootClear :: Defer (Some RootClear) m => IORef (DMap k Identity) -> m ()
880−scheduleRootClear r = defer $ Some.This $ RootClear r
909+scheduleRootClear r = defer $ Some $ RootClear r
881910
882911instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
883912 {-# INLINE getDeferralQueue #-}
@@ -932,9 +961,9 @@ getHoldEventSubscription h = do
932961
933962type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
934963
935−--type role BehaviorM representational
936964-- BehaviorM can sample behaviors
937−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))
938967
939968instance Monad (BehaviorM x) where
940969 {-# INLINE (>>=) #-}
@@ -985,7 +1014,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
9851014#endif
9861015 }
9871016
988−data Root x (k :: * -> *)
1017+data Root x k
9891018 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
9901019 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
9911020 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1055,25 +1084,25 @@ heightBagVerify b@(HeightBag s c) = if
10551084heightBagVerify = id
10561085#endif
10571086
1058−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1059− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1060− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1061− , _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)))
10621091 }
10631092
1064−data FanSubscribed (x :: *) k
1065− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1066− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1067− , 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
10681097 , fanSubscribedParent :: !(EventSubscription x)
10691098#ifdef DEBUG_NODEIDS
10701099 , fanSubscribedNodeId :: Int
10711100#endif
10721101 }
10731102
1074−data Fan x k
1075− = Fan { fanParent :: !(Event x (DMap k Identity))
1076− , 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)))
10771106 }
10781107
10791108data SwitchSubscribed x a
@@ -1135,19 +1164,27 @@ instance HasSpiderTimeline x => Align (Event x) where
11351164#if MIN_VERSION_these(0, 8, 0)
11361165instance HasSpiderTimeline x => Semialign (Event x) where
11371166#endif
1138− 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
11391176
11401177data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11411178 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11421179 | HoldDyn !(Hold x p)
11431180
1144−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1181+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11451182
1146−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)
11471184newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
11481185
11491186--TODO: Avoid the duplication between this and R.zipDynWith
1150−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)
11511188zipDynWith f da db =
11521189 let eab = align (dynamicUpdated da) (dynamicUpdated db)
11531190 ec = flip push eab $ \o -> do
@@ -1170,18 +1207,12 @@ buildDynamic readV0 v' = do
11701207 return d
11711208
11721209unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1173−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1174− where x = (readV0, v')
1210+unsafeBuildDynamic readV0 v' =
1211+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11751212
11761213-- ResultM can read behaviors and events
11771214type ResultM = EventM
11781215
1179−{-# NOINLINE unsafeNewIORef #-}
1180−unsafeNewIORef :: a -> b -> IORef b
1181−unsafeNewIORef a b = unsafePerformIO $ do
1182− touch a
1183− newIORef b
1184−
11851216instance HasSpiderTimeline x => Functor (Event x) where
11861217 fmap f = push $ return . Just . f
11871218
@@ -1194,33 +1225,42 @@ push f e = cacheEvent (pushCheap f e)
11941225
11951226{-# INLINABLE pull #-}
11961227pull :: BehaviorM x a -> Behavior x a
1197−pull a = behaviorPull $ Pull
1198− { pullCompute = a
1199− , pullValue = unsafeNewIORef a Nothing
1228+pull a = unsafePerformIO $ do
1229+ ref <- newIORef Nothing
12001230#ifdef DEBUG_NODEIDS
1201− , pullNodeId = unsafeNodeId a
1231+ nid <- newNodeId
12021232#endif
1203− }
1233+ pure $ behaviorPull $ Pull
1234+ { pullCompute = a
1235+ , pullValue = ref
1236+#ifdef DEBUG_NODEIDS
1237+ , pullNodeId = nid
1238+#endif
1239+ }
12041240
12051241{-# INLINABLE switch #-}
12061242switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1207−switch a = eventSwitch $ Switch
1208− { switchParent = a
1209− , switchSubscribed = unsafeNewIORef a Nothing
1210− }
1243+switch a = unsafePerformIO $ do
1244+ ref <- newIORef Nothing
1245+ pure $ eventSwitch $ Switch
1246+ { switchParent = a
1247+ , switchSubscribed = ref
1248+ }
12111249
12121250coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1213−coincidence a = eventCoincidence $ Coincidence
1214− { coincidenceParent = a
1215− , coincidenceSubscribed = unsafeNewIORef a Nothing
1216− }
1251+coincidence a = unsafePerformIO $ do
1252+ ref <- newIORef Nothing
1253+ pure $ eventCoincidence $ Coincidence
1254+ { coincidenceParent = a
1255+ , coincidenceSubscribed = ref
1256+ }
12171257
12181258-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12191259run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
12201260run roots after = do
12211261 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
12221262 let t = spiderTimeline :: SpiderTimelineEnv x
1223− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1263+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
12241264 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
12251265 occBefore <- liftIO $ do
12261266 occBefore <- readIORef occRef
@@ -1284,9 +1324,9 @@ type WeakList a = [Weak a]
12841324withIncreasedDepth :: CanTrace x m => m a -> m a
12851325withIncreasedDepth a = do
12861326 spiderTimeline <- askSpiderTimelineEnv
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1327+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
12881328 result <- a
1289− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1329+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
12901330 return result
12911331#else
12921332withIncreasedDepth :: m a -> m a
@@ -1313,7 +1353,7 @@ traceMWhen _ b getMessage = when b $ do
13131353 message <- getMessage
13141354#ifdef DEBUG
13151355 spiderTimeline <- askSpiderTimelineEnv
1316− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1356+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
13171357#else
13181358 let d = 0
13191359#endif
@@ -1354,25 +1394,25 @@ propagateSubscriberHold h a = do
13541394data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
13551395
13561396runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1357−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1397+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
13581398
13591399askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
13601400askInvalidator = do
1361− (!m, _) <- BehaviorM ask
1401+ (!m, _) <- ask
13621402 case m of
13631403 Nothing -> return Nothing
13641404 Just (!wi, _) -> return $ Just wi
13651405
13661406askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
13671407askParentsRef = do
1368− (!m, _) <- BehaviorM ask
1408+ (!m, _) <- ask
13691409 case m of
13701410 Nothing -> return Nothing
13711411 Just (_, !p) -> return $ Just p
13721412
13731413askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
13741414askBehaviorHoldInits = do
1375− (_, !his) <- BehaviorM ask
1415+ (_, !his) <- ask
13761416 return his
13771417
13781418{-# INLINE getDynHold #-}
@@ -1417,6 +1457,9 @@ getRootSubscribed k r sub = do
14171457 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14181458 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14191459 writeIORef uninitRef $! uninit
1460+#ifdef DEBUG_NODEIDS
1461+ nid <- newNodeId
1462+#endif
14201463 let !subscribed = RootSubscribed
14211464 { rootSubscribedKey = k
14221465 , rootSubscribedCachedSubscribed = cached
@@ -1425,7 +1468,7 @@ getRootSubscribed k r sub = do
14251468 , rootSubscribedUninit = uninit
14261469 , rootSubscribedWeakSelf = weakSelf
14271470#ifdef DEBUG_NODEIDS
1428− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1471+ , rootSubscribedNodeId = nid
14291472#endif
14301473 }
14311474 -- If we die at the same moment that all our children die, they will
@@ -1474,16 +1517,10 @@ newFanInt = do
14741517 , _fanInt_occRef = occRef
14751518 }
14761519
1477−{-# NOINLINE unsafeNewFanInt #-}
1478−unsafeNewFanInt :: b -> FanInt x a
1479−unsafeNewFanInt b = unsafePerformIO $ do
1480− touch b
1481− newFanInt
1482−
14831520fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1484−fanInt p =
1485− let self = unsafeNewFanInt p
1486− in EventSelectorInt $ \k -> Event $ \sub -> do
1521+fanInt p = unsafePerformIO $ do
1522+ self <- newFanInt
1523+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14871524 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14881525 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14891526 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1491,15 +1528,15 @@ fanInt p =
14911528 liftIO $ writeIORef (_fanInt_occRef self) m
14921529 scheduleIntClear $ _fanInt_occRef self
14931530 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1494− FastWeakBag.traverse b $ \s -> do
1531+ FastWeakBag.traverse_ b $ \s -> do
14951532 subscriberPropagate s v
14961533 , subscriberInvalidateHeight = \old -> do
14971534 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1498− FastWeakBag.traverse b $ \s -> do
1535+ FastWeakBag.traverse_ b $ \s -> do
14991536 subscriberInvalidateHeight s old
15001537 , subscriberRecalculateHeight = \new -> do
15011538 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1502− FastWeakBag.traverse b $ \s -> do
1539+ FastWeakBag.traverse_ b $ \s -> do
15031540 subscriberRecalculateHeight s new
15041541 }
15051542 liftIO $ do
@@ -1519,7 +1556,7 @@ fanInt p =
15191556 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15201557
15211558{-# INLINABLE getFanSubscribed #-}
1522−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))
15231560getFanSubscribed k f sub = do
15241561 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15251562 case mSubscribed of
@@ -1537,13 +1574,16 @@ getFanSubscribed k f sub = do
15371574 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15381575 occRef <- liftIO $ newIORef parentOcc
15391576 when (isJust parentOcc) $ scheduleClear occRef
1577+#ifdef DEBUG_NODEIDS
1578+ nid <- liftIO newNodeId
1579+#endif
15401580 let subscribed = FanSubscribed
15411581 { fanSubscribedCachedSubscribed = fanSubscribed f
15421582 , fanSubscribedOccurrence = occRef
15431583 , fanSubscribedParent = subscription
15441584 , fanSubscribedSubscribers = subscribersRef
15451585#ifdef DEBUG_NODEIDS
1546− , fanSubscribedNodeId = unsafeNodeId f
1586+ , fanSubscribedNodeId = nid
15471587#endif
15481588 }
15491589 let !self = (k, subscribed)
@@ -1553,7 +1593,7 @@ getFanSubscribed k f sub = do
15531593 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15541594 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15551595
1556−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1596+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15571597cleanupFanSubscribed (k, subscribed) = do
15581598 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15591599 let reducedSubscribers = DMap.delete k subscribers
@@ -1565,7 +1605,7 @@ cleanupFanSubscribed (k, subscribed) = do
15651605 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15661606
15671607{-# INLINE subscribeFanSubscribed #-}
1568−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
15691609subscribeFanSubscribed k subscribed sub = do
15701610 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15711611 case DMap.lookup k subscribers of
@@ -1603,6 +1643,9 @@ getSwitchSubscribed s sub = do
16031643 when (isJust parentOcc) $ scheduleClear occRef
16041644 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16051645 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1646+#ifdef DEBUG_NODEIDS
1647+ nid <- liftIO newNodeId
1648+#endif
16061649 let !subscribed = SwitchSubscribed
16071650 { switchSubscribedCachedSubscribed = switchSubscribed s
16081651 , switchSubscribedOccurrence = occRef
@@ -1615,7 +1658,7 @@ getSwitchSubscribed s sub = do
16151658 , switchSubscribedCurrentParent = subscriptionRef
16161659 , switchSubscribedWeakSelf = weakSelf
16171660#ifdef DEBUG_NODEIDS
1618− , switchSubscribedNodeId = unsafeNodeId s
1661+ , switchSubscribedNodeId = nid
16191662#endif
16201663 }
16211664 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1660,6 +1703,9 @@ getCoincidenceSubscribed c sub = do
16601703 scheduleClear innerSubdRef
16611704 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16621705 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1706+#ifdef DEBUG_NODEIDS
1707+ nid <- liftIO newNodeId
1708+#endif
16631709 let subscribed = CoincidenceSubscribed
16641710 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16651711 , coincidenceSubscribedOccurrence = occRef
@@ -1670,7 +1716,7 @@ getCoincidenceSubscribed c sub = do
16701716 , coincidenceSubscribedInnerParent = innerSubdRef
16711717 , coincidenceSubscribedWeakSelf = weakSelf
16721718#ifdef DEBUG_NODEIDS
1673− , coincidenceSubscribedNodeId = unsafeNodeId c
1719+ , coincidenceSubscribedNodeId = nid
16741720#endif
16751721 }
16761722 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1687,26 +1733,34 @@ cleanupCoincidenceSubscribed subscribed = do
16871733subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16881734subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16891735
1690−{-# INLINE merge #-}
1691−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1692−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)
16931741
16941742{-# INLINE mergeWithMove #-}
1695−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1696−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)
16971747
16981748{-# INLINE [1] mergeCheap #-}
1699−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1700−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
17011755 where
1702− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1756+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17031757 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17041758 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17051759 (mOldSubd, newPs) <- case me of
17061760 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17071761 Just e -> do
17081762 let s = subscriber $ return k
1709− subscription@(EventSubscription _ subd) <- subscribe e s
1763+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17101764 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17111765 let newParent = MergeSubscribedParent subscription
17121766 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1716,28 +1770,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17161770 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17171771 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17181772 foldM f ([], oldParents) $ DMap.toList p
1719− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1773+
1774+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17201775 getInitialSubscribers initialParents subscriber = do
17211776 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17221777 let s = subscriber $ return k
1723− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1778+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17241779 height <- liftIO $ getEventSubscribedHeight parentSubd
1725− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1780+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
17261781 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17271782 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17281783 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17291784 )
1785+
17301786 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17311787 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17321788
17331789{-# INLINE [1] mergeCheapWithMove #-}
1734−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1735−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
17361795 where
1737− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1796+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17381797 updateMe subscriber heightBagRef oldParents p = do
17391798 -- Prepare new parents for insertion
1740− 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)
17411800 subscribeParent k e = do
17421801 keyRef <- liftIO $ newIORef k
17431802 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1746,9 +1805,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17461805 newParentHeight <- getEventSubscribedHeight subd
17471806 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17481807 return $ MergeSubscribedParentWithMove subscription keyRef
1749− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1808+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17501809 -- Collect old parents for deletion and update the keys of moved parents
1751− 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
17521811 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17531812 Nothing -> do
17541813 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1759,46 +1818,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17591818 return Nothing
17601819 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17611820 return (toDelete, applyAlways p' oldParents)
1762− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1821+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17631822 getInitialSubscribers initialParents subscriber = do
17641823 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17651824 keyRef <- liftIO $ newIORef k
17661825 let s = subscriber $ liftIO $ readIORef keyRef
1767− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1826+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17681827 height <- liftIO $ getEventSubscribedHeight parentSubd
1769− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1828+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17701829 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17711830 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17721831 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17731832 )
1833+
17741834 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17751835 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17761836
1777−type MergeUpdateFunc k x p s
1778− = (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))
17791839 -> IORef HeightBag
17801840 -> DMap k s
17811841 -> p
17821842 -> EventM x ([EventSubscription x], DMap k s)
17831843
1784−type MergeInitFunc k x s
1785− = DMap k (Event x)
1786− -> (forall a. EventM x (k a) -> Subscriber x a)
1787− -> 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)
17881848
17891849type MergeDestroyFunc k s
17901850 = DMap k s
17911851 -> IO ()
17921852
1793−data Merge x k s = Merge
1853+data Merge x k v s = Merge
17941854 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17951855 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17961856 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1797− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1798− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1857+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1858+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
17991859 }
18001860
1801−invalidateMergeHeight :: Merge x k s -> IO ()
1861+invalidateMergeHeight :: Merge x k v s -> IO ()
18021862invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18031863
18041864invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1808,8 +1868,7 @@ invalidateMergeHeight' heightRef sub = do
18081868 writeIORef heightRef $! invalidHeight
18091869 subscriberInvalidateHeight sub oldHeight
18101870
1811−
1812−revalidateMergeHeight :: Merge x k s -> IO ()
1871+revalidateMergeHeight :: Merge x k v s -> IO ()
18131872revalidateMergeHeight m = do
18141873 currentHeight <- readIORef $ _merge_heightRef m
18151874 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1825,19 +1884,19 @@ revalidateMergeHeight m = do
18251884 subscriberRecalculateHeight (_merge_sub m) height
18261885 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18271886
1828−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1887+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18291888scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18301889 vals <- liftIO $ readIORef $ _merge_accumRef m
18311890 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
18321891 --TODO: Assert that m is not empty
18331892 subscriberPropagate (_merge_sub m) vals
18341893
1835−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)
18361895mergeSubscriber m getKey = Subscriber
18371896 { subscriberPropagate = \a -> do
18381897 oldM <- liftIO $ readIORef $ _merge_accumRef m
18391898 k <- getKey
1840− 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
18411900 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18421901 liftIO $ writeIORef (_merge_accumRef m) $! newM
18431902 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1852,7 +1911,7 @@ mergeSubscriber m getKey = Subscriber
18521911 else liftIO $ do
18531912#ifdef DEBUG_CYCLES
18541913 nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1855− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1914+ stacks <- forM nodesInvolvedInCycle $ \(Some es) -> whoCreatedEventSubscribed es
18561915 let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
18571916#else
18581917 let cycleInfo = ""
@@ -1868,7 +1927,7 @@ mergeSubscriber m getKey = Subscriber
18681927 }
18691928
18701929--TODO: Be able to run as much of this as possible promptly
1871−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
18721931updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18731932 where updateMe = do
18741933 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1876,9 +1935,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18761935 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18771936 return subscriptionsToKill
18781937
1879−{-# INLINE mergeCheap' #-}
1880−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)
1881−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
18821942 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18831943 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18841944 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1926,11 +1986,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
19261986 , occ
19271987 )
19281988
1929−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)
19301990mergeInt = cacheEvent . mergeIntCheap
19311991
19321992{-# INLINABLE mergeIntCheap #-}
1933−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)
19341994mergeIntCheap d = Event $ \sub -> do
19351995 initialParents <- readBehaviorUntracked $ dynamicCurrent d
19361996 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2027,14 +2087,16 @@ mergeIntCheap d = Event $ \sub -> do
20272087 )
20282088
20292089newtype 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) }
20302091
2031−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2032−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
20332095 let f = Fan
20342096 { fanParent = e
2035− , fanSubscribed = unsafeNewIORef e Nothing
2097+ , fanSubscribed = ref
20362098 }
2037− in EventSelector $ \k -> eventFan k f
2099+ pure $ EventSelectorG $ \k -> eventFan k f
20382100
20392101runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20402102runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2088,18 +2150,18 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
20882150-- | Run an event action outside of a frame
20892151runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
20902152runFrame a = SpiderHost $ do
2091− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2153+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
20922154 let go = do
20932155 result <- a
20942156 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
20952157 return result
20962158 result <- runEventM go
20972159 toClear <- readIORef $ eventEnvClears env
2098− forM_ toClear $ \(Some.This (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
2160+ forM_ toClear $ \(Some (Clear ref)) -> {-# SCC "clear" #-} writeIORef ref Nothing
20992161 toClearInt <- readIORef $ eventEnvIntClears env
2100− forM_ toClearInt $ \(Some.This (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2162+ forM_ toClearInt $ \(Some (IntClear ref)) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
21012163 toClearRoot <- readIORef $ eventEnvRootClears env
2102− forM_ toClearRoot $ \(Some.This (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2164+ forM_ toClearRoot $ \(Some (RootClear ref)) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
21032165 toAssign <- readIORef $ eventEnvAssignments env
21042166 toReconnectRef <- newIORef []
21052167 coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
@@ -2146,7 +2208,7 @@ runFrame a = SpiderHost $ do
21462208 myHeight <- readIORef $ switchSubscribedHeight subscribed
21472209 when (parentHeight /= myHeight) $ do
21482210 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2149− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2211+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
21502212 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
21512213 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
21522214 unsubscribe subscription
@@ -2187,7 +2249,7 @@ invalidateCoincidenceHeight subscribed = do
21872249 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
21882250 when (oldHeight /= invalidHeight) $ do
21892251 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2190− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2252+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
21912253
21922254updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
21932255updateSwitchHeight new subscribed = do
@@ -2195,7 +2257,7 @@ updateSwitchHeight new subscribed = do
21952257 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21962258 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21972259 writeIORef (switchSubscribedHeight subscribed) $! new
2198− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2260+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
21992261
22002262recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
22012263recalculateCoincidenceHeight subscribed = do
@@ -2204,7 +2266,7 @@ recalculateCoincidenceHeight subscribed = do
22042266 height <- calculateCoincidenceHeight subscribed
22052267 when (height /= invalidHeight) $ do
22062268 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2207− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2269+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
22082270
22092271calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
22102272calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2299,7 +2361,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
22992361 fail _ = error "Dynamic does not support 'fail'"
23002362
23012363{-# INLINABLE newJoinDyn #-}
2302−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)
23032365newJoinDyn d =
23042366 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
23052367 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2449,7 +2511,7 @@ unsafeNewSpiderTimelineEnv = do
24492511#ifdef DEBUG
24502512 depthRef <- newIORef 0
24512513#endif
2452− return $ SpiderTimelineEnv
2514+ return $ STE $ SpiderTimelineEnv
24532515 { _spiderTimeline_lock = lock
24542516 , _spiderTimeline_eventEnv = env
24552517#ifdef DEBUG
@@ -2459,19 +2521,19 @@ unsafeNewSpiderTimelineEnv = do
24592521
24602522-- | Create a new SpiderTimelineEnv
24612523newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2462−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2524+newSpiderTimeline = withSpiderTimeline (pure . Some)
24632525
2464−data LocalSpiderTimeline x s
2526+data LocalSpiderTimeline (x :: Type) s
24652527
24662528instance Reifies s (SpiderTimelineEnv x) =>
24672529 HasSpiderTimeline (LocalSpiderTimeline x s) where
2468− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2530+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
24692531
24702532localSpiderTimeline
2471− :: Proxy s
2533+ :: proxy s
24722534 -> SpiderTimelineEnv x
24732535 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2474−localSpiderTimeline _ = unsafeCoerce
2536+localSpiderTimeline _ = coerce
24752537
24762538-- | Pass a new timeline to the given function.
24772539withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2479,18 +2541,18 @@ withSpiderTimeline k = do
24792541 env <- unsafeNewSpiderTimelineEnv
24802542 reify env $ \s -> k $ localSpiderTimeline s env
24812543
2482−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)
24832545
24842546type ComputeM = EventM
24852547
2486−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)
24872549
24882550instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24892551 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
24902552 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
24912553 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2492− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2493− 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 }
24942556 type PullM (SpiderTimeline x) = SpiderPullM x
24952557 type PushM (SpiderTimeline x) = SpiderPushM x
24962558 {-# INLINABLE never #-}
@@ -2503,10 +2565,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25032565 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
25042566 {-# INLINABLE pull #-}
25052567 pull = SpiderBehavior . pull . coerce
2506− {-# INLINABLE merge #-}
2507− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2508− {-# INLINABLE fan #-}
2509− 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
25102577 {-# INLINABLE switch #-}
25112578 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25122579 {-# INLINABLE coincidence #-}
@@ -2514,15 +2581,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25142581 {-# INLINABLE current #-}
25152582 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
25162583 {-# INLINABLE updated #-}
2517− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2584+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
25182585 {-# INLINABLE unsafeBuildDynamic #-}
25192586 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25202587 {-# INLINABLE unsafeBuildIncremental #-}
25212588 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2522− {-# INLINABLE mergeIncremental #-}
2523− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2524− {-# INLINABLE mergeIncrementalWithMove #-}
2525− 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
25262593 {-# INLINABLE currentIncremental #-}
25272594 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25282595 {-# INLINABLE updatedIncremental #-}
@@ -2533,9 +2600,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25332600 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
25342601 eventCoercion Coercion = Coercion
25352602 behaviorCoercion Coercion = Coercion
2536− 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
25372605 {-# INLINABLE mergeIntIncremental #-}
2538− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2606+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
25392607 {-# INLINABLE fanInt #-}
25402608 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
25412609
@@ -2560,7 +2628,7 @@ instance MonadAtomicRef (EventM x) where
25602628 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25612629
25622630-- | The monad for actions that manipulate a Spider timeline identified by @x@
2563−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)
25642632
25652633instance Monad (SpiderHost x) where
25662634 {-# INLINABLE (>>=) #-}
@@ -2582,7 +2650,7 @@ runSpiderHost (SpiderHost a) = a
25822650runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25832651runSpiderHostForTimeline (SpiderHost a) _ = a
25842652
2585−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2653+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25862654 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25872655
25882656instance Monad (SpiderHostFrame x) where
@@ -2638,3 +2706,11 @@ instance MonadAtomicRef (SpiderHostFrame x) where
26382706instance PrimMonad (SpiderHostFrame x) where
26392707 type PrimState (SpiderHostFrame x) = PrimState IO
26402708 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/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