on develop help sign in
Merge branch 'develop' into clarify-view-docs
commit0acbdbeb1c088a0e16361326e7f103e16e6fdea5 authorJohn Ericson <John.Ericson@Obsidian.Systems> dateMon 9 Dec 2019, 18:37 (7 years ago) parent2e255e918595a1
48 files changed+897−435
.travis.yml+171 CONTRIBUTING.md+9 ChangeLog.md+41 cabal.haskell-ci+16 cabal.project+1 cabal.project.freeze+1 default.nix+17−7 dep/reflex-platform/default.nix+7 dep/reflex-platform/github.json+7 hydra.json−29 jobsets.nix−70 reflex-platform.nix−8 reflex.cabal+62−31 release.nix+36−7 src/Control/Monad/ReaderIO.hs+60 src/Data/AppendMap.hs+5−1 src/Data/FastWeakBag.hs+9−4 src/Data/Functor/Misc.hs−1 src/Data/WeakBag.hs+8−3 src/Reflex/Adjustable/Class.hs−1 src/Reflex/BehaviorWriter/Class.hs−1 src/Reflex/Class.hs+109−24 src/Reflex/Collection.hs+7 src/Reflex/Dynamic/TH.hs+1−1 src/Reflex/DynamicWriter/Class.hs−1 src/Reflex/EventWriter/Base.hs+1−1 src/Reflex/EventWriter/Class.hs−1 src/Reflex/Host/Class.hs−1 src/Reflex/NotReady/Class.hs−1 src/Reflex/Patch/IntMap.hs−8 src/Reflex/Patch/Map.hs−8 src/Reflex/PerformEvent/Base.hs+1−1 src/Reflex/PerformEvent/Class.hs−1 src/Reflex/PostBuild/Class.hs−1 src/Reflex/Profiled.hs+24−16 src/Reflex/Pure.hs+21−17 src/Reflex/Query/Base.hs−1 src/Reflex/Query/Class.hs−1 src/Reflex/Requester/Class.hs−1 src/Reflex/Spider/Internal.hs+246−179 src/Reflex/TriggerEvent/Class.hs−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+41
@@ -1,7 +1,48 @@
11# Revision history for reflex
22
3+## Unreleased
4+
5+* Data.WeakBag.traverse and Data.FastWeakBag.traverse have been
6+ renamed to Data.WeakBag.traverse_ and Data.FastWeakBag.traverse_
7+ respectively.
8+
9+## 0.6.2.4
10+
11+* Update to monoidal-containers 0.6
12+
13+## 0.6.2.3
14+
15+* Add an upper-bound to witherable
16+
17+## 0.6.2.2
18+
19+* Support these >= 1. Add `split-these` flag to control whether to use new these/semialign combination or not.
20+* Update version bounds to fix some CI failures
21+* Add travis CI configuration
22+
23+## 0.6.2.1
24+
25+* Generalize `fan` to `fanG` to take a `DMap` with non-`Identity`
26+ functor:
27+ * `fan` to `fanG`
28+ * `EventSelectorG` for `fanG` result selector.
29+
30+* Reduce the amount of unsafeCoerce in coercing newtypes under Event/Dynamic/Behavior.
31+ * Add fused ReaderIO for the purpose of coercion (ReaderT's third argument has nominal role preventing automated coerce)
32+ * Add incrementalCoercion/coerceIncremental to go with dynamicCoercion/coerceDynamic
33+
34+* Generalize merging functions:
35+ `merge` to `mergeG`,
36+ `mergeIncremental` to `mergeIncrementalG`,
37+ `mergeIncrementalWithMove` to `mergeIncrementalWithMoveG`.
38+
39+* Generalize distribute function:
40+ `distributeDMapOverDynPure` to `distributeDMapOverDynPureG`,
41+
342## 0.6.2.0
443
44+* Fix `holdDyn` so that it is lazy in its event argument
45+ These produce `DMap`s whose values needn't be `Identity`.
546* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
647* Fix `holdDyn` so that it is lazy in its event argument.
748
cabal.haskell-cinew file+16
@@ -0,0 +1,16 @@
1+distribution: xenial
2+benchmarks: False
3+unconstrained: False
4+installed: -all
5+
6+-- https://github.com/haskell/cabal/issues/6106
7+install-dependencies: False
8+
9+constraint-set no-th
10+ constraints: reflex -use-template-haskell
11+
12+constraint-set old-these
13+ constraints: these <1
14+
15+constraint-set old-witherable
16+ constraints: witherable <0.3.2
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
cabal.project.freezenew file+1
@@ -0,0 +1 @@
1+constraints: any.text < 1.2.4.0
default.nix+17−7
@@ -6,13 +6,19 @@
66, template-haskell , these, time, transformers
77, transformers-compat, unbounded-delays, prim-uniq
88, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers, witherable
9+, monoidal-containers, witherable, profunctors
10+, splitThese ? (semialign != null), semialign ? null, these-lens ? null
1011, useTemplateHaskell ? true
1112}:
1213mkDerivation {
1314 pname = "reflex";
14− version = "0.6.2.0";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
15+ version = "0.6.2.4";
16+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
17+ "default.nix"
18+ "release.nix"
19+ ".git"
20+ "dist"
21+ ])) ./.;
1622 libraryHaskellDepends = [
1723 base bifunctors containers dependent-map dependent-sum
1824 exception-transformers lens
@@ -22,17 +28,21 @@ mkDerivation {
2228 base bifunctors containers deepseq dependent-map dependent-sum
2329 mtl ref-tf split transformers data-default
2430 random time unbounded-delays monoidal-containers witherable
31+ profunctors
2532 ] ++ (if ghc.isGhcjs or false then [
2633 ghcjs-base
2734 ] else []) ++ (if !useTemplateHaskell then [] else [
2835 haskell-src-exts haskell-src-meta
29− ]);
36+ ]) ++ (if splitThese then [
37+ semialign
38+ these-lens
39+ ] else []);
3040 testHaskellDepends = if ghc.isGhcjs or false then [] else [
3141 hlint filepath directory filemanip
3242 ];
33− configureFlags = if useTemplateHaskell then [] else [
34− "-f-use-template-haskell"
35− ];
43+ configureFlags =
44+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
45+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
3646 homepage = "https://github.com/reflex-frp/reflex";
3747 description = "Higher-order Functional Reactive Programming";
3848 license = stdenv.lib.licenses.bsd3;
dep/reflex-platform/default.nixnew file+7
@@ -0,0 +1,7 @@
1+# DO NOT HAND-EDIT THIS FILE
2+import ((import <nixpkgs> {}).fetchFromGitHub (
3+ let json = builtins.fromJSON (builtins.readFile ./github.json);
4+ in { inherit (json) owner repo rev sha256;
5+ private = json.private or false;
6+ }
7+))
dep/reflex-platform/github.jsonnew file+7
@@ -0,0 +1,7 @@
1+{
2+ "owner": "reflex-frp",
3+ "repo": "reflex-platform",
4+ "branch": "jailbreakTheselens",
5+ "rev": "4284ed527c96c373538dc7e31776d9a50ca6aa91",
6+ "sha256": "1m1nkxq7ng9wnqbd77xn8d81f2iz8g61y5m0r3gp0gi0q59jm8ay"
7+}
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+62−31
@@ -1,5 +1,5 @@
11Name: reflex
2−Version: 0.6.2.0
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,39 +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− constraints-extras >= 0.2,
48− 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,
4958 data-default >= 0.5 && < 0.8,
5059 dependent-map >= 0.3 && < 0.4,
5160 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
5262 lens >= 4.7 && < 5,
5363 monad-control >= 1.0.1 && < 1.1,
54− monoidal-containers == 0.4.*,
5564 mtl >= 2.1 && < 2.3,
5665 prim-uniq >= 0.1.0.1 && < 0.2,
57− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
5868 random == 1.1.*,
5969 ref-tf == 0.4.*,
6070 reflection == 2.1.*,
6171 semigroupoids >= 4.0 && < 6,
62− semigroups >= 0.16 && < 0.19,
6372 stm >= 2.4 && < 2.6,
6473 syb >= 0.5 && < 0.8,
65− these >= 0.4 && < 0.9,
6674 time >= 1.4 && < 1.9,
67− transformers >= 0.2,
68− transformers-compat >= 0.3,
75+ transformers >= 0.5.6.0 && < 0.6,
6976 unbounded-delays >= 0.1.0.0 && < 0.2,
70− 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
7186
7287 exposed-modules:
88+ Control.Monad.ReaderIO
7389 Data.AppendMap,
7490 Data.FastMutableIntMap,
7591 Data.FastWeakBag,
@@ -143,7 +159,7 @@ library
143159 other-extensions: TemplateHaskell
144160 else
145161 build-depends:
146− dependent-sum == 0.6.*
162+ dependent-sum >= 0.6 && < 0.7
147163
148164 if flag(fast-weak) && impl(ghcjs)
149165 cpp-options: -DGHCJS_FAST_WEAK
@@ -160,14 +176,14 @@ test-suite semantics
160176 base,
161177 bifunctors,
162178 containers,
163− deepseq >= 1.3 && < 1.5,
179+ deepseq,
164180 dependent-map,
165181 dependent-sum,
166182 mtl,
167183 ref-tf,
168184 reflex,
169185 split,
170− transformers >= 0.3
186+ transformers
171187 other-modules:
172188 Reflex.Bench.Focused
173189 Reflex.Plan.Pure
@@ -186,7 +202,7 @@ test-suite CrossImpl
186202 containers,
187203 dependent-map,
188204 dependent-sum,
189− deepseq >= 1.3 && < 1.5,
205+ deepseq,
190206 mtl,
191207 transformers,
192208 ref-tf,
@@ -205,7 +221,7 @@ test-suite hlint
205221 , directory
206222 , filepath
207223 , filemanip
208− , hlint
224+ , hlint < 2.1 || >= 2.2.2
209225 if impl(ghcjs)
210226 buildable: False
211227
@@ -215,7 +231,7 @@ test-suite EventWriterT
215231 hs-source-dirs: test
216232 build-depends: base
217233 , containers
218− , deepseq >= 1.3 && < 1.5
234+ , deepseq
219235 , dependent-map
220236 , dependent-sum
221237 , lens
@@ -224,6 +240,10 @@ test-suite EventWriterT
224240 , transformers
225241 , reflex
226242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
227247 other-modules:
228248 Reflex.Test
229249 Reflex.TestPlan
@@ -231,13 +251,14 @@ test-suite EventWriterT
231251 Reflex.Plan.Pure
232252 Test.Run
233253
254+
234255test-suite RequesterT
235256 type: exitcode-stdio-1.0
236257 main-is: RequesterT.hs
237258 hs-source-dirs: test
238259 build-depends: base
239260 , containers
240− , deepseq >= 1.3 && < 1.5
261+ , deepseq
241262 , dependent-sum
242263 , dependent-map
243264 , lens
@@ -246,9 +267,14 @@ test-suite RequesterT
246267 , transformers
247268 , reflex
248269 , ref-tf
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
249274 other-modules:
250275 Reflex.TestPlan
251276 Reflex.Plan.Pure
277+ Test.Run
252278
253279test-suite QueryT
254280 type: exitcode-stdio-1.0
@@ -258,15 +284,18 @@ test-suite QueryT
258284 , containers
259285 , dependent-map
260286 , dependent-sum
261− , deepseq >= 1.3 && < 1.5
287+ , deepseq
262288 , lens
263289 , monoidal-containers
264290 , mtl
265291 , ref-tf
266292 , reflex
267− , semigroups
268293 , these
269294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
270299 other-modules:
271300 Test.Run
272301 Reflex.TestPlan
@@ -281,14 +310,16 @@ test-suite GC-Semantics
281310 , containers
282311 , dependent-sum
283312 , dependent-map
284− , deepseq >= 1.3 && < 1.5
313+ , deepseq
285314 , mtl
286315 , these
287316 , transformers
288317 , reflex
289318 , ref-tf
290− if impl(ghc < 8)
291− build-depends: semigroups
319+
320+ if flag(split-these)
321+ build-depends: semialign
322+
292323 other-modules:
293324 Reflex.Plan.Pure
294325 Reflex.Plan.Reflex
@@ -301,7 +332,7 @@ test-suite rootCleanup
301332 hs-source-dirs: test
302333 build-depends: base
303334 , containers
304− , deepseq >= 1.3 && < 1.5
335+ , deepseq
305336 , dependent-sum
306337 , mtl
307338 , reflex
@@ -320,8 +351,8 @@ benchmark spider-bench
320351 build-depends:
321352 base,
322353 containers,
323− criterion >= 1.1 && < 1.6,
324− deepseq >= 1.3 && < 1.5,
354+ criterion,
355+ deepseq,
325356 dependent-map,
326357 dependent-sum,
327358 ref-tf,
@@ -330,7 +361,7 @@ benchmark spider-bench
330361 reflex,
331362 split,
332363 stm,
333− transformers >= 0.3
364+ transformers
334365 other-modules:
335366 Reflex.TestPlan
336367 Reflex.Plan.Reflex
@@ -344,9 +375,9 @@ benchmark saulzar-bench
344375 ghc-options: -Wall -O2 -rtsopts -threaded
345376 build-depends:
346377 base,
347− containers >= 0.5 && < 0.7,
348− criterion >= 1.1 && < 1.6,
349− deepseq >= 1.3 && < 1.5,
378+ containers,
379+ criterion,
380+ deepseq,
350381 dependent-map,
351382 dependent-sum,
352383 loch-th,
@@ -358,7 +389,7 @@ benchmark saulzar-bench
358389 split,
359390 stm,
360391 time,
361− transformers >= 0.3
392+ transformers
362393 other-modules:
363394 Reflex.TestPlan
364395 Reflex.Plan.Reflex
release.nix+36−7
@@ -1,9 +1,38 @@
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+ hsPkgs = lib.genAttrs compilers (ghc: let
21+ ghc' = reflex-platform.${ghc}.override {
22+ overrides = self: super: {
23+ reflex-dontUseTemplateHaskell = self.callPackage ./. { useTemplateHaskell = false; };
24+ reflex = self.callPackage ./. { useTemplateHaskell = true; };
25+ };
26+ };
27+ in {
28+ inherit (ghc') reflex reflex-dontUseTemplateHaskell;
29+ });
30+ in hsPkgs // {
31+ cache = reflex-platform.pinBuildInputs "reflex-${system}"
32+ (lib.concatLists (map builtins.attrValues (builtins.attrValues hsPkgs)));
33+ });
34+
35+ metaCache = native-reflex-platform.pinBuildInputs "reflex-everywhere"
36+ (map (a: a.cache) (builtins.attrValues perPlatform));
37+
38+in perPlatform // { inherit metaCache; }
src/Control/Monad/ReaderIO.hsnew file+60
@@ -0,0 +1,60 @@
1+{-# language RoleAnnotations #-}
2+{-# language MultiParamTypeClasses #-}
3+{-# language FlexibleInstances #-}
4+{-# language CPP #-}
5+module Control.Monad.ReaderIO
6+ (
7+ ReaderIO (..)
8+ )
9+ where
10+
11+import Control.Monad.Fix
12+#if MIN_VERSION_base(4,10,0)
13+import Control.Applicative
14+#endif
15+import Control.Monad
16+import Control.Monad.Reader.Class
17+import Control.Monad.IO.Class
18+
19+-- | An approximate clone of @RIO@ from the @rio@ package, but not based on
20+-- @ReaderT@. The trouble with @ReaderT@ is that its third type argument has a
21+-- @nominal@ role, so we can't coerce through it when it's wrapped in some
22+-- other @data@ type. Ugh.
23+newtype ReaderIO e a = ReaderIO { runReaderIO :: e -> IO a }
24+type role ReaderIO representational representational
25+
26+instance Functor (ReaderIO e) where
27+ fmap = liftM
28+ {-# INLINE fmap #-}
29+ a <$ m = m >> pure a
30+ {-# INLINE (<$) #-}
31+
32+instance Applicative (ReaderIO e) where
33+ pure a = ReaderIO $ \_ -> pure a
34+ {-# INLINE pure #-}
35+ (<*>) = ap
36+ {-# INLINE (<*>) #-}
37+#if MIN_VERSION_base(4,10,0)
38+ liftA2 = liftM2
39+ {-# INLINE liftA2 #-}
40+#endif
41+ (*>) = (>>)
42+ {-# INLINE (*>) #-}
43+
44+instance Monad (ReaderIO e) where
45+ ReaderIO q >>= f = ReaderIO $ \e -> q e >>= \a -> runReaderIO (f a) e
46+ {-# INLINE (>>=) #-}
47+
48+instance MonadFix (ReaderIO e) where
49+ mfix f = ReaderIO $ \e -> mfix $ \r -> runReaderIO (f r) e
50+ {-# INLINE mfix #-}
51+
52+instance MonadIO (ReaderIO e) where
53+ liftIO m = ReaderIO $ \_ -> m
54+ {-# INLINE liftIO #-}
55+
56+instance MonadReader e (ReaderIO e) where
57+ ask = ReaderIO pure
58+ {-# INLINE ask #-}
59+ local f (ReaderIO m) = ReaderIO (m . f)
60+ {-# INLINE local #-}
src/Data/AppendMap.hs+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−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE LambdaCase #-}
98{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE PatternSynonyms #-}
src/Data/WeakBag.hs+8−3
@@ -15,6 +15,7 @@ module Data.WeakBag
1515 , singleton
1616 , insert
1717 , traverse
18+ , traverse_
1819 , remove
1920 -- * Internal functions
2021 -- These will not always be available.
@@ -99,18 +100,22 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99100 ticket <- insert a bag wbRef finalizer
100101 return (bag, ticket)
101102
102−{-# INLINE traverse #-}
103+{-# INLINE traverse_ #-}
103104-- | Visit every node in the given list. If new nodes are appended during the
104105-- traversal, they will not be visited. Every live node that was in the list
105106-- when the traversal began will be visited exactly once; however, no guarantee
106107-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
108+traverse_ :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109+traverse_ (WeakBag _ children) f = {-# SCC "traverse" #-} do
109110 cs <- liftIO $ readIORef children
110111 forM_ cs $ \c -> do
111112 ma <- liftIO $ deRefWeak c
112113 mapM_ f ma
113114
115+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
116+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
117+traverse = traverse_
118+
114119-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115120-- on the same 'WeakBagTicket'.
116121{-# INLINE remove #-}
src/Reflex/Adjustable/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
src/Reflex/BehaviorWriter/Class.hs−1
@@ -5,7 +5,6 @@ Description: This module defines the 'MonadBehaviorWriter' class
55{-# LANGUAGE CPP #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE UndecidableInstances #-}
109#ifdef USE_REFLEX_OPTIMIZER
1110{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Class.hs+109−24
@@ -7,7 +7,6 @@
77{-# LANGUAGE FunctionalDependencies #-}
88{-# LANGUAGE GADTs #-}
99{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
1110{-# LANGUAGE NoMonomorphismRestriction #-}
1211{-# LANGUAGE PolyKinds #-}
1312{-# LANGUAGE RankNTypes #-}
@@ -17,6 +16,7 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
2020#ifdef USE_REFLEX_OPTIMIZER
2121{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2222#endif
@@ -36,16 +36,21 @@ module Reflex.Class
3636 , coerceBehavior
3737 , coerceEvent
3838 , coerceDynamic
39+ , coerceIncremental
3940 , MonadSample (..)
4041 , MonadHold (..)
4142 -- ** 'fan' related types
4243 , EventSelector (..)
44+ , EventSelectorG (..)
4345 , EventSelectorInt (..)
4446 -- * Convenience functions
4547 , constDyn
4648 , pushAlways
4749 -- ** Combining 'Event's
4850 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
4954 , mergeMap
5055 , mergeIntMap
5156 , mergeMapIncremental
@@ -60,6 +65,7 @@ module Reflex.Class
6065 , alignEventWithMaybe
6166 -- ** Breaking up 'Event's
6267 , splitE
68+ , fan
6369 , fanEither
6470 , fanThese
6571 , fanMap
@@ -84,6 +90,7 @@ module Reflex.Class
8490 , gate
8591 -- ** Combining 'Dynamic's
8692 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
8794 , distributeListOverDyn
8895 , distributeListOverDynWith
8996 , zipDyn
@@ -166,6 +173,17 @@ module Reflex.Class
166173 , slowHeadE
167174 ) where
168175
176+#ifdef MIN_VERSION_semialign
177+import Prelude hiding (zip, zipWith)
178+
179+#if MIN_VERSION_these(0,8,0)
180+import Data.These.Combinators (justThese)
181+#endif
182+#if MIN_VERSION_semialign(1,1,0)
183+import Data.Zip (Zip (..))
184+#endif
185+#endif
186+
169187import Control.Applicative
170188import Control.Monad.Identity
171189import Control.Monad.Reader
@@ -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
@@ -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/DynamicWriter/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE CPP #-}
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE UndecidableInstances #-}
87#ifdef USE_REFLEX_OPTIMIZER
98{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/EventWriter/Base.hs+1−1
@@ -52,7 +52,7 @@ import qualified Data.Map as Map
5252import Data.Semigroup
5353import Data.Some (Some)
5454import Data.Tuple
55−import Data.Type.Equality hiding (apply)
55+import Data.Type.Equality
5656
5757import Unsafe.Coerce
5858
src/Reflex/EventWriter/Class.hs−1
@@ -2,7 +2,6 @@
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76#ifdef USE_REFLEX_OPTIMIZER
87{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE RankNTypes #-}
98{-# LANGUAGE RoleAnnotations #-}
109{-# LANGUAGE ScopedTypeVariables #-}
src/Reflex/NotReady/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleContexts #-}
44{-# LANGUAGE FlexibleInstances #-}
55{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
98#ifdef USE_REFLEX_OPTIMIZER
src/Reflex/Patch/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/PerformEvent/Base.hs+1−1
@@ -141,7 +141,7 @@ hostPerformEventT a = do
141141 case mToPerform of
142142 Nothing -> return [result']
143143 Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
144+ responses <- runHostFrame $ traverseRequesterData (Identity <$>) toPerform
145145 mrt <- readRef responseTrigger
146146 let followupEventTriggers = case mrt of
147147 Just rt -> [rt :=> Identity responses]
src/Reflex/PerformEvent/Class.hs−1
@@ -5,7 +5,6 @@
55{-# LANGUAGE FlexibleContexts #-}
66{-# LANGUAGE FlexibleInstances #-}
77{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
98{-# LANGUAGE RankNTypes #-}
109{-# LANGUAGE ScopedTypeVariables #-}
1110{-# LANGUAGE TypeFamilies #-}
src/Reflex/PostBuild/Class.hs−1
@@ -4,7 +4,6 @@
44{-# LANGUAGE DataKinds #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
87{-# LANGUAGE ScopedTypeVariables #-}
98{-# LANGUAGE TypeFamilies #-}
109{-# LANGUAGE TypeOperators #-}
src/Reflex/Profiled.hs+24−16
@@ -8,6 +8,9 @@
88{-# LANGUAGE StandaloneDeriving #-}
99{-# LANGUAGE TypeFamilies #-}
1010{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE RankNTypes #-}
1114-- |
1215-- Module:
1316-- Reflex.Profiled
@@ -16,7 +19,6 @@
1619-- profiling/cost-center information.
1720module Reflex.Profiled where
1821
19−import Control.Lens hiding (children)
2022import Control.Monad
2123import Control.Monad.Exception
2224import Control.Monad.Fix
@@ -33,6 +35,7 @@ import Data.Map (Map)
3335import qualified Data.Map.Strict as Map
3436import Data.Monoid ((<>))
3537import Data.Ord
38+import Data.Profunctor.Unsafe ((#.))
3639import qualified Data.Semigroup as S
3740import Data.Type.Coercion
3841import Foreign.Ptr
@@ -77,9 +80,9 @@ getCostCentreStack = go []
7780 go (cc : l) parent
7881
7982toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
80−toCostCentreTree ccs n = do
81− ccList <- getCostCentreStack ccs
82− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
83+toCostCentreTree ccs n =
84+ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
85+ <$> getCostCentreStack ccs
8386
8487getCostCentreTree :: IO CostCentreTree
8588getCostCentreTree = do
@@ -133,27 +136,32 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
133136 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
134137 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
135138 pull = Behavior_Profiled . pull . coerce
136− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
137− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
138− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
139+ fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
140+ mergeG :: forall (k :: z -> *) q v. GCompare k
141+ => (forall a. q a -> Event (ProfiledTimeline t) (v a))
142+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
143+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
139144 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
140145 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
141146 current (Dynamic_Profiled d) = coerce $ current d
142147 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
143148 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
144149 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
145− mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
146− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
150+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
151+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
147152 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
148153 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
149154 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
150− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
151− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
152− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
153− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
154− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
155− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
156− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
155+ behaviorCoercion c =
156+ Coercion `trans` behaviorCoercion @t c `trans` Coercion
157+ eventCoercion c =
158+ Coercion `trans` eventCoercion @t c `trans` Coercion
159+ dynamicCoercion c =
160+ Coercion `trans` dynamicCoercion @t c `trans` Coercion
161+ incrementalCoercion c d =
162+ Coercion `trans` incrementalCoercion @t c d `trans` Coercion
163+ mergeIntIncremental = Event_Profiled . mergeIntIncremental .
164+ coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
157165 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
158166
159167deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
src/Reflex/Pure.hs+21−17
@@ -5,6 +5,9 @@
55{-# LANGUAGE MultiParamTypeClasses #-}
66{-# LANGUAGE ScopedTypeVariables #-}
77{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
811#ifdef USE_REFLEX_OPTIMIZER
912{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
1013#endif
@@ -28,14 +31,9 @@ module Reflex.Pure
2831 , Incremental (..)
2932 ) where
3033
31−#if !MIN_VERSION_base(4,8,0)
32−import Control.Applicative
33−#endif
34−
3534import Control.Monad
3635import Data.Dependent.Map (DMap, GCompare)
3736import qualified Data.Dependent.Map as DMap
38−import Data.Functor.Identity
3937import Data.IntMap (IntMap)
4038import qualified Data.IntMap as IntMap
4139import Data.Maybe
@@ -43,10 +41,11 @@ import Data.MemoTrie
4341import Data.Monoid
4442import Data.Type.Coercion
4543import Reflex.Class
44+import Data.Kind (Type)
4645
4746-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
4847-- with the type @/t/@.
49−data Pure t
48+data Pure (t :: Type)
5049
5150-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
5251-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
@@ -79,17 +78,19 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
7978 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
8079 -- warning because the GCompare instance is not used; however, removing the
8180 -- GCompare instance produces a different warning, due to that constraint
82− -- being present in the original class definition
81+ -- being present in the original class definition.
8382
84− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
85− merge events = Event $ memo $ \t ->
86− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
83+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
84+ -- -> DMap k q -> Event (Pure t) (DMap k v)
85+ mergeG nt events = Event $ memo $ \t ->
86+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
8787 in if DMap.null currentOccurrences
8888 then Nothing
8989 else Just currentOccurrences
9090
91− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
92− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
91+-- The instance signature doeesn't compile, leave commented for documentation
92+-- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
93+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
9394
9495 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
9596 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
@@ -112,8 +113,8 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
112113 --a) -> Incremental (Pure t) p a
113114 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114115
115− mergeIncremental = mergeIncrementalImpl
116− mergeIncrementalWithMove = mergeIncrementalImpl
116+ mergeIncrementalG = mergeIncrementalImpl
117+ mergeIncrementalWithMoveG = mergeIncrementalImpl
117118
118119 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119120
@@ -128,14 +129,17 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
128129 behaviorCoercion Coercion = Coercion
129130 eventCoercion Coercion = Coercion
130131 dynamicCoercion Coercion = Coercion
132+ incrementalCoercion Coercion Coercion = Coercion
131133
132134 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
133135
134136 mergeIntIncremental = mergeIntIncrementalImpl
135137
136−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
137−mergeIncrementalImpl i = Event $ \t ->
138− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
138+mergeIncrementalImpl :: (PatchTarget p ~ DMap k q, GCompare k)
139+ => (forall a. q a -> Event (Pure t) (v a))
140+ -> Incremental (Pure t) p -> Event (Pure t) (DMap k v)
141+mergeIncrementalImpl nt i = Event $ \t ->
142+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
139143 in if DMap.null results
140144 then Nothing
141145 else Just results
src/Reflex/Query/Base.hs−1
@@ -1,7 +1,6 @@
11{-# LANGUAGE FlexibleInstances #-}
22{-# LANGUAGE GeneralizedNewtypeDeriving #-}
33{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
54{-# LANGUAGE LambdaCase #-}
65{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE RankNTypes #-}
src/Reflex/Query/Class.hs−1
@@ -3,7 +3,6 @@
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
55{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
76{-# LANGUAGE TypeFamilies #-}
87{-# LANGUAGE UndecidableInstances #-}
98-- |
src/Reflex/Requester/Class.hs−1
@@ -6,7 +6,6 @@
66{-# LANGUAGE CPP #-}
77{-# LANGUAGE FlexibleInstances #-}
88{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE RecursiveDo #-}
1110{-# LANGUAGE TypeFamilies #-}
1211{-# LANGUAGE UndecidableInstances #-}
src/Reflex/Spider/Internal.hs+246−179
@@ -9,7 +9,6 @@
99{-# LANGUAGE GADTs #-}
1010{-# LANGUAGE GeneralizedNewtypeDeriving #-}
1111{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
1312{-# LANGUAGE MultiWayIf #-}
1413{-# LANGUAGE RankNTypes #-}
1514{-# LANGUAGE RoleAnnotations #-}
@@ -17,9 +16,15 @@
1716{-# LANGUAGE TypeFamilies #-}
1817{-# LANGUAGE TypeOperators #-}
1918{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
2024#ifdef USE_REFLEX_OPTIMIZER
2125{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
2226#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
2328-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
2429-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
2530module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -33,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
3338import Control.Monad.Exception
3439import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
3540import Control.Monad.Primitive
36−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
41+--import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
42+import Control.Monad.Reader.Class
43+import Control.Monad.IO.Class
44+import Control.Monad.ReaderIO
3745import Control.Monad.Ref
3846import Data.Align
3947import Data.Coerce
@@ -49,6 +57,7 @@ import Data.GADT.Compare
4957import Data.IntMap.Strict (IntMap)
5058import qualified Data.IntMap.Strict as IntMap
5159import Data.IORef
60+import Data.Kind (Type)
5261import Data.Maybe hiding (mapMaybe)
5362import Data.Monoid ((<>))
5463import Data.Proxy
@@ -63,6 +72,15 @@ import System.IO.Unsafe
6372import System.Mem.Weak
6473import Unsafe.Coerce
6574
75+#ifdef MIN_VERSION_semialign
76+#if MIN_VERSION_these(0,8,0)
77+import Data.These.Combinators (justThese)
78+#endif
79+#if MIN_VERSION_semialign(1,1,0)
80+import Data.Zip (Zip (..))
81+#endif
82+#endif
83+
6684#ifdef DEBUG_CYCLES
6785import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
6886import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -76,6 +94,7 @@ import qualified Data.FastWeakBag as FastWeakBag
7694import Data.Reflection
7795import 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
@@ -192,12 +211,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
192211
193212newNodeId :: IO Int
194213newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
195−
196−{-# NOINLINE unsafeNodeId #-}
197−unsafeNodeId :: a -> Int
198−unsafeNodeId a = unsafePerformIO $ do
199− touch a
200− newNodeId
201214#endif
202215
203216--------------------------------------------------------------------------------
@@ -301,9 +314,10 @@ cacheEvent e =
301314#else
302315 Event $
303316#endif
304− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
305− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
306− in \sub -> {-# SCC "cacheEvent" #-} do
317+ unsafePerformIO $ do
318+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
319+ <- newIORef emptyFastWeak
320+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
307321#ifdef DEBUG_TRACE_EVENTS
308322 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
309323#endif
@@ -318,9 +332,9 @@ cacheEvent e =
318332 scheduleClear occRef
319333 propagateFast a subscribers
320334 , subscriberInvalidateHeight = \old -> do
321− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
335+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
322336 , subscriberRecalculateHeight = \new -> do
323− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
337+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
324338 }
325339 when (isJust occ) $ do
326340 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
@@ -369,7 +383,7 @@ eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed
369383eventNever :: Event x a
370384eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
371385
372−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
386+eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k v -> Event x (v a)
373387eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
374388
375389eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -421,14 +435,14 @@ newSubscriberHold h = return $ Subscriber
421435 , subscriberRecalculateHeight = \_ -> return ()
422436 }
423437
424−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
438+newSubscriberFan :: forall x k v. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k v -> IO (Subscriber x (DMap k v))
425439newSubscriberFan subscribed = return $ Subscriber
426440 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
427441 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
428442 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
429443 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
430444 scheduleClear $ fanSubscribedOccurrence subscribed
431− let f _ (Pair (Identity v) subsubs) = do
445+ let f _ (Pair v subsubs) = do
432446 propagate v $ _fanSubscribedChildren_list subsubs
433447 return $ Constant ()
434448 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -436,12 +450,12 @@ newSubscriberFan subscribed = return $ Subscriber
436450 , subscriberInvalidateHeight = \old -> do
437451 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
438452 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
439− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
453+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
440454 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
441455 , subscriberRecalculateHeight = \new -> do
442456 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
443457 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
444− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
458+ forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse_ (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
445459 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
446460 }
447461
@@ -457,7 +471,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
457471 oldHeight <- readIORef $ switchSubscribedHeight subscribed
458472 when (oldHeight /= invalidHeight) $ do
459473 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
460− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
474+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
461475 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
462476 , subscriberRecalculateHeight = \new -> do
463477 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -480,8 +494,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
480494 Nothing -> do
481495 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
482496 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
483− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
497+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
485499 Just o -> do -- Since it's already firing, no need to adjust height
486500 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
487501 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -528,14 +542,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
528542propagate a subscribers = withIncreasedDepth $ do
529543 -- 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
530544 --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
545+ WeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
532546
533547-- | Propagate everything at the current height
534548propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
535549propagateFast a subscribers = withIncreasedDepth $ do
536550 -- 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
537551 --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
552+ FastWeakBag.traverse_ subscribers $ \s -> subscriberPropagate s a
539553
540554--------------------------------------------------------------------------------
541555-- EventSubscribed
@@ -544,6 +558,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
544558toAny :: a -> Any
545559toAny = unsafeCoerce
546560
561+-- Why do we use Any here, instead of just giving eventSubscribedRetained an
562+-- existential type? Sadly, GHC does not currently know how to unbox types
563+-- with existentially quantified fields. So instead we just coerce values
564+-- to type Any on the way in. Since we never coerce them back, this is
565+-- perfectly safe.
547566data EventSubscribed x = EventSubscribed
548567 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
549568 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -576,7 +595,7 @@ eventSubscribedNever = EventSubscribed
576595#endif
577596 }
578597
579−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
598+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
580599eventSubscribedFan !subscribed = EventSubscribed
581600 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
582601 , eventSubscribedRetained = toAny subscribed
@@ -667,7 +686,7 @@ behaviorPull !p = Behavior $ do
667686 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
668687 parentsRef <- liftIO $ newIORef []
669688 holdInits <- askBehaviorHoldInits
670− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
689+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
671690 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
672691 parents <- liftIO $ readIORef parentsRef
673692 let subscribed = PullSubscribed
@@ -702,42 +721,46 @@ readBehaviorUntracked b = do
702721-- Dynamic
703722--------------------------------------------------------------------------------
704723
705−data Dynamic x p = Dynamic
706− { dynamicCurrent :: !(Behavior x (PatchTarget p))
724+type DynamicS x p = Dynamic x (PatchTarget p) p
725+
726+data Dynamic x target p = Dynamic
727+ { dynamicCurrent :: !(Behavior x target)
707728 , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
708729 }
709730
710−dynamicHold :: Hold x p -> Dynamic x p
731+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
732+
733+
734+
735+
736+dynamicHold :: Hold x p -> DynamicS x p
711737dynamicHold !h = Dynamic
712738 { dynamicCurrent = behaviorHold h
713739 , dynamicUpdated = eventHold h
714740 }
715741
716−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
742+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
717743dynamicHoldIdentity = dynamicHold
718744
719−dynamicConst :: PatchTarget p -> Dynamic x p
745+dynamicConst :: PatchTarget p -> DynamicS x p
720746dynamicConst !a = Dynamic
721747 { dynamicCurrent = behaviorConst a
722748 , dynamicUpdated = eventNever
723749 }
724750
725−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
751+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
726752dynamicDyn !d = Dynamic
727753 { dynamicCurrent = behaviorDyn d
728754 , dynamicUpdated = eventDyn d
729755 }
730756
731−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
757+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
732758dynamicDynIdentity = dynamicDyn
733759
734760--------------------------------------------------------------------------------
735761-- Combinators
736762--------------------------------------------------------------------------------
737763
738−--TODO: Figure out why certain things are not 'representational', then make them
739−--representational so we can use coerce
740−
741764--type role Hold representational
742765data Hold x p
743766 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -758,20 +781,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
758781
759782-- | Stores all global data relevant to a particular Spider timeline; only one
760783-- value should exist for each type @x@
761−data SpiderTimelineEnv x = SpiderTimelineEnv
784+newtype SpiderTimelineEnv x = STE {unSTE :: SpiderTimelineEnv' x}
785+-- We implement SpiderTimelineEnv with a newtype wrapper so
786+-- we can get the coercions we want safely.
787+type role SpiderTimelineEnv nominal
788+
789+data SpiderTimelineEnv' x = SpiderTimelineEnv
762790 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
763791 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
764792#ifdef DEBUG
765793 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
766794#endif
767795 }
768−type role SpiderTimelineEnv nominal
796+type role SpiderTimelineEnv' phantom
769797
770798instance Eq (SpiderTimelineEnv x) where
771799 _ == _ = True -- Since only one exists of each type
772800
773801instance GEq SpiderTimelineEnv where
774− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
802+ a `geq` b = if _spiderTimeline_lock (unSTE a) == _spiderTimeline_lock (unSTE b)
775803 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
776804 else Nothing
777805
@@ -794,7 +822,7 @@ runEventM :: EventM x a -> IO a
794822runEventM = unEventM
795823
796824asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
797−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
825+asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (unSTE (spiderTimeline :: SpiderTimelineEnv x))
798826
799827class MonadIO m => Defer a m where
800828 getDeferralQueue :: m (IORef [a])
@@ -932,9 +960,9 @@ getHoldEventSubscription h = do
932960
933961type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
934962
935−--type role BehaviorM representational
936963-- BehaviorM can sample behaviors
937−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
964+newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderIO (BehaviorEnv x) a }
965+ deriving (Functor, Applicative, MonadIO, MonadFix, MonadReader (BehaviorEnv x))
938966
939967instance Monad (BehaviorM x) where
940968 {-# INLINE (>>=) #-}
@@ -985,7 +1013,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
9851013#endif
9861014 }
9871015
988−data Root x (k :: * -> *)
1016+data Root x k
9891017 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
9901018 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
9911019 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1055,25 +1083,25 @@ heightBagVerify b@(HeightBag s c) = if
10551083heightBagVerify = id
10561084#endif
10571085
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)))
1086+data FanSubscribedChildren x k v a = FanSubscribedChildren
1087+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1088+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1089+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
10621090 }
10631091
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
1092+data FanSubscribed x k v
1093+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1094+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1095+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
10681096 , fanSubscribedParent :: !(EventSubscription x)
10691097#ifdef DEBUG_NODEIDS
10701098 , fanSubscribedNodeId :: Int
10711099#endif
10721100 }
10731101
1074−data Fan x k
1075− = Fan { fanParent :: !(Event x (DMap k Identity))
1076− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1102+data Fan x k v
1103+ = Fan { fanParent :: !(Event x (DMap k v))
1104+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
10771105 }
10781106
10791107data SwitchSubscribed x a
@@ -1135,19 +1163,27 @@ instance HasSpiderTimeline x => Align (Event x) where
11351163#if MIN_VERSION_these(0, 8, 0)
11361164instance HasSpiderTimeline x => Semialign (Event x) where
11371165#endif
1138− align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1166+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1167+ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1168+
1169+#ifdef MIN_VERSION_semialign
1170+#if MIN_VERSION_semialign(1,1,0)
1171+instance HasSpiderTimeline x => Zip (Event x) where
1172+#endif
1173+ zip x y = mapMaybe justThese $ align x y
1174+#endif
11391175
11401176data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
11411177 | BuildDyn !(EventM x (PatchTarget p), Event x p)
11421178 | HoldDyn !(Hold x p)
11431179
1144−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1180+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
11451181
1146−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1182+newMapDyn :: HasSpiderTimeline x => (a -> b) -> DynamicS x (Identity a) -> DynamicS x (Identity b)
11471183newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
11481184
11491185--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)
1186+zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> DynamicS x (Identity a) -> DynamicS x (Identity b) -> DynamicS x (Identity c)
11511187zipDynWith f da db =
11521188 let eab = align (dynamicUpdated da) (dynamicUpdated db)
11531189 ec = flip push eab $ \o -> do
@@ -1170,18 +1206,12 @@ buildDynamic readV0 v' = do
11701206 return d
11711207
11721208unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1173−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1174− where x = (readV0, v')
1209+unsafeBuildDynamic readV0 v' =
1210+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
11751211
11761212-- ResultM can read behaviors and events
11771213type ResultM = EventM
11781214
1179−{-# NOINLINE unsafeNewIORef #-}
1180−unsafeNewIORef :: a -> b -> IORef b
1181−unsafeNewIORef a b = unsafePerformIO $ do
1182− touch a
1183− newIORef b
1184−
11851215instance HasSpiderTimeline x => Functor (Event x) where
11861216 fmap f = push $ return . Just . f
11871217
@@ -1194,33 +1224,42 @@ push f e = cacheEvent (pushCheap f e)
11941224
11951225{-# INLINABLE pull #-}
11961226pull :: BehaviorM x a -> Behavior x a
1197−pull a = behaviorPull $ Pull
1198− { pullCompute = a
1199− , pullValue = unsafeNewIORef a Nothing
1227+pull a = unsafePerformIO $ do
1228+ ref <- newIORef Nothing
12001229#ifdef DEBUG_NODEIDS
1201− , pullNodeId = unsafeNodeId a
1230+ nid <- newNodeId
12021231#endif
1203− }
1232+ pure $ behaviorPull $ Pull
1233+ { pullCompute = a
1234+ , pullValue = ref
1235+#ifdef DEBUG_NODEIDS
1236+ , pullNodeId = nid
1237+#endif
1238+ }
12041239
12051240{-# INLINABLE switch #-}
12061241switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1207−switch a = eventSwitch $ Switch
1208− { switchParent = a
1209− , switchSubscribed = unsafeNewIORef a Nothing
1210− }
1242+switch a = unsafePerformIO $ do
1243+ ref <- newIORef Nothing
1244+ pure $ eventSwitch $ Switch
1245+ { switchParent = a
1246+ , switchSubscribed = ref
1247+ }
12111248
12121249coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1213−coincidence a = eventCoincidence $ Coincidence
1214− { coincidenceParent = a
1215− , coincidenceSubscribed = unsafeNewIORef a Nothing
1216− }
1250+coincidence a = unsafePerformIO $ do
1251+ ref <- newIORef Nothing
1252+ pure $ eventCoincidence $ Coincidence
1253+ { coincidenceParent = a
1254+ , coincidenceSubscribed = ref
1255+ }
12171256
12181257-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
12191258run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
12201259run roots after = do
12211260 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
12221261 let t = spiderTimeline :: SpiderTimelineEnv x
1223− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1262+ result <- SpiderHost $ withMVar (_spiderTimeline_lock (unSTE t)) $ \_ -> unSpiderHost $ runFrame $ do
12241263 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
12251264 occBefore <- liftIO $ do
12261265 occBefore <- readIORef occRef
@@ -1284,9 +1323,9 @@ type WeakList a = [Weak a]
12841323withIncreasedDepth :: CanTrace x m => m a -> m a
12851324withIncreasedDepth a = do
12861325 spiderTimeline <- askSpiderTimelineEnv
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1326+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
12881327 result <- a
1289− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1328+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
12901329 return result
12911330#else
12921331withIncreasedDepth :: m a -> m a
@@ -1313,7 +1352,7 @@ traceMWhen _ b getMessage = when b $ do
13131352 message <- getMessage
13141353#ifdef DEBUG
13151354 spiderTimeline <- askSpiderTimelineEnv
1316− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1355+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
13171356#else
13181357 let d = 0
13191358#endif
@@ -1354,25 +1393,25 @@ propagateSubscriberHold h a = do
13541393data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
13551394
13561395runBehaviorM :: 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)
1396+runBehaviorM a mwi holdInits = runReaderIO (unBehaviorM a) (mwi, holdInits)
13581397
13591398askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
13601399askInvalidator = do
1361− (!m, _) <- BehaviorM ask
1400+ (!m, _) <- ask
13621401 case m of
13631402 Nothing -> return Nothing
13641403 Just (!wi, _) -> return $ Just wi
13651404
13661405askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
13671406askParentsRef = do
1368− (!m, _) <- BehaviorM ask
1407+ (!m, _) <- ask
13691408 case m of
13701409 Nothing -> return Nothing
13711410 Just (_, !p) -> return $ Just p
13721411
13731412askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
13741413askBehaviorHoldInits = do
1375− (_, !his) <- BehaviorM ask
1414+ (_, !his) <- ask
13761415 return his
13771416
13781417{-# INLINE getDynHold #-}
@@ -1417,6 +1456,9 @@ getRootSubscribed k r sub = do
14171456 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
14181457 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
14191458 writeIORef uninitRef $! uninit
1459+#ifdef DEBUG_NODEIDS
1460+ nid <- newNodeId
1461+#endif
14201462 let !subscribed = RootSubscribed
14211463 { rootSubscribedKey = k
14221464 , rootSubscribedCachedSubscribed = cached
@@ -1425,7 +1467,7 @@ getRootSubscribed k r sub = do
14251467 , rootSubscribedUninit = uninit
14261468 , rootSubscribedWeakSelf = weakSelf
14271469#ifdef DEBUG_NODEIDS
1428− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1470+ , rootSubscribedNodeId = nid
14291471#endif
14301472 }
14311473 -- If we die at the same moment that all our children die, they will
@@ -1474,16 +1516,10 @@ newFanInt = do
14741516 , _fanInt_occRef = occRef
14751517 }
14761518
1477−{-# NOINLINE unsafeNewFanInt #-}
1478−unsafeNewFanInt :: b -> FanInt x a
1479−unsafeNewFanInt b = unsafePerformIO $ do
1480− touch b
1481− newFanInt
1482−
14831519fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1484−fanInt p =
1485− let self = unsafeNewFanInt p
1486− in EventSelectorInt $ \k -> Event $ \sub -> do
1520+fanInt p = unsafePerformIO $ do
1521+ self <- newFanInt
1522+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
14871523 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
14881524 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
14891525 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1491,15 +1527,15 @@ fanInt p =
14911527 liftIO $ writeIORef (_fanInt_occRef self) m
14921528 scheduleIntClear $ _fanInt_occRef self
14931529 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
1530+ FastWeakBag.traverse_ b $ \s -> do
14951531 subscriberPropagate s v
14961532 , subscriberInvalidateHeight = \old -> do
14971533 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1498− FastWeakBag.traverse b $ \s -> do
1534+ FastWeakBag.traverse_ b $ \s -> do
14991535 subscriberInvalidateHeight s old
15001536 , subscriberRecalculateHeight = \new -> do
15011537 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1502− FastWeakBag.traverse b $ \s -> do
1538+ FastWeakBag.traverse_ b $ \s -> do
15031539 subscriberRecalculateHeight s new
15041540 }
15051541 liftIO $ do
@@ -1519,7 +1555,7 @@ fanInt p =
15191555 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
15201556
15211557{-# INLINABLE getFanSubscribed #-}
1522−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1558+getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k v -> Subscriber x (v a) -> EventM x (WeakBagTicket, FanSubscribed x k v, Maybe (v a))
15231559getFanSubscribed k f sub = do
15241560 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
15251561 case mSubscribed of
@@ -1537,13 +1573,16 @@ getFanSubscribed k f sub = do
15371573 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
15381574 occRef <- liftIO $ newIORef parentOcc
15391575 when (isJust parentOcc) $ scheduleClear occRef
1576+#ifdef DEBUG_NODEIDS
1577+ nid <- liftIO newNodeId
1578+#endif
15401579 let subscribed = FanSubscribed
15411580 { fanSubscribedCachedSubscribed = fanSubscribed f
15421581 , fanSubscribedOccurrence = occRef
15431582 , fanSubscribedParent = subscription
15441583 , fanSubscribedSubscribers = subscribersRef
15451584#ifdef DEBUG_NODEIDS
1546− , fanSubscribedNodeId = unsafeNodeId f
1585+ , fanSubscribedNodeId = nid
15471586#endif
15481587 }
15491588 let !self = (k, subscribed)
@@ -1553,7 +1592,7 @@ getFanSubscribed k f sub = do
15531592 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
15541593 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
15551594
1556−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1595+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
15571596cleanupFanSubscribed (k, subscribed) = do
15581597 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15591598 let reducedSubscribers = DMap.delete k subscribers
@@ -1565,7 +1604,7 @@ cleanupFanSubscribed (k, subscribed) = do
15651604 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
15661605
15671606{-# INLINE subscribeFanSubscribed #-}
1568−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1607+subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k v -> Subscriber x (v a) -> IO WeakBagTicket
15691608subscribeFanSubscribed k subscribed sub = do
15701609 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
15711610 case DMap.lookup k subscribers of
@@ -1603,6 +1642,9 @@ getSwitchSubscribed s sub = do
16031642 when (isJust parentOcc) $ scheduleClear occRef
16041643 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
16051644 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1645+#ifdef DEBUG_NODEIDS
1646+ nid <- liftIO newNodeId
1647+#endif
16061648 let !subscribed = SwitchSubscribed
16071649 { switchSubscribedCachedSubscribed = switchSubscribed s
16081650 , switchSubscribedOccurrence = occRef
@@ -1615,7 +1657,7 @@ getSwitchSubscribed s sub = do
16151657 , switchSubscribedCurrentParent = subscriptionRef
16161658 , switchSubscribedWeakSelf = weakSelf
16171659#ifdef DEBUG_NODEIDS
1618− , switchSubscribedNodeId = unsafeNodeId s
1660+ , switchSubscribedNodeId = nid
16191661#endif
16201662 }
16211663 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1660,6 +1702,9 @@ getCoincidenceSubscribed c sub = do
16601702 scheduleClear innerSubdRef
16611703 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
16621704 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1705+#ifdef DEBUG_NODEIDS
1706+ nid <- liftIO newNodeId
1707+#endif
16631708 let subscribed = CoincidenceSubscribed
16641709 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
16651710 , coincidenceSubscribedOccurrence = occRef
@@ -1670,7 +1715,7 @@ getCoincidenceSubscribed c sub = do
16701715 , coincidenceSubscribedInnerParent = innerSubdRef
16711716 , coincidenceSubscribedWeakSelf = weakSelf
16721717#ifdef DEBUG_NODEIDS
1673− , coincidenceSubscribedNodeId = unsafeNodeId c
1718+ , coincidenceSubscribedNodeId = nid
16741719#endif
16751720 }
16761721 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1687,26 +1732,34 @@ cleanupCoincidenceSubscribed subscribed = do
16871732subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
16881733subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
16891734
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)
1735+{-# INLINE mergeG #-}
1736+mergeG :: forall k q x v. (HasSpiderTimeline x, GCompare k)
1737+ => (forall a. q a -> Event x (v a))
1738+ -> DynamicS x (PatchDMap k q) -> Event x (DMap k v)
1739+mergeG nt d = cacheEvent (mergeCheap nt d)
16931740
16941741{-# 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)
1742+mergeWithMove :: forall k v q x. (HasSpiderTimeline x, GCompare k)
1743+ => (forall a. q a -> Event x (v a))
1744+ -> DynamicS x (PatchDMapWithMove k q) -> Event x (DMap k v)
1745+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
16971746
16981747{-# 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
1748+mergeCheap
1749+ :: forall k x q v. (HasSpiderTimeline x, GCompare k)
1750+ => (forall a. q a -> Event x (v a))
1751+ -> DynamicS x (PatchDMap k q)
1752+ -> Event x (DMap k v)
1753+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
17011754 where
1702− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1755+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
17031756 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
17041757 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
17051758 (mOldSubd, newPs) <- case me of
17061759 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
17071760 Just e -> do
17081761 let s = subscriber $ return k
1709− subscription@(EventSubscription _ subd) <- subscribe e s
1762+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
17101763 newParentHeight <- liftIO $ getEventSubscribedHeight subd
17111764 let newParent = MergeSubscribedParent subscription
17121765 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1716,28 +1769,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
17161769 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
17171770 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
17181771 foldM f ([], oldParents) $ DMap.toList p
1719− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1772+
1773+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
17201774 getInitialSubscribers initialParents subscriber = do
17211775 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17221776 let s = subscriber $ return k
1723− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1777+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17241778 height <- liftIO $ getEventSubscribedHeight parentSubd
1725− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1779+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParent subscription)
17261780 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17271781 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17281782 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17291783 )
1784+
17301785 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
17311786 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
17321787
17331788{-# 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
1789+mergeCheapWithMove :: forall k x v q. (HasSpiderTimeline x, GCompare k)
1790+ => (forall a. q a -> Event x (v a))
1791+ -> DynamicS x (PatchDMapWithMove k q)
1792+ -> Event x (DMap k v)
1793+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
17361794 where
1737− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1795+ updateMe :: MergeUpdateFunc k v x (PatchDMapWithMove k q) (MergeSubscribedParentWithMove x k)
17381796 updateMe subscriber heightBagRef oldParents p = do
17391797 -- Prepare new parents for insertion
1740− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1798+ let subscribeParent :: forall a. k a -> Event x (v a) -> EventM x (MergeSubscribedParentWithMove x k a)
17411799 subscribeParent k e = do
17421800 keyRef <- liftIO $ newIORef k
17431801 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1746,9 +1804,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17461804 newParentHeight <- getEventSubscribedHeight subd
17471805 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
17481806 return $ MergeSubscribedParentWithMove subscription keyRef
1749− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1807+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
17501808 -- 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
1809+ let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k q a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
17521810 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
17531811 Nothing -> do
17541812 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1759,46 +1817,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
17591817 return Nothing
17601818 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
17611819 return (toDelete, applyAlways p' oldParents)
1762− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1820+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
17631821 getInitialSubscribers initialParents subscriber = do
17641822 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
17651823 keyRef <- liftIO $ newIORef k
17661824 let s = subscriber $ liftIO $ readIORef keyRef
1767− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1825+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
17681826 height <- liftIO $ getEventSubscribedHeight parentSubd
1769− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1827+ return (fmap (k :=>) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
17701828 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
17711829 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
17721830 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
17731831 )
1832+
17741833 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
17751834 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
17761835
1777−type MergeUpdateFunc k x p s
1778− = (forall a. EventM x (k a) -> Subscriber x a)
1836+type MergeUpdateFunc k v x p s
1837+ = (forall a. EventM x (k a) -> Subscriber x (v a))
17791838 -> IORef HeightBag
17801839 -> DMap k s
17811840 -> p
17821841 -> EventM x ([EventSubscription x], DMap k s)
17831842
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)
1843+type MergeInitFunc k v q x s
1844+ = DMap k q
1845+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1846+ -> EventM x (DMap k v, [Height], DMap k s)
17881847
17891848type MergeDestroyFunc k s
17901849 = DMap k s
17911850 -> IO ()
17921851
1793−data Merge x k s = Merge
1852+data Merge x k v s = Merge
17941853 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
17951854 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
17961855 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1797− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1798− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1856+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1857+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
17991858 }
18001859
1801−invalidateMergeHeight :: Merge x k s -> IO ()
1860+invalidateMergeHeight :: Merge x k v s -> IO ()
18021861invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
18031862
18041863invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1808,8 +1867,7 @@ invalidateMergeHeight' heightRef sub = do
18081867 writeIORef heightRef $! invalidHeight
18091868 subscriberInvalidateHeight sub oldHeight
18101869
1811−
1812−revalidateMergeHeight :: Merge x k s -> IO ()
1870+revalidateMergeHeight :: Merge x k v s -> IO ()
18131871revalidateMergeHeight m = do
18141872 currentHeight <- readIORef $ _merge_heightRef m
18151873 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
@@ -1825,19 +1883,19 @@ revalidateMergeHeight m = do
18251883 subscriberRecalculateHeight (_merge_sub m) height
18261884 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
18271885
1828−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1886+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
18291887scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
18301888 vals <- liftIO $ readIORef $ _merge_accumRef m
18311889 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
18321890 --TODO: Assert that m is not empty
18331891 subscriberPropagate (_merge_sub m) vals
18341892
1835−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1893+mergeSubscriber :: forall x k v s a. (HasSpiderTimeline x, GCompare k) => Merge x k v s -> EventM x (k a) -> Subscriber x (v a)
18361894mergeSubscriber m getKey = Subscriber
18371895 { subscriberPropagate = \a -> do
18381896 oldM <- liftIO $ readIORef $ _merge_accumRef m
18391897 k <- getKey
1840− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1898+ let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k a oldM
18411899 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
18421900 liftIO $ writeIORef (_merge_accumRef m) $! newM
18431901 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1868,7 +1926,7 @@ mergeSubscriber m getKey = Subscriber
18681926 }
18691927
18701928--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
1929+updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k v s -> MergeUpdateFunc k v x p s -> p -> SomeMergeUpdate x
18721930updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
18731931 where updateMe = do
18741932 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1876,9 +1934,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
18761934 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
18771935 return subscriptionsToKill
18781936
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
1937+{-# INLINE mergeGCheap' #-}
1938+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
1939+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1940+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
18821941 initialParents <- readBehaviorUntracked $ dynamicCurrent d
18831942 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
18841943 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1926,11 +1985,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
19261985 , occ
19271986 )
19281987
1929−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1988+mergeInt :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19301989mergeInt = cacheEvent . mergeIntCheap
19311990
19321991{-# INLINABLE mergeIntCheap #-}
1933−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1992+mergeIntCheap :: forall x a. (HasSpiderTimeline x) => DynamicS x (PatchIntMap (Event x a)) -> Event x (IntMap a)
19341993mergeIntCheap d = Event $ \sub -> do
19351994 initialParents <- readBehaviorUntracked $ dynamicCurrent d
19361995 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2027,14 +2086,16 @@ mergeIntCheap d = Event $ \sub -> do
20272086 )
20282087
20292088newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2089+newtype EventSelectorG x k v = EventSelectorG { selectG :: forall a. k a -> Event x (v a) }
20302090
2031−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2032−fan e =
2091+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2092+fanG e = unsafePerformIO $ do
2093+ ref <- newIORef Nothing
20332094 let f = Fan
20342095 { fanParent = e
2035− , fanSubscribed = unsafeNewIORef e Nothing
2096+ , fanSubscribed = ref
20362097 }
2037− in EventSelector $ \k -> eventFan k f
2098+ pure $ EventSelectorG $ \k -> eventFan k f
20382099
20392100runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
20402101runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2088,7 +2149,7 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
20882149-- | Run an event action outside of a frame
20892150runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
20902151runFrame a = SpiderHost $ do
2091− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2152+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
20922153 let go = do
20932154 result <- a
20942155 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
@@ -2146,7 +2207,7 @@ runFrame a = SpiderHost $ do
21462207 myHeight <- readIORef $ switchSubscribedHeight subscribed
21472208 when (parentHeight /= myHeight) $ do
21482209 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2149− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2210+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
21502211 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
21512212 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
21522213 unsubscribe subscription
@@ -2187,7 +2248,7 @@ invalidateCoincidenceHeight subscribed = do
21872248 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
21882249 when (oldHeight /= invalidHeight) $ do
21892250 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2190− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2251+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
21912252
21922253updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
21932254updateSwitchHeight new subscribed = do
@@ -2195,7 +2256,7 @@ updateSwitchHeight new subscribed = do
21952256 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21962257 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
21972258 writeIORef (switchSubscribedHeight subscribed) $! new
2198− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2259+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
21992260
22002261recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
22012262recalculateCoincidenceHeight subscribed = do
@@ -2204,7 +2265,7 @@ recalculateCoincidenceHeight subscribed = do
22042265 height <- calculateCoincidenceHeight subscribed
22052266 when (height /= invalidHeight) $ do
22062267 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2207− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2268+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
22082269
22092270calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
22102271calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2299,7 +2360,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
22992360 fail _ = error "Dynamic does not support 'fail'"
23002361
23012362{-# 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)
2363+newJoinDyn :: HasSpiderTimeline x => DynamicS x (Identity (DynamicS x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
23032364newJoinDyn d =
23042365 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
23052366 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2449,7 +2510,7 @@ unsafeNewSpiderTimelineEnv = do
24492510#ifdef DEBUG
24502511 depthRef <- newIORef 0
24512512#endif
2452− return $ SpiderTimelineEnv
2513+ return $ STE $ SpiderTimelineEnv
24532514 { _spiderTimeline_lock = lock
24542515 , _spiderTimeline_eventEnv = env
24552516#ifdef DEBUG
@@ -2461,17 +2522,17 @@ unsafeNewSpiderTimelineEnv = do
24612522newSpiderTimeline :: IO (Some SpiderTimelineEnv)
24622523newSpiderTimeline = withSpiderTimeline (pure . Some)
24632524
2464−data LocalSpiderTimeline x s
2525+data LocalSpiderTimeline (x :: Type) s
24652526
24662527instance Reifies s (SpiderTimelineEnv x) =>
24672528 HasSpiderTimeline (LocalSpiderTimeline x s) where
2468− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2529+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
24692530
24702531localSpiderTimeline
2471− :: Proxy s
2532+ :: proxy s
24722533 -> SpiderTimelineEnv x
24732534 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2474−localSpiderTimeline _ = unsafeCoerce
2535+localSpiderTimeline _ = coerce
24752536
24762537-- | Pass a new timeline to the given function.
24772538withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2479,18 +2540,18 @@ withSpiderTimeline k = do
24792540 env <- unsafeNewSpiderTimelineEnv
24802541 reify env $ \s -> k $ localSpiderTimeline s env
24812542
2482−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2543+newtype SpiderPullM (x :: Type) a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24832544
24842545type ComputeM = EventM
24852546
2486−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2547+newtype SpiderPushM (x :: Type) a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
24872548
24882549instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
24892550 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
24902551 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
24912552 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 }
2553+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2554+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
24942555 type PullM (SpiderTimeline x) = SpiderPullM x
24952556 type PushM (SpiderTimeline x) = SpiderPushM x
24962557 {-# INLINABLE never #-}
@@ -2503,10 +2564,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25032564 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
25042565 {-# INLINABLE pull #-}
25052566 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))
2567+ {-# INLINABLE fanG #-}
2568+ fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2569+ {-# INLINABLE mergeG #-}
2570+ mergeG
2571+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2572+ => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2573+ -> DMap k q
2574+ -> R.Event (SpiderTimeline x) (DMap k v)
2575+ mergeG nt = SpiderEvent . mergeG (unSpiderEvent #. nt) . dynamicConst
25102576 {-# INLINABLE switch #-}
25112577 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
25122578 {-# INLINABLE coincidence #-}
@@ -2514,15 +2580,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25142580 {-# INLINABLE current #-}
25152581 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
25162582 {-# INLINABLE updated #-}
2517− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2583+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
25182584 {-# INLINABLE unsafeBuildDynamic #-}
25192585 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
25202586 {-# INLINABLE unsafeBuildIncremental #-}
25212587 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
2588+ {-# INLINABLE mergeIncrementalG #-}
2589+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2590+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2591+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
25262592 {-# INLINABLE currentIncremental #-}
25272593 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
25282594 {-# INLINABLE updatedIncremental #-}
@@ -2533,9 +2599,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
25332599 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
25342600 eventCoercion Coercion = Coercion
25352601 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
2602+ dynamicCoercion Coercion = Coercion
2603+ incrementalCoercion Coercion Coercion = Coercion
25372604 {-# INLINABLE mergeIntIncremental #-}
2538− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2605+ mergeIntIncremental = SpiderEvent . mergeInt . coerce
25392606 {-# INLINABLE fanInt #-}
25402607 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
25412608
@@ -2560,7 +2627,7 @@ instance MonadAtomicRef (EventM x) where
25602627 atomicModifyRef r f = liftIO $ atomicModifyRef r f
25612628
25622629-- | 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)
2630+newtype SpiderHost (x :: Type) a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25642631
25652632instance Monad (SpiderHost x) where
25662633 {-# INLINABLE (>>=) #-}
@@ -2582,7 +2649,7 @@ runSpiderHost (SpiderHost a) = a
25822649runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
25832650runSpiderHostForTimeline (SpiderHost a) _ = a
25842651
2585−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2652+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
25862653 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
25872654
25882655instance Monad (SpiderHostFrame x) where
src/Reflex/TriggerEvent/Class.hs−1
@@ -2,7 +2,6 @@
22-- new 'Event's that can be triggered from 'IO'.
33{-# LANGUAGE FlexibleInstances #-}
44{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
65{-# LANGUAGE UndecidableInstances #-}
76module Reflex.TriggerEvent.Class
87 ( TriggerEvent (..)
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