on develop help sign in
Merge branch 'develop' into clarify-view-docs
0acbdbeJohn Ericson
.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!
5- [Opening Issues](#opening-issues)
5- [Opening Issues](#opening-issues)
6- [Submitting Changes](#submitting-changes)
6- [Submitting Changes](#submitting-changes)
7 - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
7 - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
8 - [Code Quality](#code-quality)
9 - [Code Quality](#code-quality)
9 - [Documentation](#documentation)
10 - [Documentation](#documentation)
10
11
@@ -30,6 +31,10 @@ One way to think about it is that your commit message should be able to complete
30#### Body
31#### Body
31For 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.
32For 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.
32
33
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+
33### Code Quality
38### Code Quality
34
39
35#### Warnings
40#### Warnings
@@ -40,6 +45,10 @@ Your pull request should add no new warnings to the project. It should also gene
40
45
41Make 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!
46Make 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!
42
47
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+
43### Documentation
52### Documentation
44
53
45#### In the code
54#### In the code
ChangeLog.md+41
@@ -1,7 +1,48 @@
1# Revision history for reflex
1# Revision history for reflex
2
2
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+
3## 0.6.2.0
42## 0.6.2.0
4
43
44+* Fix `holdDyn` so that it is lazy in its event argument
45+ These produce `DMap`s whose values needn't be `Identity`.
5* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
46* Stop using the now-deprecated `*Tag` classes (e.g., `ShowTag`).
6* Fix `holdDyn` so that it is lazy in its event argument.
47* Fix `holdDyn` so that it is lazy in its event argument.
7
48
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 @@
6, template-haskell , these, time, transformers
6, template-haskell , these, time, transformers
7, transformers-compat, unbounded-delays, prim-uniq
7, transformers-compat, unbounded-delays, prim-uniq
8, data-default, filepath, directory, filemanip, ghcjs-base
8, 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
10, useTemplateHaskell ? true
11, useTemplateHaskell ? true
11}:
12}:
12mkDerivation {
13mkDerivation {
13 pname = "reflex";
14 pname = "reflex";
14− version = "0.6.2.0";
15+ version = "0.6.2.4";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16+ src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
17+ "default.nix"
18+ "release.nix"
19+ ".git"
20+ "dist"
21+ ])) ./.;
16 libraryHaskellDepends = [
22 libraryHaskellDepends = [
17 base bifunctors containers dependent-map dependent-sum
23 base bifunctors containers dependent-map dependent-sum
18 exception-transformers lens
24 exception-transformers lens
@@ -22,17 +28,21 @@ mkDerivation {
22 base bifunctors containers deepseq dependent-map dependent-sum
28 base bifunctors containers deepseq dependent-map dependent-sum
23 mtl ref-tf split transformers data-default
29 mtl ref-tf split transformers data-default
24 random time unbounded-delays monoidal-containers witherable
30 random time unbounded-delays monoidal-containers witherable
31+ profunctors
25 ] ++ (if ghc.isGhcjs or false then [
32 ] ++ (if ghc.isGhcjs or false then [
26 ghcjs-base
33 ghcjs-base
27 ] else []) ++ (if !useTemplateHaskell then [] else [
34 ] else []) ++ (if !useTemplateHaskell then [] else [
28 haskell-src-exts haskell-src-meta
35 haskell-src-exts haskell-src-meta
29− ]);
36+ ]) ++ (if splitThese then [
37+ semialign
38+ these-lens
39+ ] else []);
30 testHaskellDepends = if ghc.isGhcjs or false then [] else [
40 testHaskellDepends = if ghc.isGhcjs or false then [] else [
31 hlint filepath directory filemanip
41 hlint filepath directory filemanip
32 ];
42 ];
33− configureFlags = if useTemplateHaskell then [] else [
43+ configureFlags =
34− "-f-use-template-haskell"
44+ stdenv.lib.optional (!useTemplateHaskell) [ "-f-use-template-haskell" ] ++
35− ];
45+ stdenv.lib.optional (!splitThese) [ "-f-split-these" ];
36 homepage = "https://github.com/reflex-frp/reflex";
46 homepage = "https://github.com/reflex-frp/reflex";
37 description = "Higher-order Functional Reactive Programming";
47 description = "Higher-order Functional Reactive Programming";
38 license = stdenv.lib.licenses.bsd3;
48 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 @@
1Name: reflex
1Name: reflex
2−Version: 0.6.2.0
2+Version: 0.6.2.4
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
4Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5License: BSD3
5License: BSD3
@@ -17,6 +17,10 @@ extra-source-files:
17 Quickref.md
17 Quickref.md
18 ChangeLog.md
18 ChangeLog.md
19
19
20+tested-with:
21+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5,
22+ GHCJS ==8.4
23+
20flag use-reflex-optimizer
24flag use-reflex-optimizer
21 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
25 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
22 default: False
26 default: False
@@ -37,39 +41,51 @@ flag fast-weak
37 default: False
41 default: False
38 manual: True
42 manual: True
39
43
44+flag split-these
45+ description: Use split these/semialign packages
46+ manual: False
47+ default: True
48+
40library
49library
41 hs-source-dirs: src
50 hs-source-dirs: src
42 build-depends:
51 build-depends:
43 MemoTrie == 0.6.*,
52 MemoTrie == 0.6.*,
44 base >= 4.9 && < 4.13,
53 base >= 4.9 && < 4.13,
45 bifunctors >= 5.2 && < 5.6,
54 bifunctors >= 5.2 && < 5.6,
46− comonad,
55+ comonad >= 5.0.4 && < 5.1,
47− constraints-extras >= 0.2,
56+ constraints-extras >= 0.3 && < 0.4,
48− containers >= 0.5 && < 0.7,
57+ containers >= 0.6 && < 0.7,
49 data-default >= 0.5 && < 0.8,
58 data-default >= 0.5 && < 0.8,
50 dependent-map >= 0.3 && < 0.4,
59 dependent-map >= 0.3 && < 0.4,
51 exception-transformers == 0.4.*,
60 exception-transformers == 0.4.*,
61+ profunctors >= 5.3 && < 5.5,
52 lens >= 4.7 && < 5,
62 lens >= 4.7 && < 5,
53 monad-control >= 1.0.1 && < 1.1,
63 monad-control >= 1.0.1 && < 1.1,
54− monoidal-containers == 0.4.*,
55 mtl >= 2.1 && < 2.3,
64 mtl >= 2.1 && < 2.3,
56 prim-uniq >= 0.1.0.1 && < 0.2,
65 prim-uniq >= 0.1.0.1 && < 0.2,
57− primitive >= 0.5 && < 0.7,
66+ primitive >= 0.5 && < 0.8,
67+ profunctors,
58 random == 1.1.*,
68 random == 1.1.*,
59 ref-tf == 0.4.*,
69 ref-tf == 0.4.*,
60 reflection == 2.1.*,
70 reflection == 2.1.*,
61 semigroupoids >= 4.0 && < 6,
71 semigroupoids >= 4.0 && < 6,
62− semigroups >= 0.16 && < 0.19,
63 stm >= 2.4 && < 2.6,
72 stm >= 2.4 && < 2.6,
64 syb >= 0.5 && < 0.8,
73 syb >= 0.5 && < 0.8,
65− these >= 0.4 && < 0.9,
66 time >= 1.4 && < 1.9,
74 time >= 1.4 && < 1.9,
67− transformers >= 0.2,
75+ transformers >= 0.5.6.0 && < 0.6,
68− transformers-compat >= 0.3,
69 unbounded-delays >= 0.1.0.0 && < 0.2,
76 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
71
86
72 exposed-modules:
87 exposed-modules:
88+ Control.Monad.ReaderIO
73 Data.AppendMap,
89 Data.AppendMap,
74 Data.FastMutableIntMap,
90 Data.FastMutableIntMap,
75 Data.FastWeakBag,
91 Data.FastWeakBag,
@@ -143,7 +159,7 @@ library
143 other-extensions: TemplateHaskell
159 other-extensions: TemplateHaskell
144 else
160 else
145 build-depends:
161 build-depends:
146− dependent-sum == 0.6.*
162+ dependent-sum >= 0.6 && < 0.7
147
163
148 if flag(fast-weak) && impl(ghcjs)
164 if flag(fast-weak) && impl(ghcjs)
149 cpp-options: -DGHCJS_FAST_WEAK
165 cpp-options: -DGHCJS_FAST_WEAK
@@ -160,14 +176,14 @@ test-suite semantics
160 base,
176 base,
161 bifunctors,
177 bifunctors,
162 containers,
178 containers,
163− deepseq >= 1.3 && < 1.5,
179+ deepseq,
164 dependent-map,
180 dependent-map,
165 dependent-sum,
181 dependent-sum,
166 mtl,
182 mtl,
167 ref-tf,
183 ref-tf,
168 reflex,
184 reflex,
169 split,
185 split,
170− transformers >= 0.3
186+ transformers
171 other-modules:
187 other-modules:
172 Reflex.Bench.Focused
188 Reflex.Bench.Focused
173 Reflex.Plan.Pure
189 Reflex.Plan.Pure
@@ -186,7 +202,7 @@ test-suite CrossImpl
186 containers,
202 containers,
187 dependent-map,
203 dependent-map,
188 dependent-sum,
204 dependent-sum,
189− deepseq >= 1.3 && < 1.5,
205+ deepseq,
190 mtl,
206 mtl,
191 transformers,
207 transformers,
192 ref-tf,
208 ref-tf,
@@ -205,7 +221,7 @@ test-suite hlint
205 , directory
221 , directory
206 , filepath
222 , filepath
207 , filemanip
223 , filemanip
208− , hlint
224+ , hlint < 2.1 || >= 2.2.2
209 if impl(ghcjs)
225 if impl(ghcjs)
210 buildable: False
226 buildable: False
211
227
@@ -215,7 +231,7 @@ test-suite EventWriterT
215 hs-source-dirs: test
231 hs-source-dirs: test
216 build-depends: base
232 build-depends: base
217 , containers
233 , containers
218− , deepseq >= 1.3 && < 1.5
234+ , deepseq
219 , dependent-map
235 , dependent-map
220 , dependent-sum
236 , dependent-sum
221 , lens
237 , lens
@@ -224,6 +240,10 @@ test-suite EventWriterT
224 , transformers
240 , transformers
225 , reflex
241 , reflex
226 , ref-tf
242 , ref-tf
243+
244+ if flag(split-these)
245+ build-depends: these-lens
246+
227 other-modules:
247 other-modules:
228 Reflex.Test
248 Reflex.Test
229 Reflex.TestPlan
249 Reflex.TestPlan
@@ -231,13 +251,14 @@ test-suite EventWriterT
231 Reflex.Plan.Pure
251 Reflex.Plan.Pure
232 Test.Run
252 Test.Run
233
253
254+
234test-suite RequesterT
255test-suite RequesterT
235 type: exitcode-stdio-1.0
256 type: exitcode-stdio-1.0
236 main-is: RequesterT.hs
257 main-is: RequesterT.hs
237 hs-source-dirs: test
258 hs-source-dirs: test
238 build-depends: base
259 build-depends: base
239 , containers
260 , containers
240− , deepseq >= 1.3 && < 1.5
261+ , deepseq
241 , dependent-sum
262 , dependent-sum
242 , dependent-map
263 , dependent-map
243 , lens
264 , lens
@@ -246,9 +267,14 @@ test-suite RequesterT
246 , transformers
267 , transformers
247 , reflex
268 , reflex
248 , ref-tf
269 , ref-tf
270+
271+ if flag(split-these)
272+ build-depends: these-lens
273+
249 other-modules:
274 other-modules:
250 Reflex.TestPlan
275 Reflex.TestPlan
251 Reflex.Plan.Pure
276 Reflex.Plan.Pure
277+ Test.Run
252
278
253test-suite QueryT
279test-suite QueryT
254 type: exitcode-stdio-1.0
280 type: exitcode-stdio-1.0
@@ -258,15 +284,18 @@ test-suite QueryT
258 , containers
284 , containers
259 , dependent-map
285 , dependent-map
260 , dependent-sum
286 , dependent-sum
261− , deepseq >= 1.3 && < 1.5
287+ , deepseq
262 , lens
288 , lens
263 , monoidal-containers
289 , monoidal-containers
264 , mtl
290 , mtl
265 , ref-tf
291 , ref-tf
266 , reflex
292 , reflex
267− , semigroups
268 , these
293 , these
269 , transformers
294 , transformers
295+
296+ if flag(split-these)
297+ build-depends: semialign, these-lens
298+
270 other-modules:
299 other-modules:
271 Test.Run
300 Test.Run
272 Reflex.TestPlan
301 Reflex.TestPlan
@@ -281,14 +310,16 @@ test-suite GC-Semantics
281 , containers
310 , containers
282 , dependent-sum
311 , dependent-sum
283 , dependent-map
312 , dependent-map
284− , deepseq >= 1.3 && < 1.5
313+ , deepseq
285 , mtl
314 , mtl
286 , these
315 , these
287 , transformers
316 , transformers
288 , reflex
317 , reflex
289 , ref-tf
318 , ref-tf
290− if impl(ghc < 8)
319+
320+ if flag(split-these)
291− build-depends: semigroups
321+ build-depends: semialign
322+
292 other-modules:
323 other-modules:
293 Reflex.Plan.Pure
324 Reflex.Plan.Pure
294 Reflex.Plan.Reflex
325 Reflex.Plan.Reflex
@@ -301,7 +332,7 @@ test-suite rootCleanup
301 hs-source-dirs: test
332 hs-source-dirs: test
302 build-depends: base
333 build-depends: base
303 , containers
334 , containers
304− , deepseq >= 1.3 && < 1.5
335+ , deepseq
305 , dependent-sum
336 , dependent-sum
306 , mtl
337 , mtl
307 , reflex
338 , reflex
@@ -320,8 +351,8 @@ benchmark spider-bench
320 build-depends:
351 build-depends:
321 base,
352 base,
322 containers,
353 containers,
323− criterion >= 1.1 && < 1.6,
354+ criterion,
324− deepseq >= 1.3 && < 1.5,
355+ deepseq,
325 dependent-map,
356 dependent-map,
326 dependent-sum,
357 dependent-sum,
327 ref-tf,
358 ref-tf,
@@ -330,7 +361,7 @@ benchmark spider-bench
330 reflex,
361 reflex,
331 split,
362 split,
332 stm,
363 stm,
333− transformers >= 0.3
364+ transformers
334 other-modules:
365 other-modules:
335 Reflex.TestPlan
366 Reflex.TestPlan
336 Reflex.Plan.Reflex
367 Reflex.Plan.Reflex
@@ -344,9 +375,9 @@ benchmark saulzar-bench
344 ghc-options: -Wall -O2 -rtsopts -threaded
375 ghc-options: -Wall -O2 -rtsopts -threaded
345 build-depends:
376 build-depends:
346 base,
377 base,
347− containers >= 0.5 && < 0.7,
378+ containers,
348− criterion >= 1.1 && < 1.6,
379+ criterion,
349− deepseq >= 1.3 && < 1.5,
380+ deepseq,
350 dependent-map,
381 dependent-map,
351 dependent-sum,
382 dependent-sum,
352 loch-th,
383 loch-th,
@@ -358,7 +389,7 @@ benchmark saulzar-bench
358 split,
389 split,
359 stm,
390 stm,
360 time,
391 time,
361− transformers >= 0.3
392+ transformers
362 other-modules:
393 other-modules:
363 Reflex.TestPlan
394 Reflex.TestPlan
364 Reflex.Plan.Reflex
395 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
2}:
2}:
3+
3let
4let
5+ native-reflex-platform = reflex-platform-fun {};
4− inherit (rp.nixpkgs) lib;
6+ inherit (native-reflex-platform.nixpkgs) lib;
5− compilers = ["ghc8_4" "ghc8_0" "ghcjs8_4" "ghcjs8_0"];
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+ ];
6−in lib.genAttrs compilers (ghc: {
20+ hsPkgs = lib.genAttrs compilers (ghc: let
21+ ghc' = reflex-platform.${ghc}.override {
22+ overrides = self: super: {
23+ reflex-dontUseTemplateHaskell = self.callPackage ./. { useTemplateHaskell = false; };
7− reflex-useTemplateHaskell = rp.${ghc}.callPackage ./. { useTemplateHaskell = true; };
24+ reflex = self.callPackage ./. { useTemplateHaskell = true; };
8− reflex = rp.${ghc}.callPackage ./. { useTemplateHaskell = false; };
25+ };
9−})
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)
32#endif
32#endif
33import qualified Data.Witherable as W
33import qualified Data.Witherable as W
34import Data.Map.Monoidal
34import Data.Map.Monoidal
35+import qualified Data.Map.Monoidal as MonoidalMap
36+
35
37
36{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
38{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
37-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
39-- | AppendMap is a synonym for 'Data.Map.Monoidal.MonoidalMap'
@@ -46,8 +48,10 @@ _unAppendMap = getMonoidalMap
46pattern AppendMap :: Map k v -> MonoidalMap k v
48pattern AppendMap :: Map k v -> MonoidalMap k v
47pattern AppendMap m = MonoidalMap m
49pattern AppendMap m = MonoidalMap m
48
50
51+#if !MIN_VERSION_witherable(0,3,2)
49instance W.Filterable (MonoidalMap k) where
52instance W.Filterable (MonoidalMap k) where
50− mapMaybe = mapMaybe
53+ mapMaybe = MonoidalMap.mapMaybe
54+#endif
51
55
52-- | Deletes a key, returning 'Nothing' if the result is empty.
56-- | Deletes a key, returning 'Nothing' if the result is empty.
53nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
57nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
src/Data/FastWeakBag.hs+9−4
@@ -18,6 +18,7 @@ module Data.FastWeakBag
18 , isEmpty
18 , isEmpty
19 , insert
19 , insert
20 , traverse
20 , traverse
21+ , traverse_
21 , remove
22 , remove
22 -- * Internal functions
23 -- * Internal functions
23 -- These will not always be available.
24 -- These will not always be available.
@@ -116,14 +117,14 @@ foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.len
116isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117#endif
118#endif
118
119
119−{-# INLINE traverse #-}
120+{-# INLINE traverse_ #-}
120-- | Visit every node in the given list. If new nodes are appended during the
121-- | Visit every node in the given list. If new nodes are appended during the
121-- traversal, they will not be visited. Every live node that was in the list
122-- traversal, they will not be visited. Every live node that was in the list
122-- when the traversal began will be visited exactly once; however, no guarantee
123-- when the traversal began will be visited exactly once; however, no guarantee
123-- is made about the order of the traversal.
124-- 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 ()
125#ifdef GHCJS_FAST_WEAK
126#ifdef GHCJS_FAST_WEAK
126−traverse wb f = do
127+traverse_ wb f = do
127 let go cursor = when (not $ js_isNull cursor) $ do
128 let go cursor = when (not $ js_isNull cursor) $ do
128 val <- liftIO $ js_getTicketValue cursor
129 val <- liftIO $ js_getTicketValue cursor
129 f $ unsafeFromRawJSVal val
130 f $ unsafeFromRawJSVal val
@@ -134,13 +135,17 @@ foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO
134--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
135--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
135foreign 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
136foreign 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
136#else
137#else
137−traverse (FastWeakBag _ children) f = {-# SCC "traverse" #-} do
138+traverse_ (FastWeakBag _ children) f = {-# SCC "traverse_" #-} do
138 cs <- liftIO $ readIORef children
139 cs <- liftIO $ readIORef children
139 forM_ cs $ \c -> do
140 forM_ cs $ \c -> do
140 ma <- liftIO $ deRefWeak c
141 ma <- liftIO $ deRefWeak c
141 mapM_ f ma
142 mapM_ f ma
142#endif
143#endif
143
144
145+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
146+traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
147+traverse = traverse_
148+
144-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
149-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
145-- on the same 'FastWeakBagTicket'.
150-- on the same 'FastWeakBagTicket'.
146{-# INLINE remove #-}
151{-# INLINE remove #-}
src/Data/Functor/Misc.hs−1
@@ -4,7 +4,6 @@
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
8{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE PatternSynonyms #-}
src/Data/WeakBag.hs+8−3
@@ -15,6 +15,7 @@ module Data.WeakBag
15 , singleton
15 , singleton
16 , insert
16 , insert
17 , traverse
17 , traverse
18+ , traverse_
18 , remove
19 , remove
19 -- * Internal functions
20 -- * Internal functions
20 -- These will not always be available.
21 -- These will not always be available.
@@ -99,18 +100,22 @@ singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99 ticket <- insert a bag wbRef finalizer
100 ticket <- insert a bag wbRef finalizer
100 return (bag, ticket)
101 return (bag, ticket)
101
102
102−{-# INLINE traverse #-}
103+{-# INLINE traverse_ #-}
103-- | Visit every node in the given list. If new nodes are appended during the
104-- | Visit every node in the given list. If new nodes are appended during the
104-- traversal, they will not be visited. Every live node that was in the list
105-- traversal, they will not be visited. Every live node that was in the list
105-- when the traversal began will be visited exactly once; however, no guarantee
106-- when the traversal began will be visited exactly once; however, no guarantee
106-- is made about the order of the traversal.
107-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108+traverse_ :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
109+traverse_ (WeakBag _ children) f = {-# SCC "traverse" #-} do
109 cs <- liftIO $ readIORef children
110 cs <- liftIO $ readIORef children
110 forM_ cs $ \c -> do
111 forM_ cs $ \c -> do
111 ma <- liftIO $ deRefWeak c
112 ma <- liftIO $ deRefWeak c
112 mapM_ f ma
113 mapM_ f ma
113
114
115+{-# DEPRECATED traverse "Use 'traverse_' instead" #-}
116+traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
117+traverse = traverse_
118+
114-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
119-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115-- on the same 'WeakBagTicket'.
120-- on the same 'WeakBagTicket'.
116{-# INLINE remove #-}
121{-# INLINE remove #-}
src/Reflex/Adjustable/Class.hs−1
@@ -3,7 +3,6 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE LambdaCase #-}
5{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
src/Reflex/BehaviorWriter/Class.hs−1
@@ -5,7 +5,6 @@ Description: This module defines the 'MonadBehaviorWriter' class
5{-# LANGUAGE CPP #-}
5{-# LANGUAGE CPP #-}
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE UndecidableInstances #-}
8{-# LANGUAGE UndecidableInstances #-}
10#ifdef USE_REFLEX_OPTIMIZER
9#ifdef USE_REFLEX_OPTIMIZER
11{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Class.hs+109−24
@@ -7,7 +7,6 @@
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8{-# LANGUAGE GADTs #-}
8{-# LANGUAGE GADTs #-}
9{-# LANGUAGE LambdaCase #-}
9{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11{-# LANGUAGE NoMonomorphismRestriction #-}
10{-# LANGUAGE NoMonomorphismRestriction #-}
12{-# LANGUAGE PolyKinds #-}
11{-# LANGUAGE PolyKinds #-}
13{-# LANGUAGE RankNTypes #-}
12{-# LANGUAGE RankNTypes #-}
@@ -17,6 +16,7 @@
17{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE TypeOperators #-}
19{-# LANGUAGE UndecidableInstances #-}
18{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE Trustworthy #-}
20#ifdef USE_REFLEX_OPTIMIZER
20#ifdef USE_REFLEX_OPTIMIZER
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
22#endif
22#endif
@@ -36,16 +36,21 @@ module Reflex.Class
36 , coerceBehavior
36 , coerceBehavior
37 , coerceEvent
37 , coerceEvent
38 , coerceDynamic
38 , coerceDynamic
39+ , coerceIncremental
39 , MonadSample (..)
40 , MonadSample (..)
40 , MonadHold (..)
41 , MonadHold (..)
41 -- ** 'fan' related types
42 -- ** 'fan' related types
42 , EventSelector (..)
43 , EventSelector (..)
44+ , EventSelectorG (..)
43 , EventSelectorInt (..)
45 , EventSelectorInt (..)
44 -- * Convenience functions
46 -- * Convenience functions
45 , constDyn
47 , constDyn
46 , pushAlways
48 , pushAlways
47 -- ** Combining 'Event's
49 -- ** Combining 'Event's
48 , leftmost
50 , leftmost
51+ , merge
52+ , mergeIncremental
53+ , mergeIncrementalWithMove
49 , mergeMap
54 , mergeMap
50 , mergeIntMap
55 , mergeIntMap
51 , mergeMapIncremental
56 , mergeMapIncremental
@@ -60,6 +65,7 @@ module Reflex.Class
60 , alignEventWithMaybe
65 , alignEventWithMaybe
61 -- ** Breaking up 'Event's
66 -- ** Breaking up 'Event's
62 , splitE
67 , splitE
68+ , fan
63 , fanEither
69 , fanEither
64 , fanThese
70 , fanThese
65 , fanMap
71 , fanMap
@@ -84,6 +90,7 @@ module Reflex.Class
84 , gate
90 , gate
85 -- ** Combining 'Dynamic's
91 -- ** Combining 'Dynamic's
86 , distributeDMapOverDynPure
92 , distributeDMapOverDynPure
93+ , distributeDMapOverDynPureG
87 , distributeListOverDyn
94 , distributeListOverDyn
88 , distributeListOverDynWith
95 , distributeListOverDynWith
89 , zipDyn
96 , zipDyn
@@ -166,6 +173,17 @@ module Reflex.Class
166 , slowHeadE
173 , slowHeadE
167 ) where
174 ) where
168
175
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+
169import Control.Applicative
187import Control.Applicative
170import Control.Monad.Identity
188import Control.Monad.Identity
171import Control.Monad.Reader
189import Control.Monad.Reader
@@ -255,11 +273,16 @@ class ( MonadHold t (PushM t)
255 -- | Merge a collection of events; the resulting 'Event' will only occur if at
273 -- | Merge a collection of events; the resulting 'Event' will only occur if at
256 -- least one input event is occurring, and will contain all of the input keys
274 -- least one input event is occurring, and will contain all of the input keys
257 -- that are occurring simultaneously
275 -- that are occurring simultaneously
276+
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
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+
259 -- | Efficiently fan-out an event to many destinations. You should save the
281 -- | 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
261 -- create child events
283 -- 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+
263 -- | Create an 'Event' that will occur whenever the currently-selected input
286 -- | Create an 'Event' that will occur whenever the currently-selected input
264 -- 'Event' occurs
287 -- 'Event' occurs
265 switch :: Behavior t (Event t a) -> Event t a
288 switch :: Behavior t (Event t a) -> Event t a
@@ -277,9 +300,14 @@ class ( MonadHold t (PushM t)
277 -- that value.
300 -- that value.
278 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
301 unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
279 -- | Create a merge whose parents can change over time
302 -- | Create a merge whose parents can change over time
303+ mergeIncrementalG :: GCompare k
304+ => (forall a. q a -> Event t (v a))
280− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
305+ -> Incremental t (PatchDMap k q)
306+ -> Event t (DMap k v)
281 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
307 -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
308+ mergeIncrementalWithMoveG :: GCompare k
309+ => (forall a. q a -> Event t (v a))
282− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
310+ -> Incremental t (PatchDMapWithMove k q) -> Event t (DMap k v)
283 -- | Extract the 'Behavior' component of an 'Incremental'
311 -- | Extract the 'Behavior' component of an 'Incremental'
284 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
312 currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
285 -- | Extract the 'Event' component of an 'Incremental'
313 -- | Extract the 'Event' component of an 'Incremental'
@@ -295,9 +323,25 @@ class ( MonadHold t (PushM t)
295 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
323 -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
296 -- occurrence type
324 -- occurrence type
297 dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
325 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)
298 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
330 mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
299 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
331 fanInt :: Event t (IntMap a) -> EventSelectorInt t a
300
332
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+
301--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
345--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
302-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
346-- | Constructs a single 'Event' out of a map of events. The output event may fire with multiple
303-- keys simultaneously.
347-- keys simultaneously.
@@ -316,6 +360,12 @@ coerceEvent = coerceWith $ eventCoercion Coercion
316coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
360coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
317coerceDynamic = coerceWith $ dynamicCoercion Coercion
361coerceDynamic = coerceWith $ dynamicCoercion Coercion
318
362
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+
319-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
369-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
320-- __must__ change when and only when the 'Event' fires, such that the
370-- __must__ change when and only when the 'Event' fires, such that the
321-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
371-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
@@ -485,6 +535,17 @@ newtype EventSelector t k = EventSelector
485 select :: forall a. k a -> Event t a
535 select :: forall a. k a -> Event t a
486 }
536 }
487
537
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+
488-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
549-- | Efficiently select an 'Event' keyed on 'Int'. This is more efficient than manually
489-- filtering by key.
550-- filtering by key.
490newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
551newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
@@ -629,11 +690,7 @@ instance (Num a, Reflex t) => Num (Dynamic t a) where
629instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
690instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
630 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
691 a <> b = pull $ liftM2 (<>) (sample a) (sample b)
631 sconcat = pull . fmap sconcat . mapM sample
692 sconcat = pull . fmap sconcat . mapM sample
632−#if MIN_VERSION_semigroups(0,17,0)
633 stimes n = fmap $ stimes n
693 stimes n = fmap $ stimes n
634−#else
635− times1p n = fmap $ times1p n
636−#endif
637
694
638-- | Alias for 'mapMaybe'
695-- | Alias for 'mapMaybe'
639fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
696fmapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
@@ -660,9 +717,12 @@ filterRight = mapMaybe (either (const Nothing) Just)
660instance Reflex t => Alt (Event t) where
717instance Reflex t => Alt (Event t) where
661 ev1 <!> ev2 = leftmost [ev1, ev2]
718 ev1 <!> ev2 = leftmost [ev1, ev2]
662
719
663−-- | 'Event' intersection (convenient interface to 'coincidence').
720+-- | 'Event' intersection. Only occurs when both events are co-incident.
664instance Reflex t => Apply (Event t) where
721instance 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+
666
726
667-- | 'Event' intersection (convenient interface to 'coincidence').
727-- | 'Event' intersection (convenient interface to 'coincidence').
668instance Reflex t => Bind (Event t) where
728instance Reflex t => Bind (Event t) where
@@ -831,11 +891,7 @@ traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
831instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
891instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
832 (<>) = alignWith (mergeThese (<>))
892 (<>) = alignWith (mergeThese (<>))
833 sconcat = fmap sconcat . mergeList . toList
893 sconcat = fmap sconcat . mergeList . toList
834−#if MIN_VERSION_semigroups(0,17,0)
835 stimes n = fmap $ stimes n
894 stimes n = fmap $ stimes n
836−#else
837− times1p n = fmap $ times1p n
838−#endif
839
895
840instance (Semigroup a, Reflex t) => Monoid (Event t a) where
896instance (Semigroup a, Reflex t) => Monoid (Event t a) where
841 mempty = never
897 mempty = never
@@ -1024,6 +1080,13 @@ instance Reflex t => Semialign (Event t) where
1024#endif
1080#endif
1025 align = alignEventWithMaybe Just
1081 align = alignEventWithMaybe Just
1026
1082
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+
1027
1090
1028-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
1091-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
1029-- the 'Behavior' is true at the time of occurrence.
1092-- the 'Behavior' is true at the time of occurrence.
@@ -1064,11 +1127,7 @@ zipDynWith f da db =
1064
1127
1065instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
1128instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
1066 (<>) = zipDynWith (<>)
1129 (<>) = zipDynWith (<>)
1067−#if MIN_VERSION_semigroups(0,17,0)
1068 stimes n = fmap $ stimes n
1130 stimes n = fmap $ stimes n
1069−#else
1070− times1p n = fmap $ times1p n
1071−#endif
1072
1131
1073instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
1132instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
1074 mconcat = distributeListOverDynWith mconcat
1133 mconcat = distributeListOverDynWith mconcat
@@ -1079,12 +1138,21 @@ instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
1079-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1138-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
1080-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1139-- through the use of multiple uses of 'zipDynWith' or 'Applicative' operators.
1081distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
1140distributeDMapOverDynPure :: 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
1083 [] -> constDyn DMap.empty
1151 [] -> constDyn DMap.empty
1084− [k :=> v] -> fmap (DMap.singleton k . Identity) v
1152+ [k :=> v] -> DMap.singleton k <$> nt v
1085 _ ->
1153 _ ->
1086− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
1154+ let getInitial = DMap.traverseWithKey (\_ -> sample . current . nt) dm
1087− edmPre = merge $ DMap.map updated dm
1155+ edmPre = mergeG getCompose $ DMap.map (Compose . updated . nt) dm
1088 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
1156 result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
1089 olds <- sample $ current result
1157 olds <- sample $ current result
1090 return $ DMap.unionWithKey (\_ _ new -> new) olds news
1158 return $ DMap.unionWithKey (\_ _ new -> new) olds news
@@ -1557,6 +1625,23 @@ fmapCheap f = pushCheap $ return . Just . f
1557tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1625tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1558tagCheap b = pushAlwaysCheap $ \_ -> sample b
1626tagCheap b = pushAlwaysCheap $ \_ -> sample b
1559
1627
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+
1560-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
1645-- | A "cheap" version of 'mergeWithCheap'. See the performance note on 'pushCheap'.
1561{-# INLINE mergeWithCheap #-}
1646{-# INLINE mergeWithCheap #-}
1562mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1647mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
src/Reflex/Collection.hs+7
@@ -27,6 +27,13 @@ module Reflex.Collection
27 , simpleList
27 , simpleList
28 ) where
28 ) where
29
29
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+
30import Control.Monad.Identity
37import Control.Monad.Identity
31import Data.Align
38import Data.Align
32import Data.Functor.Misc
39import Data.Functor.Misc
src/Reflex/Dynamic/TH.hs+1−1
@@ -81,7 +81,7 @@ mkDynPure = QuasiQuoter
81 }
81 }
82
82
83mkDynExp :: String -> Q Exp
83mkDynExp :: 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
85 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
85 Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
86 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
86 Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
87 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
87 where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
src/Reflex/DynamicWriter/Class.hs−1
@@ -3,7 +3,6 @@
3{-# LANGUAGE CPP #-}
3{-# LANGUAGE CPP #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE UndecidableInstances #-}
6{-# LANGUAGE UndecidableInstances #-}
8#ifdef USE_REFLEX_OPTIMIZER
7#ifdef USE_REFLEX_OPTIMIZER
9{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/EventWriter/Base.hs+1−1
@@ -52,7 +52,7 @@ import qualified Data.Map as Map
52import Data.Semigroup
52import Data.Semigroup
53import Data.Some (Some)
53import Data.Some (Some)
54import Data.Tuple
54import Data.Tuple
55−import Data.Type.Equality hiding (apply)
55+import Data.Type.Equality
56
56
57import Unsafe.Coerce
57import Unsafe.Coerce
58
58
src/Reflex/EventWriter/Class.hs−1
@@ -2,7 +2,6 @@
2{-# LANGUAGE CPP #-}
2{-# LANGUAGE CPP #-}
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
7#ifdef USE_REFLEX_OPTIMIZER
6#ifdef USE_REFLEX_OPTIMIZER
8{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
src/Reflex/Host/Class.hs−1
@@ -4,7 +4,6 @@
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RankNTypes #-}
9{-# LANGUAGE RoleAnnotations #-}
8{-# LANGUAGE RoleAnnotations #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
src/Reflex/NotReady/Class.hs−1
@@ -3,7 +3,6 @@
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
9#ifdef USE_REFLEX_OPTIMIZER
8#ifdef USE_REFLEX_OPTIMIZER
src/Reflex/Patch/IntMap.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveFoldable #-}
1{-# LANGUAGE DeriveFoldable #-}
3{-# LANGUAGE DeriveFunctor #-}
2{-# LANGUAGE DeriveFunctor #-}
4{-# LANGUAGE DeriveTraversable #-}
3{-# LANGUAGE DeriveTraversable #-}
@@ -33,14 +32,7 @@ instance Patch (PatchIntMap a) where
33instance Semigroup (PatchIntMap v) where
32instance Semigroup (PatchIntMap v) where
34 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
33 PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
35 -- PatchMap is idempotent, so stimes n is id for every n
34 -- PatchMap is idempotent, so stimes n is id for every n
36−#if MIN_VERSION_semigroups(0,17,0)
37 stimes = stimesIdempotentMonoid
35 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
44
36
45-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
37-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
46-- (that is, all inserts/updates), producing a @PatchIntMap b@.
38-- (that is, all inserts/updates), producing a @PatchIntMap b@.
src/Reflex/Patch/Map.hs−8
@@ -1,4 +1,3 @@
1−{-# LANGUAGE CPP #-}
2{-# LANGUAGE LambdaCase #-}
1{-# LANGUAGE LambdaCase #-}
3{-# LANGUAGE TypeFamilies #-}
2{-# LANGUAGE TypeFamilies #-}
4-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
3-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
@@ -35,14 +34,7 @@ instance Ord k => Patch (PatchMap k v) where
35instance Ord k => Semigroup (PatchMap k v) where
34instance Ord k => Semigroup (PatchMap k v) where
36 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
35 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
37 -- PatchMap is idempotent, so stimes n is id for every n
36 -- PatchMap is idempotent, so stimes n is id for every n
38−#if MIN_VERSION_semigroups(0,17,0)
39 stimes = stimesIdempotentMonoid
37 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
46
38
47-- | The empty 'PatchMap' contains no insertions or deletions
39-- | The empty 'PatchMap' contains no insertions or deletions
48instance Ord k => Monoid (PatchMap k v) where
40instance Ord k => Monoid (PatchMap k v) where
src/Reflex/PerformEvent/Base.hs+1−1
@@ -141,7 +141,7 @@ hostPerformEventT a = do
141 case mToPerform of
141 case mToPerform of
142 Nothing -> return [result']
142 Nothing -> return [result']
143 Just toPerform -> do
143 Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
144+ responses <- runHostFrame $ traverseRequesterData (Identity <$>) toPerform
145 mrt <- readRef responseTrigger
145 mrt <- readRef responseTrigger
146 let followupEventTriggers = case mrt of
146 let followupEventTriggers = case mrt of
147 Just rt -> [rt :=> Identity responses]
147 Just rt -> [rt :=> Identity responses]
src/Reflex/PerformEvent/Class.hs−1
@@ -5,7 +5,6 @@
5{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FunctionalDependencies #-}
7{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE RankNTypes #-}
8{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
src/Reflex/PostBuild/Class.hs−1
@@ -4,7 +4,6 @@
4{-# LANGUAGE DataKinds #-}
4{-# LANGUAGE DataKinds #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
6{-# LANGUAGE FunctionalDependencies #-}
6{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeOperators #-}
9{-# LANGUAGE TypeOperators #-}
src/Reflex/Profiled.hs+24−16
@@ -8,6 +8,9 @@
8{-# LANGUAGE StandaloneDeriving #-}
8{-# LANGUAGE StandaloneDeriving #-}
9{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE UndecidableInstances #-}
10{-# LANGUAGE UndecidableInstances #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE TypeApplications #-}
13+{-# LANGUAGE RankNTypes #-}
11-- |
14-- |
12-- Module:
15-- Module:
13-- Reflex.Profiled
16-- Reflex.Profiled
@@ -16,7 +19,6 @@
16-- profiling/cost-center information.
19-- profiling/cost-center information.
17module Reflex.Profiled where
20module Reflex.Profiled where
18
21
19−import Control.Lens hiding (children)
20import Control.Monad
22import Control.Monad
21import Control.Monad.Exception
23import Control.Monad.Exception
22import Control.Monad.Fix
24import Control.Monad.Fix
@@ -33,6 +35,7 @@ import Data.Map (Map)
33import qualified Data.Map.Strict as Map
35import qualified Data.Map.Strict as Map
34import Data.Monoid ((<>))
36import Data.Monoid ((<>))
35import Data.Ord
37import Data.Ord
38+import Data.Profunctor.Unsafe ((#.))
36import qualified Data.Semigroup as S
39import qualified Data.Semigroup as S
37import Data.Type.Coercion
40import Data.Type.Coercion
38import Foreign.Ptr
41import Foreign.Ptr
@@ -77,9 +80,9 @@ getCostCentreStack = go []
77 go (cc : l) parent
80 go (cc : l) parent
78
81
79toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
82toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
80−toCostCentreTree ccs n = do
83+toCostCentreTree ccs n =
81− ccList <- getCostCentreStack ccs
82− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
84+ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty)
85+ <$> getCostCentreStack ccs
83
86
84getCostCentreTree :: IO CostCentreTree
87getCostCentreTree :: IO CostCentreTree
85getCostCentreTree = do
88getCostCentreTree = do
@@ -133,27 +136,32 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
133 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
136 push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
134 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
137 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
135 pull = Behavior_Profiled . pull . coerce
138 pull = Behavior_Profiled . pull . coerce
136− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
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))
137− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
142+ -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
138− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
143+ mergeG nt = Event_Profiled #. mergeG (coerce nt)
139 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
144 switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
140 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
145 coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
141 current (Dynamic_Profiled d) = coerce $ current d
146 current (Dynamic_Profiled d) = coerce $ current d
142 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
147 updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
143 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
148 unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
144 unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
149 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)))
150+ mergeIncrementalG nt = (Event_Profiled . coerce) #. mergeIncrementalG nt
146− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
151+ mergeIncrementalWithMoveG nt = (Event_Profiled . coerce) #. mergeIncrementalWithMoveG nt
147 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
152 currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
148 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
153 updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
149 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
154 incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
150− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
155+ behaviorCoercion c =
151− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
156+ Coercion `trans` behaviorCoercion @t c `trans` Coercion
152− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
157+ eventCoercion c =
153− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
158+ Coercion `trans` eventCoercion @t c `trans` Coercion
159+ dynamicCoercion c =
154− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
160+ Coercion `trans` dynamicCoercion @t c `trans` Coercion
155− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
161+ incrementalCoercion c d =
162+ Coercion `trans` incrementalCoercion @t c d `trans` Coercion
156− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
163+ mergeIntIncremental = Event_Profiled . mergeIntIncremental .
164+ coerceWith (Coercion `trans` incrementalCoercion Coercion Coercion `trans` Coercion)
157 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
165 fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
158
166
159deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
167deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
src/Reflex/Pure.hs+21−17
@@ -5,6 +5,9 @@
5{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE ScopedTypeVariables #-}
6{-# LANGUAGE ScopedTypeVariables #-}
7{-# LANGUAGE TypeFamilies #-}
7{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE PolyKinds #-}
10+
8#ifdef USE_REFLEX_OPTIMIZER
11#ifdef USE_REFLEX_OPTIMIZER
9{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10#endif
13#endif
@@ -28,14 +31,9 @@ module Reflex.Pure
28 , Incremental (..)
31 , Incremental (..)
29 ) where
32 ) where
30
33
31−#if !MIN_VERSION_base(4,8,0)
32−import Control.Applicative
33−#endif
34−
35import Control.Monad
34import Control.Monad
36import Data.Dependent.Map (DMap, GCompare)
35import Data.Dependent.Map (DMap, GCompare)
37import qualified Data.Dependent.Map as DMap
36import qualified Data.Dependent.Map as DMap
38−import Data.Functor.Identity
39import Data.IntMap (IntMap)
37import Data.IntMap (IntMap)
40import qualified Data.IntMap as IntMap
38import qualified Data.IntMap as IntMap
41import Data.Maybe
39import Data.Maybe
@@ -43,10 +41,11 @@ import Data.MemoTrie
43import Data.Monoid
41import Data.Monoid
44import Data.Type.Coercion
42import Data.Type.Coercion
45import Reflex.Class
43import Reflex.Class
44+import Data.Kind (Type)
46
45
47-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
46-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
48-- with the type @/t/@.
47-- with the type @/t/@.
49−data Pure t
48+data Pure (t :: Type)
50
49
51-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
50-- | The 'Enum' instance of @/t/@ must be dense: for all @/x :: t/@, there must not exist
52-- any @/y :: t/@ such that @/'pred' x < y < x/@. The 'HasTrie' instance will be used
51-- 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
79 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
78 -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
80 -- warning because the GCompare instance is not used; however, removing the
79 -- warning because the GCompare instance is not used; however, removing the
81 -- GCompare instance produces a different warning, due to that constraint
80 -- 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.
83
82
84− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
83+ --mergeG :: GCompare k => (forall a. q a -> Event (Pure t) (v a))
84+ -- -> DMap k q -> Event (Pure t) (DMap k v)
85− merge events = Event $ memo $ \t ->
85+ mergeG nt events = Event $ memo $ \t ->
86− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
86+ let currentOccurrences = DMap.mapMaybeWithKey (\_ q -> case nt q of Event a -> a t) events
87 in if DMap.null currentOccurrences
87 in if DMap.null currentOccurrences
88 then Nothing
88 then Nothing
89 else Just currentOccurrences
89 else Just currentOccurrences
90
90
91+-- The instance signature doeesn't compile, leave commented for documentation
91− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
92+-- fanG :: GCompare k => Event (Pure t) (DMap k v) -> EventSelectorG (Pure t) k v
92− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
93+ fanG e = EventSelectorG $ \k -> Event $ \t -> unEvent e t >>= DMap.lookup k
93
94
94 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
95 switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
95 switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
96 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
112 --a) -> Incremental (Pure t) p a
113 --a) -> Incremental (Pure t) p a
113 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114 unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
114
115
115− mergeIncremental = mergeIncrementalImpl
116+ mergeIncrementalG = mergeIncrementalImpl
116− mergeIncrementalWithMove = mergeIncrementalImpl
117+ mergeIncrementalWithMoveG = mergeIncrementalImpl
117
118
118 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119 currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
119
120
@@ -128,14 +129,17 @@ instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
128 behaviorCoercion Coercion = Coercion
129 behaviorCoercion Coercion = Coercion
129 eventCoercion Coercion = Coercion
130 eventCoercion Coercion = Coercion
130 dynamicCoercion Coercion = Coercion
131 dynamicCoercion Coercion = Coercion
132+ incrementalCoercion Coercion Coercion = Coercion
131
133
132 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
134 fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
133
135
134 mergeIntIncremental = mergeIntIncrementalImpl
136 mergeIntIncremental = mergeIntIncrementalImpl
135
137
136−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
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)
137−mergeIncrementalImpl i = Event $ \t ->
141+mergeIncrementalImpl nt i = Event $ \t ->
138− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
142+ let results = DMap.mapMaybeWithKey (\_ q -> case nt q of Event e -> e t) $ fst $ unIncremental i t
139 in if DMap.null results
143 in if DMap.null results
140 then Nothing
144 then Nothing
141 else Just results
145 else Just results
src/Reflex/Query/Base.hs−1
@@ -1,7 +1,6 @@
1{-# LANGUAGE FlexibleInstances #-}
1{-# LANGUAGE FlexibleInstances #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3{-# LANGUAGE InstanceSigs #-}
3{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
5{-# LANGUAGE LambdaCase #-}
4{-# LANGUAGE LambdaCase #-}
6{-# LANGUAGE MultiParamTypeClasses #-}
5{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE RankNTypes #-}
6{-# LANGUAGE RankNTypes #-}
src/Reflex/Query/Class.hs−1
@@ -3,7 +3,6 @@
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7{-# LANGUAGE TypeFamilies #-}
6{-# LANGUAGE TypeFamilies #-}
8{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
9-- |
8-- |
src/Reflex/Requester/Class.hs−1
@@ -6,7 +6,6 @@
6{-# LANGUAGE CPP #-}
6{-# LANGUAGE CPP #-}
7{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE FlexibleInstances #-}
8{-# LANGUAGE FunctionalDependencies #-}
8{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10{-# LANGUAGE RecursiveDo #-}
9{-# LANGUAGE RecursiveDo #-}
11{-# LANGUAGE TypeFamilies #-}
10{-# LANGUAGE TypeFamilies #-}
12{-# LANGUAGE UndecidableInstances #-}
11{-# LANGUAGE UndecidableInstances #-}
src/Reflex/Spider/Internal.hs+246−179
@@ -9,7 +9,6 @@
9{-# LANGUAGE GADTs #-}
9{-# LANGUAGE GADTs #-}
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11{-# LANGUAGE LambdaCase #-}
11{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
13{-# LANGUAGE MultiWayIf #-}
12{-# LANGUAGE MultiWayIf #-}
14{-# LANGUAGE RankNTypes #-}
13{-# LANGUAGE RankNTypes #-}
15{-# LANGUAGE RoleAnnotations #-}
14{-# LANGUAGE RoleAnnotations #-}
@@ -17,9 +16,15 @@
17{-# LANGUAGE TypeFamilies #-}
16{-# LANGUAGE TypeFamilies #-}
18{-# LANGUAGE TypeOperators #-}
17{-# LANGUAGE TypeOperators #-}
19{-# LANGUAGE UndecidableInstances #-}
18{-# LANGUAGE UndecidableInstances #-}
19+{-# LANGUAGE PolyKinds #-}
20+{-# LANGUAGE DeriveFunctor #-}
21+{-# LANGUAGE StandaloneDeriving #-}
22+{-# LANGUAGE InstanceSigs #-}
23+
20#ifdef USE_REFLEX_OPTIMIZER
24#ifdef USE_REFLEX_OPTIMIZER
21{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
25{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
22#endif
26#endif
27+{-# OPTIONS_GHC -Wunused-binds #-}
23-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
28-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
24-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
29-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
25module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
30module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
@@ -33,7 +38,10 @@ import Control.Monad hiding (forM, forM_, mapM, mapM_)
33import Control.Monad.Exception
38import Control.Monad.Exception
34import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
39import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
35import Control.Monad.Primitive
40import 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
37import Control.Monad.Ref
45import Control.Monad.Ref
38import Data.Align
46import Data.Align
39import Data.Coerce
47import Data.Coerce
@@ -49,6 +57,7 @@ import Data.GADT.Compare
49import Data.IntMap.Strict (IntMap)
57import Data.IntMap.Strict (IntMap)
50import qualified Data.IntMap.Strict as IntMap
58import qualified Data.IntMap.Strict as IntMap
51import Data.IORef
59import Data.IORef
60+import Data.Kind (Type)
52import Data.Maybe hiding (mapMaybe)
61import Data.Maybe hiding (mapMaybe)
53import Data.Monoid ((<>))
62import Data.Monoid ((<>))
54import Data.Proxy
63import Data.Proxy
@@ -63,6 +72,15 @@ import System.IO.Unsafe
63import System.Mem.Weak
72import System.Mem.Weak
64import Unsafe.Coerce
73import Unsafe.Coerce
65
74
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+
66#ifdef DEBUG_CYCLES
84#ifdef DEBUG_CYCLES
67import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
85import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
68import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
86import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
@@ -76,6 +94,7 @@ import qualified Data.FastWeakBag as FastWeakBag
76import Data.Reflection
94import Data.Reflection
77import Data.Some (Some(Some))
95import Data.Some (Some(Some))
78import Data.Type.Coercion
96import Data.Type.Coercion
97+import Data.Profunctor.Unsafe ((#.), (.#))
79import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
98import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
80import qualified Data.WeakBag as WeakBag
99import qualified Data.WeakBag as WeakBag
81import qualified Reflex.Class
100import qualified Reflex.Class
@@ -192,12 +211,6 @@ nextNodeIdRef = unsafePerformIO $ newIORef 1
192
211
193newNodeId :: IO Int
212newNodeId :: IO Int
194newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
213newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
195−
196−{-# NOINLINE unsafeNodeId #-}
197−unsafeNodeId :: a -> Int
198−unsafeNodeId a = unsafePerformIO $ do
199− touch a
200− newNodeId
201#endif
214#endif
202
215
203--------------------------------------------------------------------------------
216--------------------------------------------------------------------------------
@@ -301,9 +314,10 @@ cacheEvent e =
301#else
314#else
302 Event $
315 Event $
303#endif
316#endif
317+ unsafePerformIO $ do
304− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
318+ mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
305− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
319+ <- newIORef emptyFastWeak
306− in \sub -> {-# SCC "cacheEvent" #-} do
320+ pure $ \sub -> {-# SCC "cacheEvent" #-} do
307#ifdef DEBUG_TRACE_EVENTS
321#ifdef DEBUG_TRACE_EVENTS
308 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
322 unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
309#endif
323#endif
@@ -318,9 +332,9 @@ cacheEvent e =
318 scheduleClear occRef
332 scheduleClear occRef
319 propagateFast a subscribers
333 propagateFast a subscribers
320 , subscriberInvalidateHeight = \old -> do
334 , subscriberInvalidateHeight = \old -> do
321− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
335+ FastWeakBag.traverse_ subscribers $ invalidateSubscriberHeight old
322 , subscriberRecalculateHeight = \new -> do
336 , subscriberRecalculateHeight = \new -> do
323− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
337+ FastWeakBag.traverse_ subscribers $ recalculateSubscriberHeight new
324 }
338 }
325 when (isJust occ) $ do
339 when (isJust occ) $ do
326 liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
340 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
369eventNever :: Event x a
383eventNever :: Event x a
370eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
384eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
371
385
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)
373eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
387eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
374
388
375eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
389eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
@@ -421,14 +435,14 @@ newSubscriberHold h = return $ Subscriber
421 , subscriberRecalculateHeight = \_ -> return ()
435 , subscriberRecalculateHeight = \_ -> return ()
422 }
436 }
423
437
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))
425newSubscriberFan subscribed = return $ Subscriber
439newSubscriberFan subscribed = return $ Subscriber
426 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
440 { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
427 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
441 subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
428 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
442 tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
429 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
443 liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
430 scheduleClear $ fanSubscribedOccurrence subscribed
444 scheduleClear $ fanSubscribedOccurrence subscribed
431− let f _ (Pair (Identity v) subsubs) = do
445+ let f _ (Pair v subsubs) = do
432 propagate v $ _fanSubscribedChildren_list subsubs
446 propagate v $ _fanSubscribedChildren_list subsubs
433 return $ Constant ()
447 return $ Constant ()
434 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
448 _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
@@ -436,12 +450,12 @@ newSubscriberFan subscribed = return $ Subscriber
436 , subscriberInvalidateHeight = \old -> do
450 , subscriberInvalidateHeight = \old -> do
437 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
451 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
438 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
452 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
440 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
454 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
441 , subscriberRecalculateHeight = \new -> do
455 , subscriberRecalculateHeight = \new -> do
442 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
456 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
443 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
457 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
445 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
459 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
446 }
460 }
447
461
@@ -457,7 +471,7 @@ newSubscriberSwitch subscribed = return $ Subscriber
457 oldHeight <- readIORef $ switchSubscribedHeight subscribed
471 oldHeight <- readIORef $ switchSubscribedHeight subscribed
458 when (oldHeight /= invalidHeight) $ do
472 when (oldHeight /= invalidHeight) $ do
459 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
473 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
460− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
474+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
461 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
475 when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
462 , subscriberRecalculateHeight = \new -> do
476 , subscriberRecalculateHeight = \new -> do
463 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
477 when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
@@ -480,8 +494,8 @@ newSubscriberCoincidenceOuter subscribed = return $ Subscriber
480 Nothing -> do
494 Nothing -> do
481 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
495 when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
482 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
496 writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
483− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
497+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
498+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
485 Just o -> do -- Since it's already firing, no need to adjust height
499 Just o -> do -- Since it's already firing, no need to adjust height
486 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
500 liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
487 scheduleClear $ coincidenceSubscribedOccurrence subscribed
501 scheduleClear $ coincidenceSubscribedOccurrence subscribed
@@ -528,14 +542,14 @@ propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
528propagate a subscribers = withIncreasedDepth $ do
542propagate a subscribers = withIncreasedDepth $ do
529 -- 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
543 -- 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
530 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
544 --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
532
546
533-- | Propagate everything at the current height
547-- | Propagate everything at the current height
534propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
548propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
535propagateFast a subscribers = withIncreasedDepth $ do
549propagateFast a subscribers = withIncreasedDepth $ do
536 -- 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
550 -- 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
537 --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
551 --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
539
553
540--------------------------------------------------------------------------------
554--------------------------------------------------------------------------------
541-- EventSubscribed
555-- EventSubscribed
@@ -544,6 +558,11 @@ propagateFast a subscribers = withIncreasedDepth $ do
544toAny :: a -> Any
558toAny :: a -> Any
545toAny = unsafeCoerce
559toAny = unsafeCoerce
546
560
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.
547data EventSubscribed x = EventSubscribed
566data EventSubscribed x = EventSubscribed
548 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
567 { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
549 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
568 , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
@@ -576,7 +595,7 @@ eventSubscribedNever = EventSubscribed
576#endif
595#endif
577 }
596 }
578
597
579−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
598+eventSubscribedFan :: FanSubscribed x k v -> EventSubscribed x
580eventSubscribedFan !subscribed = EventSubscribed
599eventSubscribedFan !subscribed = EventSubscribed
581 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
600 { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
582 , eventSubscribedRetained = toAny subscribed
601 , eventSubscribedRetained = toAny subscribed
@@ -667,7 +686,7 @@ behaviorPull !p = Behavior $ do
667 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
686 wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
668 parentsRef <- liftIO $ newIORef []
687 parentsRef <- liftIO $ newIORef []
669 holdInits <- askBehaviorHoldInits
688 holdInits <- askBehaviorHoldInits
670− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
689+ a <- liftIO $ runReaderIO (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
671 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
690 invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
672 parents <- liftIO $ readIORef parentsRef
691 parents <- liftIO $ readIORef parentsRef
673 let subscribed = PullSubscribed
692 let subscribed = PullSubscribed
@@ -702,42 +721,46 @@ readBehaviorUntracked b = do
702-- Dynamic
721-- Dynamic
703--------------------------------------------------------------------------------
722--------------------------------------------------------------------------------
704
723
724+type DynamicS x p = Dynamic x (PatchTarget p) p
725+
705−data Dynamic x p = Dynamic
726+data Dynamic x target p = Dynamic
706− { dynamicCurrent :: !(Behavior x (PatchTarget p))
727+ { dynamicCurrent :: !(Behavior x target)
707 , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
728 , dynamicUpdated :: Event x p -- This must be lazy; see the comment on holdEvent --TODO: Would this let us eliminate `Dyn`?
708 }
729 }
709
730
731+deriving instance (HasSpiderTimeline x) => Functor (Dynamic x target)
732+
733+
734+
735+
710−dynamicHold :: Hold x p -> Dynamic x p
736+dynamicHold :: Hold x p -> DynamicS x p
711dynamicHold !h = Dynamic
737dynamicHold !h = Dynamic
712 { dynamicCurrent = behaviorHold h
738 { dynamicCurrent = behaviorHold h
713 , dynamicUpdated = eventHold h
739 , dynamicUpdated = eventHold h
714 }
740 }
715
741
716−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
742+dynamicHoldIdentity :: Hold x (Identity a) -> DynamicS x (Identity a)
717dynamicHoldIdentity = dynamicHold
743dynamicHoldIdentity = dynamicHold
718
744
719−dynamicConst :: PatchTarget p -> Dynamic x p
745+dynamicConst :: PatchTarget p -> DynamicS x p
720dynamicConst !a = Dynamic
746dynamicConst !a = Dynamic
721 { dynamicCurrent = behaviorConst a
747 { dynamicCurrent = behaviorConst a
722 , dynamicUpdated = eventNever
748 , dynamicUpdated = eventNever
723 }
749 }
724
750
725−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
751+dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> DynamicS x p
726dynamicDyn !d = Dynamic
752dynamicDyn !d = Dynamic
727 { dynamicCurrent = behaviorDyn d
753 { dynamicCurrent = behaviorDyn d
728 , dynamicUpdated = eventDyn d
754 , dynamicUpdated = eventDyn d
729 }
755 }
730
756
731−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
757+dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> DynamicS x (Identity a)
732dynamicDynIdentity = dynamicDyn
758dynamicDynIdentity = dynamicDyn
733
759
734--------------------------------------------------------------------------------
760--------------------------------------------------------------------------------
735-- Combinators
761-- Combinators
736--------------------------------------------------------------------------------
762--------------------------------------------------------------------------------
737
763
738−--TODO: Figure out why certain things are not 'representational', then make them
739−--representational so we can use coerce
740−
741--type role Hold representational
764--type role Hold representational
742data Hold x p
765data Hold x p
743 = Hold { holdValue :: !(IORef (PatchTarget p))
766 = Hold { holdValue :: !(IORef (PatchTarget p))
@@ -758,20 +781,25 @@ globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
758
781
759-- | Stores all global data relevant to a particular Spider timeline; only one
782-- | Stores all global data relevant to a particular Spider timeline; only one
760-- value should exist for each type @x@
783-- value should exist for each type @x@
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+
761−data SpiderTimelineEnv x = SpiderTimelineEnv
789+data SpiderTimelineEnv' x = SpiderTimelineEnv
762 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
790 { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
763 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
791 , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
764#ifdef DEBUG
792#ifdef DEBUG
765 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
793 , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
766#endif
794#endif
767 }
795 }
768−type role SpiderTimelineEnv nominal
796+type role SpiderTimelineEnv' phantom
769
797
770instance Eq (SpiderTimelineEnv x) where
798instance Eq (SpiderTimelineEnv x) where
771 _ == _ = True -- Since only one exists of each type
799 _ == _ = True -- Since only one exists of each type
772
800
773instance GEq SpiderTimelineEnv where
801instance 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)
775 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
803 then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
776 else Nothing
804 else Nothing
777
805
@@ -794,7 +822,7 @@ runEventM :: EventM x a -> IO a
794runEventM = unEventM
822runEventM = unEventM
795
823
796asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
824asksEventEnv :: 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))
798
826
799class MonadIO m => Defer a m where
827class MonadIO m => Defer a m where
800 getDeferralQueue :: m (IORef [a])
828 getDeferralQueue :: m (IORef [a])
@@ -932,9 +960,9 @@ getHoldEventSubscription h = do
932
960
933type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
961type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
934
962
935−--type role BehaviorM representational
936-- BehaviorM can sample behaviors
963-- 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))
938
966
939instance Monad (BehaviorM x) where
967instance Monad (BehaviorM x) where
940 {-# INLINE (>>=) #-}
968 {-# INLINE (>>=) #-}
@@ -985,7 +1013,7 @@ data RootSubscribed x a = forall k. GCompare k => RootSubscribed
985#endif
1013#endif
986 }
1014 }
987
1015
988−data Root x (k :: * -> *)
1016+data Root x k
989 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
1017 = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
990 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
1018 , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
991 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
1019 , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
@@ -1055,25 +1083,25 @@ heightBagVerify b@(HeightBag s c) = if
1055heightBagVerify = id
1083heightBagVerify = id
1056#endif
1084#endif
1057
1085
1058−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1086+data FanSubscribedChildren x k v a = FanSubscribedChildren
1059− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1087+ { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x (v a)))
1060− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1088+ , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k v)
1061− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1089+ , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k v)))
1062 }
1090 }
1063
1091
1064−data FanSubscribed (x :: *) k
1092+data FanSubscribed x k v
1065− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1093+ = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1066− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1094+ , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k v)))
1067− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1095+ , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k v))) -- This DMap should never be empty
1068 , fanSubscribedParent :: !(EventSubscription x)
1096 , fanSubscribedParent :: !(EventSubscription x)
1069#ifdef DEBUG_NODEIDS
1097#ifdef DEBUG_NODEIDS
1070 , fanSubscribedNodeId :: Int
1098 , fanSubscribedNodeId :: Int
1071#endif
1099#endif
1072 }
1100 }
1073
1101
1074−data Fan x k
1102+data Fan x k v
1075− = Fan { fanParent :: !(Event x (DMap k Identity))
1103+ = Fan { fanParent :: !(Event x (DMap k v))
1076− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1104+ , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k v)))
1077 }
1105 }
1078
1106
1079data SwitchSubscribed x a
1107data SwitchSubscribed x a
@@ -1135,19 +1163,27 @@ instance HasSpiderTimeline x => Align (Event x) where
1135#if MIN_VERSION_these(0, 8, 0)
1163#if MIN_VERSION_these(0, 8, 0)
1136instance HasSpiderTimeline x => Semialign (Event x) where
1164instance HasSpiderTimeline x => Semialign (Event x) where
1137#endif
1165#endif
1166+ align ea eb = mapMaybe dmapToThese $ mergeG coerce $ dynamicConst $
1138− align ea eb = mapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
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
1139
1175
1140data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1176data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1141 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1177 | BuildDyn !(EventM x (PatchTarget p), Event x p)
1142 | HoldDyn !(Hold x p)
1178 | HoldDyn !(Hold x p)
1143
1179
1144−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1180+newtype Dyn (x :: Type) p = Dyn { unDyn :: IORef (DynType x p) }
1145
1181
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)
1147newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1183newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1148
1184
1149--TODO: Avoid the duplication between this and R.zipDynWith
1185--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)
1151zipDynWith f da db =
1187zipDynWith f da db =
1152 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1188 let eab = align (dynamicUpdated da) (dynamicUpdated db)
1153 ec = flip push eab $ \o -> do
1189 ec = flip push eab $ \o -> do
@@ -1170,18 +1206,12 @@ buildDynamic readV0 v' = do
1170 return d
1206 return d
1171
1207
1172unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1208unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1173−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1209+unsafeBuildDynamic readV0 v' =
1174− where x = (readV0, v')
1210+ Dyn $ unsafePerformIO $ newIORef $ UnsafeDyn (readV0, v')
1175
1211
1176-- ResultM can read behaviors and events
1212-- ResultM can read behaviors and events
1177type ResultM = EventM
1213type ResultM = EventM
1178
1214
1179−{-# NOINLINE unsafeNewIORef #-}
1180−unsafeNewIORef :: a -> b -> IORef b
1181−unsafeNewIORef a b = unsafePerformIO $ do
1182− touch a
1183− newIORef b
1184−
1185instance HasSpiderTimeline x => Functor (Event x) where
1215instance HasSpiderTimeline x => Functor (Event x) where
1186 fmap f = push $ return . Just . f
1216 fmap f = push $ return . Just . f
1187
1217
@@ -1194,33 +1224,42 @@ push f e = cacheEvent (pushCheap f e)
1194
1224
1195{-# INLINABLE pull #-}
1225{-# INLINABLE pull #-}
1196pull :: BehaviorM x a -> Behavior x a
1226pull :: BehaviorM x a -> Behavior x a
1197−pull a = behaviorPull $ Pull
1227+pull a = unsafePerformIO $ do
1198− { pullCompute = a
1228+ ref <- newIORef Nothing
1199− , pullValue = unsafeNewIORef a Nothing
1200#ifdef DEBUG_NODEIDS
1229#ifdef DEBUG_NODEIDS
1201− , pullNodeId = unsafeNodeId a
1230+ nid <- newNodeId
1202#endif
1231#endif
1232+ pure $ behaviorPull $ Pull
1233+ { pullCompute = a
1234+ , pullValue = ref
1235+#ifdef DEBUG_NODEIDS
1236+ , pullNodeId = nid
1237+#endif
1203− }
1238+ }
1204
1239
1205{-# INLINABLE switch #-}
1240{-# INLINABLE switch #-}
1206switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1241switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1242+switch a = unsafePerformIO $ do
1243+ ref <- newIORef Nothing
1207−switch a = eventSwitch $ Switch
1244+ pure $ eventSwitch $ Switch
1208− { switchParent = a
1245+ { switchParent = a
1209− , switchSubscribed = unsafeNewIORef a Nothing
1246+ , switchSubscribed = ref
1210− }
1247+ }
1211
1248
1212coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1249coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1250+coincidence a = unsafePerformIO $ do
1251+ ref <- newIORef Nothing
1213−coincidence a = eventCoincidence $ Coincidence
1252+ pure $ eventCoincidence $ Coincidence
1214− { coincidenceParent = a
1253+ { coincidenceParent = a
1215− , coincidenceSubscribed = unsafeNewIORef a Nothing
1254+ , coincidenceSubscribed = ref
1216− }
1255+ }
1217
1256
1218-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1257-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1219run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1258run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1220run roots after = do
1259run roots after = do
1221 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1260 tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1222 let t = spiderTimeline :: SpiderTimelineEnv x
1261 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
1224 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1263 rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1225 occBefore <- liftIO $ do
1264 occBefore <- liftIO $ do
1226 occBefore <- readIORef occRef
1265 occBefore <- readIORef occRef
@@ -1284,9 +1323,9 @@ type WeakList a = [Weak a]
1284withIncreasedDepth :: CanTrace x m => m a -> m a
1323withIncreasedDepth :: CanTrace x m => m a -> m a
1285withIncreasedDepth a = do
1324withIncreasedDepth a = do
1286 spiderTimeline <- askSpiderTimelineEnv
1325 spiderTimeline <- askSpiderTimelineEnv
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1326+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) succ
1288 result <- a
1327 result <- a
1289− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1328+ liftIO $ modifyIORef' (_spiderTimeline_depth (unSTE spiderTimeline)) pred
1290 return result
1329 return result
1291#else
1330#else
1292withIncreasedDepth :: m a -> m a
1331withIncreasedDepth :: m a -> m a
@@ -1313,7 +1352,7 @@ traceMWhen _ b getMessage = when b $ do
1313 message <- getMessage
1352 message <- getMessage
1314#ifdef DEBUG
1353#ifdef DEBUG
1315 spiderTimeline <- askSpiderTimelineEnv
1354 spiderTimeline <- askSpiderTimelineEnv
1316− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1355+ d <- liftIO $ readIORef $ _spiderTimeline_depth $ unSTE spiderTimeline
1317#else
1356#else
1318 let d = 0
1357 let d = 0
1319#endif
1358#endif
@@ -1354,25 +1393,25 @@ propagateSubscriberHold h a = do
1354data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1393data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1355
1394
1356runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1395runBehaviorM :: 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)
1358
1397
1359askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1398askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1360askInvalidator = do
1399askInvalidator = do
1361− (!m, _) <- BehaviorM ask
1400+ (!m, _) <- ask
1362 case m of
1401 case m of
1363 Nothing -> return Nothing
1402 Nothing -> return Nothing
1364 Just (!wi, _) -> return $ Just wi
1403 Just (!wi, _) -> return $ Just wi
1365
1404
1366askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1405askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1367askParentsRef = do
1406askParentsRef = do
1368− (!m, _) <- BehaviorM ask
1407+ (!m, _) <- ask
1369 case m of
1408 case m of
1370 Nothing -> return Nothing
1409 Nothing -> return Nothing
1371 Just (_, !p) -> return $ Just p
1410 Just (_, !p) -> return $ Just p
1372
1411
1373askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1412askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1374askBehaviorHoldInits = do
1413askBehaviorHoldInits = do
1375− (_, !his) <- BehaviorM ask
1414+ (_, !his) <- ask
1376 return his
1415 return his
1377
1416
1378{-# INLINE getDynHold #-}
1417{-# INLINE getDynHold #-}
@@ -1417,6 +1456,9 @@ getRootSubscribed k r sub = do
1417 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1456 when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1418 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1457 uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1419 writeIORef uninitRef $! uninit
1458 writeIORef uninitRef $! uninit
1459+#ifdef DEBUG_NODEIDS
1460+ nid <- newNodeId
1461+#endif
1420 let !subscribed = RootSubscribed
1462 let !subscribed = RootSubscribed
1421 { rootSubscribedKey = k
1463 { rootSubscribedKey = k
1422 , rootSubscribedCachedSubscribed = cached
1464 , rootSubscribedCachedSubscribed = cached
@@ -1425,7 +1467,7 @@ getRootSubscribed k r sub = do
1425 , rootSubscribedUninit = uninit
1467 , rootSubscribedUninit = uninit
1426 , rootSubscribedWeakSelf = weakSelf
1468 , rootSubscribedWeakSelf = weakSelf
1427#ifdef DEBUG_NODEIDS
1469#ifdef DEBUG_NODEIDS
1428− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1470+ , rootSubscribedNodeId = nid
1429#endif
1471#endif
1430 }
1472 }
1431 -- If we die at the same moment that all our children die, they will
1473 -- If we die at the same moment that all our children die, they will
@@ -1474,16 +1516,10 @@ newFanInt = do
1474 , _fanInt_occRef = occRef
1516 , _fanInt_occRef = occRef
1475 }
1517 }
1476
1518
1477−{-# NOINLINE unsafeNewFanInt #-}
1478−unsafeNewFanInt :: b -> FanInt x a
1479−unsafeNewFanInt b = unsafePerformIO $ do
1480− touch b
1481− newFanInt
1482−
1483fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1519fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1484−fanInt p =
1520+fanInt p = unsafePerformIO $ do
1485− let self = unsafeNewFanInt p
1521+ self <- newFanInt
1486− in EventSelectorInt $ \k -> Event $ \sub -> do
1522+ pure $ EventSelectorInt $ \k -> Event $ \sub -> do
1487 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1523 isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1488 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1524 when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1489 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1525 (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
@@ -1491,15 +1527,15 @@ fanInt p =
1491 liftIO $ writeIORef (_fanInt_occRef self) m
1527 liftIO $ writeIORef (_fanInt_occRef self) m
1492 scheduleIntClear $ _fanInt_occRef self
1528 scheduleIntClear $ _fanInt_occRef self
1493 FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1529 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
1495 subscriberPropagate s v
1531 subscriberPropagate s v
1496 , subscriberInvalidateHeight = \old -> do
1532 , subscriberInvalidateHeight = \old -> do
1497 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1533 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1498− FastWeakBag.traverse b $ \s -> do
1534+ FastWeakBag.traverse_ b $ \s -> do
1499 subscriberInvalidateHeight s old
1535 subscriberInvalidateHeight s old
1500 , subscriberRecalculateHeight = \new -> do
1536 , subscriberRecalculateHeight = \new -> do
1501 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1537 FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1502− FastWeakBag.traverse b $ \s -> do
1538+ FastWeakBag.traverse_ b $ \s -> do
1503 subscriberRecalculateHeight s new
1539 subscriberRecalculateHeight s new
1504 }
1540 }
1505 liftIO $ do
1541 liftIO $ do
@@ -1519,7 +1555,7 @@ fanInt p =
1519 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1555 return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1520
1556
1521{-# INLINABLE getFanSubscribed #-}
1557{-# 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))
1523getFanSubscribed k f sub = do
1559getFanSubscribed k f sub = do
1524 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1560 mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1525 case mSubscribed of
1561 case mSubscribed of
@@ -1537,13 +1573,16 @@ getFanSubscribed k f sub = do
1537 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1573 subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1538 occRef <- liftIO $ newIORef parentOcc
1574 occRef <- liftIO $ newIORef parentOcc
1539 when (isJust parentOcc) $ scheduleClear occRef
1575 when (isJust parentOcc) $ scheduleClear occRef
1576+#ifdef DEBUG_NODEIDS
1577+ nid <- liftIO newNodeId
1578+#endif
1540 let subscribed = FanSubscribed
1579 let subscribed = FanSubscribed
1541 { fanSubscribedCachedSubscribed = fanSubscribed f
1580 { fanSubscribedCachedSubscribed = fanSubscribed f
1542 , fanSubscribedOccurrence = occRef
1581 , fanSubscribedOccurrence = occRef
1543 , fanSubscribedParent = subscription
1582 , fanSubscribedParent = subscription
1544 , fanSubscribedSubscribers = subscribersRef
1583 , fanSubscribedSubscribers = subscribersRef
1545#ifdef DEBUG_NODEIDS
1584#ifdef DEBUG_NODEIDS
1546− , fanSubscribedNodeId = unsafeNodeId f
1585+ , fanSubscribedNodeId = nid
1547#endif
1586#endif
1548 }
1587 }
1549 let !self = (k, subscribed)
1588 let !self = (k, subscribed)
@@ -1553,7 +1592,7 @@ getFanSubscribed k f sub = do
1553 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1592 liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1554 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1593 return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1555
1594
1556−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1595+cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k v) -> IO ()
1557cleanupFanSubscribed (k, subscribed) = do
1596cleanupFanSubscribed (k, subscribed) = do
1558 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1597 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1559 let reducedSubscribers = DMap.delete k subscribers
1598 let reducedSubscribers = DMap.delete k subscribers
@@ -1565,7 +1604,7 @@ cleanupFanSubscribed (k, subscribed) = do
1565 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1604 else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1566
1605
1567{-# INLINE subscribeFanSubscribed #-}
1606{-# 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
1569subscribeFanSubscribed k subscribed sub = do
1608subscribeFanSubscribed k subscribed sub = do
1570 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1609 subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1571 case DMap.lookup k subscribers of
1610 case DMap.lookup k subscribers of
@@ -1603,6 +1642,9 @@ getSwitchSubscribed s sub = do
1603 when (isJust parentOcc) $ scheduleClear occRef
1642 when (isJust parentOcc) $ scheduleClear occRef
1604 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1643 weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1605 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1644 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1645+#ifdef DEBUG_NODEIDS
1646+ nid <- liftIO newNodeId
1647+#endif
1606 let !subscribed = SwitchSubscribed
1648 let !subscribed = SwitchSubscribed
1607 { switchSubscribedCachedSubscribed = switchSubscribed s
1649 { switchSubscribedCachedSubscribed = switchSubscribed s
1608 , switchSubscribedOccurrence = occRef
1650 , switchSubscribedOccurrence = occRef
@@ -1615,7 +1657,7 @@ getSwitchSubscribed s sub = do
1615 , switchSubscribedCurrentParent = subscriptionRef
1657 , switchSubscribedCurrentParent = subscriptionRef
1616 , switchSubscribedWeakSelf = weakSelf
1658 , switchSubscribedWeakSelf = weakSelf
1617#ifdef DEBUG_NODEIDS
1659#ifdef DEBUG_NODEIDS
1618− , switchSubscribedNodeId = unsafeNodeId s
1660+ , switchSubscribedNodeId = nid
1619#endif
1661#endif
1620 }
1662 }
1621 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1663 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
@@ -1660,6 +1702,9 @@ getCoincidenceSubscribed c sub = do
1660 scheduleClear innerSubdRef
1702 scheduleClear innerSubdRef
1661 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1703 weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1662 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1704 (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1705+#ifdef DEBUG_NODEIDS
1706+ nid <- liftIO newNodeId
1707+#endif
1663 let subscribed = CoincidenceSubscribed
1708 let subscribed = CoincidenceSubscribed
1664 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1709 { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1665 , coincidenceSubscribedOccurrence = occRef
1710 , coincidenceSubscribedOccurrence = occRef
@@ -1670,7 +1715,7 @@ getCoincidenceSubscribed c sub = do
1670 , coincidenceSubscribedInnerParent = innerSubdRef
1715 , coincidenceSubscribedInnerParent = innerSubdRef
1671 , coincidenceSubscribedWeakSelf = weakSelf
1716 , coincidenceSubscribedWeakSelf = weakSelf
1672#ifdef DEBUG_NODEIDS
1717#ifdef DEBUG_NODEIDS
1673− , coincidenceSubscribedNodeId = unsafeNodeId c
1718+ , coincidenceSubscribedNodeId = nid
1674#endif
1719#endif
1675 }
1720 }
1676 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1721 liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
@@ -1687,26 +1732,34 @@ cleanupCoincidenceSubscribed subscribed = do
1687subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1732subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1688subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1733subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1689
1734
1690−{-# INLINE merge #-}
1735+{-# INLINE mergeG #-}
1691−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
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)
1692−merge d = cacheEvent (mergeCheap d)
1739+mergeG nt d = cacheEvent (mergeCheap nt d)
1693
1740
1694{-# INLINE mergeWithMove #-}
1741{-# INLINE mergeWithMove #-}
1695−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
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)
1696−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1745+mergeWithMove nt d = cacheEvent (mergeCheapWithMove nt d)
1697
1746
1698{-# INLINE [1] mergeCheap #-}
1747{-# INLINE [1] mergeCheap #-}
1748+mergeCheap
1699−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
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)
1700−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1753+mergeCheap nt = mergeGCheap' getInitialSubscribers updateMe destroy
1701 where
1754 where
1702− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1755+ updateMe :: MergeUpdateFunc k v x (PatchDMap k q) (MergeSubscribedParent x)
1703 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1756 updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1704 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1757 let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1705 (mOldSubd, newPs) <- case me of
1758 (mOldSubd, newPs) <- case me of
1706 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1759 Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1707 Just e -> do
1760 Just e -> do
1708 let s = subscriber $ return k
1761 let s = subscriber $ return k
1709− subscription@(EventSubscription _ subd) <- subscribe e s
1762+ subscription@(EventSubscription _ subd) <- subscribe (nt e) s
1710 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1763 newParentHeight <- liftIO $ getEventSubscribedHeight subd
1711 let newParent = MergeSubscribedParent subscription
1764 let newParent = MergeSubscribedParent subscription
1712 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1765 liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
@@ -1716,28 +1769,33 @@ mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1716 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1769 liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1717 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1770 return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1718 foldM f ([], oldParents) $ DMap.toList p
1771 foldM f ([], oldParents) $ DMap.toList p
1772+
1719− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1773+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParent x)
1720 getInitialSubscribers initialParents subscriber = do
1774 getInitialSubscribers initialParents subscriber = do
1721 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1775 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1722 let s = subscriber $ return k
1776 let s = subscriber $ return k
1723− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1777+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1724 height <- liftIO $ getEventSubscribedHeight parentSubd
1778 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)
1726 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1780 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1727 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1781 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1728 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1782 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1729 )
1783 )
1784+
1730 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1785 destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1731 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1786 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1732
1787
1733{-# INLINE [1] mergeCheapWithMove #-}
1788{-# INLINE [1] mergeCheapWithMove #-}
1734−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
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)
1735−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1793+mergeCheapWithMove nt = mergeGCheap' getInitialSubscribers updateMe destroy
1736 where
1794 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)
1738 updateMe subscriber heightBagRef oldParents p = do
1796 updateMe subscriber heightBagRef oldParents p = do
1739 -- Prepare new parents for insertion
1797 -- 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)
1741 subscribeParent k e = do
1799 subscribeParent k e = do
1742 keyRef <- liftIO $ newIORef k
1800 keyRef <- liftIO $ newIORef k
1743 let s = subscriber $ liftIO $ readIORef keyRef
1801 let s = subscriber $ liftIO $ readIORef keyRef
@@ -1746,9 +1804,9 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1746 newParentHeight <- getEventSubscribedHeight subd
1804 newParentHeight <- getEventSubscribedHeight subd
1747 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1805 modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1748 return $ MergeSubscribedParentWithMove subscription keyRef
1806 return $ MergeSubscribedParentWithMove subscription keyRef
1749− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1807+ p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey (\k q -> subscribeParent k (nt q)) p
1750 -- Collect old parents for deletion and update the keys of moved parents
1808 -- 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
1752 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1810 moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1753 Nothing -> do
1811 Nothing -> do
1754 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
1812 oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
@@ -1759,46 +1817,47 @@ mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1759 return Nothing
1817 return Nothing
1760 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1818 toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1761 return (toDelete, applyAlways p' oldParents)
1819 return (toDelete, applyAlways p' oldParents)
1762− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1820+ getInitialSubscribers :: MergeInitFunc k v q x (MergeSubscribedParentWithMove x k)
1763 getInitialSubscribers initialParents subscriber = do
1821 getInitialSubscribers initialParents subscriber = do
1764 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1822 subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1765 keyRef <- liftIO $ newIORef k
1823 keyRef <- liftIO $ newIORef k
1766 let s = subscriber $ liftIO $ readIORef keyRef
1824 let s = subscriber $ liftIO $ readIORef keyRef
1767− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1825+ (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead (nt e) s
1768 height <- liftIO $ getEventSubscribedHeight parentSubd
1826 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)
1770 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1828 return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1771 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1829 , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1772 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1830 , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1773 )
1831 )
1832+
1774 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1833 destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1775 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1834 destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1776
1835
1777−type MergeUpdateFunc k x p s
1836+type MergeUpdateFunc k v x p s
1778− = (forall a. EventM x (k a) -> Subscriber x a)
1837+ = (forall a. EventM x (k a) -> Subscriber x (v a))
1779 -> IORef HeightBag
1838 -> IORef HeightBag
1780 -> DMap k s
1839 -> DMap k s
1781 -> p
1840 -> p
1782 -> EventM x ([EventSubscription x], DMap k s)
1841 -> EventM x ([EventSubscription x], DMap k s)
1783
1842
1784−type MergeInitFunc k x s
1843+type MergeInitFunc k v q x s
1785− = DMap k (Event x)
1844+ = DMap k q
1786− -> (forall a. EventM x (k a) -> Subscriber x a)
1845+ -> (forall a. EventM x (k a) -> Subscriber x (v a))
1787− -> EventM x (DMap k Identity, [Height], DMap k s)
1846+ -> EventM x (DMap k v, [Height], DMap k s)
1788
1847
1789type MergeDestroyFunc k s
1848type MergeDestroyFunc k s
1790 = DMap k s
1849 = DMap k s
1791 -> IO ()
1850 -> IO ()
1792
1851
1793−data Merge x k s = Merge
1852+data Merge x k v s = Merge
1794 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1853 { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1795 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1854 , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1796 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1855 , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1797− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1856+ , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k v))
1798− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1857+ , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k v))
1799 }
1858 }
1800
1859
1801−invalidateMergeHeight :: Merge x k s -> IO ()
1860+invalidateMergeHeight :: Merge x k v s -> IO ()
1802invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1861invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1803
1862
1804invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1863invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
@@ -1808,8 +1867,7 @@ invalidateMergeHeight' heightRef sub = do
1808 writeIORef heightRef $! invalidHeight
1867 writeIORef heightRef $! invalidHeight
1809 subscriberInvalidateHeight sub oldHeight
1868 subscriberInvalidateHeight sub oldHeight
1810
1869
1811−
1812−revalidateMergeHeight :: Merge x k s -> IO ()
1870+revalidateMergeHeight :: Merge x k v s -> IO ()
1813revalidateMergeHeight m = do
1871revalidateMergeHeight m = do
1814 currentHeight <- readIORef $ _merge_heightRef m
1872 currentHeight <- readIORef $ _merge_heightRef m
1815 when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1873 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
1825 subscriberRecalculateHeight (_merge_sub m) height
1883 subscriberRecalculateHeight (_merge_sub m) height
1826 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1884 GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1827
1885
1828−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1886+scheduleMergeSelf :: HasSpiderTimeline x => Merge x k v s -> Height -> EventM x ()
1829scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1887scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1830 vals <- liftIO $ readIORef $ _merge_accumRef m
1888 vals <- liftIO $ readIORef $ _merge_accumRef m
1831 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
1889 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
1832 --TODO: Assert that m is not empty
1890 --TODO: Assert that m is not empty
1833 subscriberPropagate (_merge_sub m) vals
1891 subscriberPropagate (_merge_sub m) vals
1834
1892
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)
1836mergeSubscriber m getKey = Subscriber
1894mergeSubscriber m getKey = Subscriber
1837 { subscriberPropagate = \a -> do
1895 { subscriberPropagate = \a -> do
1838 oldM <- liftIO $ readIORef $ _merge_accumRef m
1896 oldM <- liftIO $ readIORef $ _merge_accumRef m
1839 k <- getKey
1897 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
1841 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1899 tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1842 liftIO $ writeIORef (_merge_accumRef m) $! newM
1900 liftIO $ writeIORef (_merge_accumRef m) $! newM
1843 when (DMap.null oldM) $ do -- Only schedule the firing once
1901 when (DMap.null oldM) $ do -- Only schedule the firing once
@@ -1868,7 +1926,7 @@ mergeSubscriber m getKey = Subscriber
1868 }
1926 }
1869
1927
1870--TODO: Be able to run as much of this as possible promptly
1928--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
1872updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1930updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1873 where updateMe = do
1931 where updateMe = do
1874 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1932 oldParents <- liftIO $ readIORef $ _merge_parentsRef m
@@ -1876,9 +1934,10 @@ updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m)
1876 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1934 liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1877 return subscriptionsToKill
1935 return subscriptionsToKill
1878
1936
1879−{-# INLINE mergeCheap' #-}
1937+{-# INLINE mergeGCheap' #-}
1938+mergeGCheap' :: forall k v x p s q. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k q)
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)
1939+ => MergeInitFunc k v q x s -> MergeUpdateFunc k v x p s -> MergeDestroyFunc k s -> DynamicS x p -> Event x (DMap k v)
1881−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1940+mergeGCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1882 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1941 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1883 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1942 accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1884 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1943 heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
@@ -1926,11 +1985,11 @@ mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1926 , occ
1985 , occ
1927 )
1986 )
1928
1987
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)
1930mergeInt = cacheEvent . mergeIntCheap
1989mergeInt = cacheEvent . mergeIntCheap
1931
1990
1932{-# INLINABLE mergeIntCheap #-}
1991{-# 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)
1934mergeIntCheap d = Event $ \sub -> do
1993mergeIntCheap d = Event $ \sub -> do
1935 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1994 initialParents <- readBehaviorUntracked $ dynamicCurrent d
1936 accum <- liftIO $ FastMutableIntMap.newEmpty
1995 accum <- liftIO $ FastMutableIntMap.newEmpty
@@ -2027,14 +2086,16 @@ mergeIntCheap d = Event $ \sub -> do
2027 )
2086 )
2028
2087
2029newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2088newtype 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) }
2030
2090
2031−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2091+fanG :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k v) -> EventSelectorG x k v
2032−fan e =
2092+fanG e = unsafePerformIO $ do
2093+ ref <- newIORef Nothing
2033 let f = Fan
2094 let f = Fan
2034 { fanParent = e
2095 { fanParent = e
2035− , fanSubscribed = unsafeNewIORef e Nothing
2096+ , fanSubscribed = ref
2036 }
2097 }
2037− in EventSelector $ \k -> eventFan k f
2098+ pure $ EventSelectorG $ \k -> eventFan k f
2038
2099
2039runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2100runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2040runHoldInits holdInitRef dynInitRef mergeInitRef = do
2101runHoldInits holdInitRef dynInitRef mergeInitRef = do
@@ -2088,7 +2149,7 @@ clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeI
2088-- | Run an event action outside of a frame
2149-- | Run an event action outside of a frame
2089runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2150runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2090runFrame a = SpiderHost $ do
2151runFrame a = SpiderHost $ do
2091− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2152+ let env = _spiderTimeline_eventEnv $ unSTE (spiderTimeline :: SpiderTimelineEnv x)
2092 let go = do
2153 let go = do
2093 result <- a
2154 result <- a
2094 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
2155 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
2146 myHeight <- readIORef $ switchSubscribedHeight subscribed
2207 myHeight <- readIORef $ switchSubscribedHeight subscribed
2147 when (parentHeight /= myHeight) $ do
2208 when (parentHeight /= myHeight) $ do
2148 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2209 writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2149− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2210+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2150 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
2211 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
2151 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
2212 forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
2152 unsubscribe subscription
2213 unsubscribe subscription
@@ -2187,7 +2248,7 @@ invalidateCoincidenceHeight subscribed = do
2187 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2248 oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2188 when (oldHeight /= invalidHeight) $ do
2249 when (oldHeight /= invalidHeight) $ do
2189 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2250 writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2190− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2251+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2191
2252
2192updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
2253updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
2193updateSwitchHeight new subscribed = do
2254updateSwitchHeight new subscribed = do
@@ -2195,7 +2256,7 @@ updateSwitchHeight new subscribed = do
2195 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2256 when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2196 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2257 when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2197 writeIORef (switchSubscribedHeight subscribed) $! new
2258 writeIORef (switchSubscribedHeight subscribed) $! new
2198− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2259+ WeakBag.traverse_ (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2199
2260
2200recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2261recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2201recalculateCoincidenceHeight subscribed = do
2262recalculateCoincidenceHeight subscribed = do
@@ -2204,7 +2265,7 @@ recalculateCoincidenceHeight subscribed = do
2204 height <- calculateCoincidenceHeight subscribed
2265 height <- calculateCoincidenceHeight subscribed
2205 when (height /= invalidHeight) $ do
2266 when (height /= invalidHeight) $ do
2206 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2267 writeIORef (coincidenceSubscribedHeight subscribed) $! height
2207− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2268+ WeakBag.traverse_ (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2208
2269
2209calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
2270calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
2210calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
2271calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
@@ -2299,7 +2360,7 @@ instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x))
2299 fail _ = error "Dynamic does not support 'fail'"
2360 fail _ = error "Dynamic does not support 'fail'"
2300
2361
2301{-# INLINABLE newJoinDyn #-}
2362{-# 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)
2303newJoinDyn d =
2364newJoinDyn d =
2304 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2365 let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2305 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
2366 eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
@@ -2449,7 +2510,7 @@ unsafeNewSpiderTimelineEnv = do
2449#ifdef DEBUG
2510#ifdef DEBUG
2450 depthRef <- newIORef 0
2511 depthRef <- newIORef 0
2451#endif
2512#endif
2452− return $ SpiderTimelineEnv
2513+ return $ STE $ SpiderTimelineEnv
2453 { _spiderTimeline_lock = lock
2514 { _spiderTimeline_lock = lock
2454 , _spiderTimeline_eventEnv = env
2515 , _spiderTimeline_eventEnv = env
2455#ifdef DEBUG
2516#ifdef DEBUG
@@ -2461,17 +2522,17 @@ unsafeNewSpiderTimelineEnv = do
2461newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2522newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2462newSpiderTimeline = withSpiderTimeline (pure . Some)
2523newSpiderTimeline = withSpiderTimeline (pure . Some)
2463
2524
2464−data LocalSpiderTimeline x s
2525+data LocalSpiderTimeline (x :: Type) s
2465
2526
2466instance Reifies s (SpiderTimelineEnv x) =>
2527instance Reifies s (SpiderTimelineEnv x) =>
2467 HasSpiderTimeline (LocalSpiderTimeline x s) where
2528 HasSpiderTimeline (LocalSpiderTimeline x s) where
2468− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2529+ spiderTimeline = localSpiderTimeline Proxy $ reflect (Proxy :: Proxy s)
2469
2530
2470localSpiderTimeline
2531localSpiderTimeline
2471− :: Proxy s
2532+ :: proxy s
2472 -> SpiderTimelineEnv x
2533 -> SpiderTimelineEnv x
2473 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2534 -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2474−localSpiderTimeline _ = unsafeCoerce
2535+localSpiderTimeline _ = coerce
2475
2536
2476-- | Pass a new timeline to the given function.
2537-- | Pass a new timeline to the given function.
2477withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2538withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
@@ -2479,18 +2540,18 @@ withSpiderTimeline k = do
2479 env <- unsafeNewSpiderTimelineEnv
2540 env <- unsafeNewSpiderTimelineEnv
2480 reify env $ \s -> k $ localSpiderTimeline s env
2541 reify env $ \s -> k $ localSpiderTimeline s env
2481
2542
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)
2483
2544
2484type ComputeM = EventM
2545type ComputeM = EventM
2485
2546
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)
2487
2548
2488instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2549instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2489 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2550 {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2490 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2551 newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2491 newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2552 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)
2553+ newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: DynamicS x (Identity a) } -- deriving (Functor, Applicative, Monad)
2493− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2554+ newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: DynamicS x p }
2494 type PullM (SpiderTimeline x) = SpiderPullM x
2555 type PullM (SpiderTimeline x) = SpiderPullM x
2495 type PushM (SpiderTimeline x) = SpiderPushM x
2556 type PushM (SpiderTimeline x) = SpiderPushM x
2496 {-# INLINABLE never #-}
2557 {-# INLINABLE never #-}
@@ -2503,10 +2564,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2503 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2564 pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2504 {-# INLINABLE pull #-}
2565 {-# INLINABLE pull #-}
2505 pull = SpiderBehavior . pull . coerce
2566 pull = SpiderBehavior . pull . coerce
2506− {-# INLINABLE merge #-}
2567+ {-# INLINABLE fanG #-}
2507− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2508− {-# INLINABLE fan #-}
2509− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2568+ 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
2510 {-# INLINABLE switch #-}
2576 {-# INLINABLE switch #-}
2511 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2577 switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2512 {-# INLINABLE coincidence #-}
2578 {-# INLINABLE coincidence #-}
@@ -2514,15 +2580,15 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2514 {-# INLINABLE current #-}
2580 {-# INLINABLE current #-}
2515 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2581 current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2516 {-# INLINABLE updated #-}
2582 {-# INLINABLE updated #-}
2517− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2583+ updated = SpiderEvent #. dynamicUpdated .# fmap coerce . unSpiderDynamic
2518 {-# INLINABLE unsafeBuildDynamic #-}
2584 {-# INLINABLE unsafeBuildDynamic #-}
2519 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2585 unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2520 {-# INLINABLE unsafeBuildIncremental #-}
2586 {-# INLINABLE unsafeBuildIncremental #-}
2521 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2587 unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2522− {-# INLINABLE mergeIncremental #-}
2588+ {-# INLINABLE mergeIncrementalG #-}
2523− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2589+ mergeIncrementalG nt = SpiderEvent #. mergeG (coerce #. nt) .# unSpiderIncremental
2524− {-# INLINABLE mergeIncrementalWithMove #-}
2590+ {-# INLINABLE mergeIncrementalWithMoveG #-}
2525− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2591+ mergeIncrementalWithMoveG nt = SpiderEvent #. mergeWithMove (coerce #. nt) .# unSpiderIncremental
2526 {-# INLINABLE currentIncremental #-}
2592 {-# INLINABLE currentIncremental #-}
2527 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2593 currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2528 {-# INLINABLE updatedIncremental #-}
2594 {-# INLINABLE updatedIncremental #-}
@@ -2533,9 +2599,10 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2533 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2599 return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2534 eventCoercion Coercion = Coercion
2600 eventCoercion Coercion = Coercion
2535 behaviorCoercion Coercion = Coercion
2601 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
2537 {-# INLINABLE mergeIntIncremental #-}
2604 {-# 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
2539 {-# INLINABLE fanInt #-}
2606 {-# INLINABLE fanInt #-}
2540 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2607 fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2541
2608
@@ -2560,7 +2627,7 @@ instance MonadAtomicRef (EventM x) where
2560 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2627 atomicModifyRef r f = liftIO $ atomicModifyRef r f
2561
2628
2562-- | The monad for actions that manipulate a Spider timeline identified by @x@
2629-- | 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)
2564
2631
2565instance Monad (SpiderHost x) where
2632instance Monad (SpiderHost x) where
2566 {-# INLINABLE (>>=) #-}
2633 {-# INLINABLE (>>=) #-}
@@ -2582,7 +2649,7 @@ runSpiderHost (SpiderHost a) = a
2582runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2649runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2583runSpiderHostForTimeline (SpiderHost a) _ = a
2650runSpiderHostForTimeline (SpiderHost a) _ = a
2584
2651
2585−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2652+newtype SpiderHostFrame (x :: Type) a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2586 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2653 deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2587
2654
2588instance Monad (SpiderHostFrame x) where
2655instance Monad (SpiderHostFrame x) where
src/Reflex/TriggerEvent/Class.hs−1
@@ -2,7 +2,6 @@
2-- new 'Event's that can be triggered from 'IO'.
2-- new 'Event's that can be triggered from 'IO'.
3{-# LANGUAGE FlexibleInstances #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FunctionalDependencies #-}
4{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
7module Reflex.TriggerEvent.Class
6module Reflex.TriggerEvent.Class
8 ( TriggerEvent (..)
7 ( TriggerEvent (..)
test/EventWriterT.hs+6−4
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
@@ -13,6 +14,10 @@ import Data.Functor.Misc
13import qualified Data.Map as M
14import qualified Data.Map as M
14import Data.These
15import Data.These
15
16
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+
16import Reflex
21import Reflex
17import Reflex.EventWriter.Base
22import Reflex.EventWriter.Base
18import Test.Run
23import Test.Run
@@ -48,16 +53,13 @@ unwrapApp x appIn = do
48 return e
53 return e
49
54
50testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
55testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
51−testOrdering pulse = do
52− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
56+testOrdering pulse = forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
53− return ()
54
57
55testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
58testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
56testSimultaneous pulse = do
59testSimultaneous pulse = do
57 let e0 = fmapMaybe (^? here) pulse
60 let e0 = fmapMaybe (^? here) pulse
58 e1 = fmapMaybe (^? there) pulse
61 e1 = fmapMaybe (^? there) pulse
59 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
62 forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
60− return ()
61
63
62-- | Test that a widget telling and event which fires at the same time it has been replaced
64-- | Test that a widget telling and event which fires at the same time it has been replaced
63-- doesn't count along with the new widget.
65-- doesn't count along with the new widget.
test/GC.hs+4−3
@@ -27,6 +27,7 @@ import qualified Reflex.Spider.Internal as S
27
27
28import System.Exit
28import System.Exit
29import System.Mem
29import System.Mem
30+import Data.Coerce
30
31
31main :: IO ()
32main :: IO ()
32main = do
33main = do
@@ -46,7 +47,7 @@ hostPerf ref = S.runSpiderHost $ do
46 eventToPerform <- Host.runHostFrame $ do
47 eventToPerform <- Host.runHostFrame $ do
47 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
48 (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
48 <- S.SpiderHostFrame
49 <- S.SpiderHostFrame
49− $ fmap ( S.merge
50+ $ fmap ( S.mergeG coerce
50 . S.dynamicHold)
51 . S.dynamicHold)
51 $ S.hold DMap.empty
52 $ S.hold DMap.empty
52 -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
53 -- 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
55 { S.subscriberPropagate = S.subscriberPropagate sub
56 { S.subscriberPropagate = S.subscriberPropagate sub
56 }
57 }
57 return (s, o))
58 return (s, o))
58− $ runIdentity <$> S.select
59+ $ runIdentity . runIdentity <$> S.selectG
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
60+ (S.fanG $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
60 (WrapArg Request)
61 (WrapArg Request)
61 return $ alignWith (mergeThese (<>))
62 return $ alignWith (mergeThese (<>))
62 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
63 (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
test/QueryT.hs+4
@@ -19,6 +19,10 @@ import Data.Map.Monoidal (MonoidalMap)
19import Data.Semigroup
19import Data.Semigroup
20import Data.These
20import Data.These
21
21
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+
22import Reflex
26import Reflex
23import Reflex.Patch.MapWithMove
27import Reflex.Patch.MapWithMove
24import Test.Run
28import Test.Run
test/Reflex/Bench/Focused.hs+12
@@ -130,6 +130,16 @@ mapDynChain = iterM (return . fmap (+1))
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
130joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
131joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132
132
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+
133combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
143combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
144combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135
145
@@ -308,6 +318,8 @@ dynamics :: Word -> [(String, TestCase)]
308dynamics n =
318dynamics n =
309 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
319 [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
320 , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
321+ , testE "holdDynChain" $ fmap updated $ holdDynChain n =<< d
322+ , testE "buildDynChain" $ fmap updated $ buildDynChain n =<< d
311 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
323 , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
324 , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
325 , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
test/Reflex/Test/CrossImpl.hs+5
@@ -218,6 +218,11 @@ testCases =
218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
218 bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219 let b' = pull $ sample =<< sample bb
219 let b' = pull $ sample =<< sample bb
220 return (b', e)
220 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)
221 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
226 , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222 bb <- hold "b" e
227 bb <- hold "b" e
223 bd <- hold never . fmap (const e) =<< headE e
228 bd <- hold never . fmap (const e) =<< headE e
test/RequesterT.hs+5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
1{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE BangPatterns #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE GADTs #-}
4{-# LANGUAGE GADTs #-}
@@ -15,6 +16,10 @@ import Data.Functor.Misc
15import qualified Data.Map as M
16import qualified Data.Map as M
16import Data.These
17import Data.These
17
18
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+
18import Reflex
23import Reflex
19import Reflex.Requester.Base
24import Reflex.Requester.Base
20import Reflex.Requester.Class
25import Reflex.Requester.Class
test/hlint.hs+1
@@ -22,6 +22,7 @@ main = do
22 , "--ignore=Use unless"
22 , "--ignore=Use unless"
23 , "--ignore=Reduce duplication"
23 , "--ignore=Reduce duplication"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
24 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
25 ]
26 ]
26 recurseInto = and <$> sequence
27 recurseInto = and <$> sequence
27 [ fileType ==? Directory
28 [ fileType ==? Directory