on ccd7204 help sign in
.github/workflows/haskell.ymldeleted−41
since splitsnapshots
unifiedsplit
@@ -1,41 +0,0 @@
1−name: github-action
2−
3−on: [push, pull_request]
4−
5−jobs:
6− build:
7− strategy:
8− matrix:
9− ghc: ['8.4.4', '8.6.5', '8.8.4', '8.10.2']
10− os: ['ubuntu-latest', 'macos-latest']
11− runs-on: ${{ matrix.os }}
12−
13− name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
14− steps:
15− - uses: actions/checkout@v2
16− - uses: actions/setup-haskell@v1
17− with:
18− ghc-version: ${{ matrix.ghc }}
19− - name: Cache
20− uses: actions/cache@v1
21− env:
22− cache-name: cache-cabal
23− with:
24− path: ~/.cabal
25− key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
26− restore-keys: |
27− ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
28− ${{ runner.os }}-${{ matrix.ghc }}-build-
29− ${{ runner.os }}-${{ matrix.ghc }}-
30− ${{ runner.os }}
31−
32− - name: Install dependencies
33− run: |
34− cabal update
35− cabal build --only-dependencies --enable-tests --enable-benchmarks
36− - name: Build
37− run: cabal build --enable-tests --enable-benchmarks all
38− - name: Run tests
39− run: cabal test all
40− - name: Build Docs
41− run: cabal haddock
.travis.ymlnew file+177
@@ -0,0 +1,177 @@
1+# This Travis job script has been generated by a script via
2+#
3+# haskell-ci '--config=cabal.haskell-ci' 'cabal.project'
4+#
5+# For more information, see https://github.com/haskell-CI/haskell-ci
6+#
7+# version: 0.3.20190814
8+#
9+language: c
10+dist: xenial
11+sudo: required
12+git:
13+ # whether to recursively clone submodules
14+ submodules: false
15+cache:
16+ directories:
17+ - $HOME/.cabal/packages
18+ - $HOME/.cabal/store
19+before_cache:
20+ - rm -fv $CABALHOME/packages/hackage.haskell.org/build-reports.log
21+ # remove files that are regenerated by 'cabal update'
22+ - rm -fv $CABALHOME/packages/hackage.haskell.org/00-index.*
23+ - rm -fv $CABALHOME/packages/hackage.haskell.org/*.json
24+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.cache
25+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar
26+ - rm -fv $CABALHOME/packages/hackage.haskell.org/01-index.tar.idx
27+ - rm -rfv $CABALHOME/packages/head.hackage
28+matrix:
29+ include:
30+ - compiler: ghcjs-8.4
31+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["cabal-install-3.0"]}}
32+ - compiler: ghc-8.8.1
33+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.8.1","cabal-install-3.0"]}}
34+ - compiler: ghc-8.6.5
35+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.6.5","cabal-install-2.4"]}}
36+ - compiler: ghc-8.4.4
37+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.4.4","cabal-install-2.4"]}}
38+ - compiler: ghc-8.2.2
39+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.2.2","cabal-install-2.4"]}}
40+ - compiler: ghc-8.0.2
41+ addons: {"apt":{"sources":["hvr-ghc"],"packages":["ghc-8.0.2","cabal-install-2.4"]}}
42+before_install:
43+ - |
44+ if [ "$TRAVIS_OS_NAME" = "linux" ]; then
45+ sudo add-apt-repository -y ppa:hvr/ghcjs;
46+ curl -s https://deb.nodesource.com/gpgkey/nodesource.gpg.key | sudo apt-key add -
47+ sudo apt-add-repository 'https://deb.nodesource.com/node_8.x xenial main'
48+ sudo apt-get update;
49+ sudo apt-get install $CC cabal-install-3.0 nodejs;
50+ fi
51+ - HC=$(echo "/opt/$CC/bin/ghc" | sed 's/-/\//')
52+ - WITHCOMPILER="-w $HC"
53+ - |
54+ if echo $CC | grep -q ghcjs; then
55+ GHCJS=true
56+ HC=${HC}js
57+ WITHCOMPILER="--ghcjs ${WITHCOMPILER}js"
58+ else
59+ GHCJS=false;
60+ fi
61+ - if $GHCJS ; then sudo apt-get install -y ghc-8.4.4 ; fi
62+ - if $GHCJS ; then PATH="/opt/ghc/8.4.4/bin:$PATH" ; fi
63+ - HCPKG="$HC-pkg"
64+ - unset CC
65+ - CABAL=/opt/ghc/bin/cabal
66+ - CABALHOME=$HOME/.cabal
67+ - export PATH="$CABALHOME/bin:$PATH"
68+ - TOP=$(pwd)
69+ - "HCNUMVER=$(${HC} --numeric-version|perl -ne '/^(\\d+)\\.(\\d+)\\.(\\d+)(\\.(\\d+))?$/; print(10000 * $1 + 100 * $2 + ($3 == 0 ? $5 != 1 : $3))')"
70+ - echo $HCNUMVER
71+ - CABAL="$CABAL -vnormal+nowrap+markoutput"
72+ - set -o pipefail
73+ - |
74+ echo 'function blue(s) { printf "\033[0;34m" s "\033[0m " }' >> .colorful.awk
75+ echo 'BEGIN { state = "output"; }' >> .colorful.awk
76+ echo '/^-----BEGIN CABAL OUTPUT-----$/ { state = "cabal" }' >> .colorful.awk
77+ echo '/^-----END CABAL OUTPUT-----$/ { state = "output" }' >> .colorful.awk
78+ echo '!/^(-----BEGIN CABAL OUTPUT-----|-----END CABAL OUTPUT-----)/ {' >> .colorful.awk
79+ echo ' if (state == "cabal") {' >> .colorful.awk
80+ echo ' print blue($0)' >> .colorful.awk
81+ echo ' } else {' >> .colorful.awk
82+ echo ' print $0' >> .colorful.awk
83+ echo ' }' >> .colorful.awk
84+ echo '}' >> .colorful.awk
85+ - cat .colorful.awk
86+ - |
87+ color_cabal_output () {
88+ awk -f $TOP/.colorful.awk
89+ }
90+ - echo text | color_cabal_output
91+install:
92+ - ${CABAL} --version
93+ - echo "$(${HC} --version) [$(${HC} --print-project-git-commit-id 2> /dev/null || echo '?')]"
94+ - node --version
95+ - echo $GHCJS
96+ - TEST=--enable-tests
97+ - BENCH=--enable-benchmarks
98+ - BENCH=--disable-benchmarks
99+ - HEADHACKAGE=false
100+ - rm -f $CABALHOME/config
101+ - |
102+ echo "verbose: normal +nowrap +markoutput" >> $CABALHOME/config
103+ echo "remote-build-reporting: anonymous" >> $CABALHOME/config
104+ echo "write-ghc-environment-files: always" >> $CABALHOME/config
105+ echo "remote-repo-cache: $CABALHOME/packages" >> $CABALHOME/config
106+ echo "logs-dir: $CABALHOME/logs" >> $CABALHOME/config
107+ echo "world-file: $CABALHOME/world" >> $CABALHOME/config
108+ echo "extra-prog-path: $CABALHOME/bin" >> $CABALHOME/config
109+ echo "symlink-bindir: $CABALHOME/bin" >> $CABALHOME/config
110+ echo "installdir: $CABALHOME/bin" >> $CABALHOME/config
111+ echo "build-summary: $CABALHOME/logs/build.log" >> $CABALHOME/config
112+ echo "store-dir: $CABALHOME/store" >> $CABALHOME/config
113+ echo "install-dirs user" >> $CABALHOME/config
114+ echo " prefix: $CABALHOME" >> $CABALHOME/config
115+ echo "repository hackage.haskell.org" >> $CABALHOME/config
116+ echo " url: http://hackage.haskell.org/" >> $CABALHOME/config
117+ - cat $CABALHOME/config
118+ - rm -fv cabal.project cabal.project.local cabal.project.freeze
119+ - travis_retry ${CABAL} v2-update -v
120+ - if $GHCJS ; then (cd /tmp && ${CABAL} v2-install -w ghc-8.4.4 happy) ; fi
121+ # Generate cabal.project
122+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
123+ - touch cabal.project
124+ - |
125+ echo "packages: ." >> cabal.project
126+ - |
127+ - cat cabal.project || true
128+ - cat cabal.project.local || true
129+ - if [ -f "./configure.ac" ]; then (cd "." && autoreconf -i); fi
130+ - ${CABAL} v2-freeze $WITHCOMPILER ${TEST} ${BENCH} | color_cabal_output
131+ - "cat cabal.project.freeze | sed -E 's/^(constraints: *| *)//' | sed 's/any.//'"
132+ - rm cabal.project.freeze
133+script:
134+ - DISTDIR=$(mktemp -d /tmp/dist-test.XXXX)
135+ # Packaging...
136+ - ${CABAL} v2-sdist all | color_cabal_output
137+ # Unpacking...
138+ - mv dist-newstyle/sdist/*.tar.gz ${DISTDIR}/
139+ - cd ${DISTDIR} || false
140+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec tar -xvf '{}' \;
141+ - find . -maxdepth 1 -type f -name '*.tar.gz' -exec rm '{}' \;
142+ - PKGDIR_reflex="$(find . -maxdepth 1 -type d -regex '.*/reflex-[0-9.]*')"
143+ # Generate cabal.project
144+ - rm -rf cabal.project cabal.project.local cabal.project.freeze
145+ - touch cabal.project
146+ - |
147+ echo "packages: ${PKGDIR_reflex}" >> cabal.project
148+ - |
149+ - cat cabal.project || true
150+ - cat cabal.project.local || true
151+ # Building...
152+ # this builds all libraries and executables (without tests/benchmarks)
153+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks all | color_cabal_output
154+ # Building with tests and benchmarks...
155+ # build & run tests, build benchmarks
156+ - ${CABAL} v2-build $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output
157+ # Testing...
158+ - if ! $GHCJS ; then ${CABAL} v2-test $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
159+ # cabal check...
160+ - (cd ${PKGDIR_reflex} && ${CABAL} -vnormal check)
161+ # haddock...
162+ - if ! $GHCJS ; then ${CABAL} v2-haddock $WITHCOMPILER ${TEST} ${BENCH} all | color_cabal_output ; fi
163+ # Constraint sets
164+ - rm -rf cabal.project.local
165+ # Constraint set no-th
166+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex -use-template-haskell' all | color_cabal_output
167+ # Constraint set old-these
168+ - if $GHCJS || ! $GHCJS && [ $HCNUMVER -lt 80800 ] ; then ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='these <1' all | color_cabal_output ; fi
169+ # Constraint set old-witherable
170+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='witherable <0.3.2' all | color_cabal_output
171+ # Constraint set debug-propagation
172+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-propagation' all | color_cabal_output
173+ # Constraint set debug-cycles
174+ - ${CABAL} v2-build $WITHCOMPILER --disable-tests --disable-benchmarks --constraint='reflex +debug-cycles' all | color_cabal_output
175+
176+# REGENDATA ["--config=cabal.haskell-ci","cabal.project"]
177+# EOF
ChangeLog.md+1−25
@@ -1,38 +1,14 @@
1# Revision history for reflex
1# Revision history for reflex
2
2
3−## 0.8.2.0
4−
5−* Add `matchResponseMapWithRequests`, which it similar to `matchResponsesWithRequests` but allows processing of multiple responses at once.
6−
7−## 0.8.1.1
8−
9−* Allow newer hlint for older GHCs, and add upper bound for newer GHCs
10−
11−## 0.8.1.0
12−
13−* Add support for GHC 8.10
14−* Drop support for GHC < 8.4
15−
16## 0.8.0.0
3## 0.8.0.0
17
4
18* Replace 0.7.2.0 with 0.8.0.0 to reflect the `MonadHold` interface change. Deprecates 0.7.2.0.
5* Replace 0.7.2.0 with 0.8.0.0 to reflect the `MonadHold` interface change. Deprecates 0.7.2.0.
19
6
20−## 0.7.2.0 -- *Deprecated*
7+## 0.7.2.0
21−
22* ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`.
8* ([#416](https://github.com/reflex-frp/reflex/pull/416)) Add `now :: m (Event t ())` to `MonadHold`.
23* Extend some dependency version bounds
9* Extend some dependency version bounds
24* Fix HLint 3 test
10* Fix HLint 3 test
25
11
26−## 0.7.1.1
27−
28−*Backport release*.
29−Changes do not carry forward to 0.7.2.0.
30−
31−* Add support for GHC 8.10
32−* Drop support for GHC < 8.4
33−* Extend some dependency version bounds
34−* Fix HLint 3 test
35−
36## 0.7.1.0
12## 0.7.1.0
37
13
38* ([#413](https://github.com/reflex-frp/reflex/pull/413), [#417](https://github.com/reflex-frp/reflex/pull/417)) Add `Reflex.Host.Headless` module which provides `runHeadlessApp` as an easy way to run a Reflex network in a "headless" environment.
14* ([#413](https://github.com/reflex-frp/reflex/pull/413), [#417](https://github.com/reflex-frp/reflex/pull/417)) Add `Reflex.Host.Headless` module which provides `runHeadlessApp` as an easy way to run a Reflex network in a "headless" environment.
README.md+4−4
@@ -1,6 +1,6 @@
1−# [Reflex](https://reflex-frp.org/)
1+## [Reflex](https://reflex-frp.org/)
2
2
3−[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/reflex.svg)](https://hackage.haskell.org/package/reflex) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/reflex/badge)](https://matrix.hackage.haskell.org/#/package/reflex) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE)
3+[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/reflex.svg)](https://hackage.haskell.org/package/reflex) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/reflex/badge)](https://matrix.hackage.haskell.org/#/package/reflex) [![Travis CI](https://api.travis-ci.org/reflex-frp/reflex.svg?branch=master)](https://travis-ci.org/reflex-frp/reflex) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/reflex/blob/master/LICENSE)
4
4
5Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.
5Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.
6
6
@@ -8,7 +8,7 @@ Reflex is a fully-deterministic, higher-order Functional Reactive Programming in
8
8
9**Visit https://reflex-frp.org for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
9**Visit https://reflex-frp.org for more information, tutorials, documentation and [examples](https://examples.reflex-frp.org/).**
10
10
11−## Resources
11+### Resources
12
12
13* [Official Website](https://reflex-frp.org)
13* [Official Website](https://reflex-frp.org)
14* [Quick Reference](Quickref.md)
14* [Quick Reference](Quickref.md)
@@ -18,6 +18,6 @@ Reflex is a fully-deterministic, higher-order Functional Reactive Programming in
18* [/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
18* [/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
19* [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
19* [irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
20
20
21−## Hacking
21+### Hacking
22
22
23From the root of a [Reflex Platform](https://github.com/reflex-frp/reflex-platform) checkout, run `./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex`. This will check out the reflex source code into the `haskell-overlays/reflex-packages/dep/reflex` directory. You can then point that checkout at your fork, make changes, etc. Use the `./try-reflex` or `./scripts/work-on` scripts to start a shell in which you can test your changes.
23From the root of a [Reflex Platform](https://github.com/reflex-frp/reflex-platform) checkout, run `./scripts/hack-on haskell-overlays/reflex-packages/dep/reflex`. This will check out the reflex source code into the `haskell-overlays/reflex-packages/dep/reflex` directory. You can then point that checkout at your fork, make changes, etc. Use the `./try-reflex` or `./scripts/work-on` scripts to start a shell in which you can test your changes.
cabal.project.freezenew file+2
@@ -0,0 +1,2 @@
1+constraints: any.text < 1.2.4.0
2+ , hlint < 2.2.6 || > 2.2.6
reflex.cabal+10−10
@@ -1,5 +1,5 @@
1Name: reflex
1Name: reflex
2−Version: 0.8.2.0
2+Version: 0.8.0.0
3Synopsis: Higher-order Functional Reactive Programming
3Synopsis: Higher-order Functional Reactive Programming
4Description:
4Description:
5 Interactive programs without callbacks or side-effects.
5 Interactive programs without callbacks or side-effects.
@@ -28,8 +28,8 @@ extra-source-files:
28 ChangeLog.md
28 ChangeLog.md
29
29
30tested-with:
30tested-with:
31− GHC ==8.4.4 || ==8.6.5 || ==8.8.1 || ==8.10.2,
31+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1,
32− GHCJS ==8.6
32+ GHCJS ==8.4
33
33
34flag use-reflex-optimizer
34flag use-reflex-optimizer
35 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
35 description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
@@ -71,15 +71,15 @@ library
71 hs-source-dirs: src
71 hs-source-dirs: src
72 build-depends:
72 build-depends:
73 MemoTrie == 0.6.*,
73 MemoTrie == 0.6.*,
74− base >= 4.11 && < 4.15,
74+ base >= 4.9 && < 4.14,
75 bifunctors >= 5.2 && < 5.6,
75 bifunctors >= 5.2 && < 5.6,
76 comonad >= 5.0.4 && < 5.1,
76 comonad >= 5.0.4 && < 5.1,
77− constraints >= 0.10 && <0.14,
78 constraints-extras >= 0.3 && < 0.4,
77 constraints-extras >= 0.3 && < 0.4,
79 containers >= 0.6 && < 0.7,
78 containers >= 0.6 && < 0.7,
80 data-default >= 0.5 && < 0.8,
79 data-default >= 0.5 && < 0.8,
81 dependent-map >= 0.3 && < 0.5,
80 dependent-map >= 0.3 && < 0.5,
82 exception-transformers == 0.4.*,
81 exception-transformers == 0.4.*,
82+ free == 5.1.*,
83 lens >= 4.7 && < 5,
83 lens >= 4.7 && < 5,
84 mmorph >= 1.0 && < 1.2,
84 mmorph >= 1.0 && < 1.2,
85 monad-control >= 1.0.1 && < 1.1,
85 monad-control >= 1.0.1 && < 1.1,
@@ -87,7 +87,7 @@ library
87 patch >= 0.0.1 && < 0.1,
87 patch >= 0.0.1 && < 0.1,
88 prim-uniq >= 0.1.0.1 && < 0.3,
88 prim-uniq >= 0.1.0.1 && < 0.3,
89 primitive >= 0.5 && < 0.8,
89 primitive >= 0.5 && < 0.8,
90− profunctors >= 5.3 && < 5.7,
90+ profunctors >= 5.3 && < 5.6,
91 random == 1.1.*,
91 random == 1.1.*,
92 ref-tf == 0.4.*,
92 ref-tf == 0.4.*,
93 reflection == 2.1.*,
93 reflection == 2.1.*,
@@ -97,7 +97,7 @@ library
97 time >= 1.4 && < 1.10,
97 time >= 1.4 && < 1.10,
98 transformers >= 0.5.6.0 && < 0.6,
98 transformers >= 0.5.6.0 && < 0.6,
99 unbounded-delays >= 0.1.0.0 && < 0.2,
99 unbounded-delays >= 0.1.0.0 && < 0.2,
100− witherable >= 0.3 && < 0.5
100+ witherable >= 0.3 && < 0.4
101
101
102 if flag(split-these)
102 if flag(split-these)
103 build-depends: these >= 1 && <1.2,
103 build-depends: these >= 1 && <1.2,
@@ -186,7 +186,7 @@ library
186 dependent-sum >= 0.6 && < 0.8,
186 dependent-sum >= 0.6 && < 0.8,
187 haskell-src-exts >= 1.16 && < 1.24,
187 haskell-src-exts >= 1.16 && < 1.24,
188 haskell-src-meta >= 0.6 && < 0.9,
188 haskell-src-meta >= 0.6 && < 0.9,
189− template-haskell >= 2.9 && < 2.17
189+ template-haskell >= 2.9 && < 2.16
190 exposed-modules:
190 exposed-modules:
191 Reflex.Dynamic.TH
191 Reflex.Dynamic.TH
192 other-extensions: TemplateHaskell
192 other-extensions: TemplateHaskell
@@ -259,9 +259,9 @@ test-suite hlint
259 , filepath
259 , filepath
260 , filemanip
260 , filemanip
261 if impl(ghc >= 8.8)
261 if impl(ghc >= 8.8)
262− build-depends: hlint >= 3 && < 4
262+ build-depends: hlint >= 3
263 else
263 else
264− build-depends: hlint (< 2.1 || >= 2.2.2) && < 4
264+ build-depends: hlint (< 2.1 || >= 2.2.2) && < 3
265 if impl(ghcjs)
265 if impl(ghcjs)
266 buildable: False
266 buildable: False
267
267
src/Reflex/Dynamic.hs+1−1
@@ -348,7 +348,7 @@ factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
348--
348--
349-- > demuxed (demux d) k === fmap (== k) d
349-- > demuxed (demux d) k === fmap (== k) d
350--
350--
351−-- However, when 'demuxed' is used multiple times, the complexity is only
351+-- However, when getDemuxed is used multiple times, the complexity is only
352-- /O(log(n))/, rather than /O(n)/ for fmap.
352-- /O(log(n))/, rather than /O(n)/ for fmap.
353data Demux t k = Demux { demuxValue :: Behavior t k
353data Demux t k = Demux { demuxValue :: Behavior t k
354 , demuxSelector :: EventSelector t (Const2 k Bool)
354 , demuxSelector :: EventSelector t (Const2 k Bool)
src/Reflex/Profiled.hs+1−1
@@ -147,7 +147,7 @@ instance Reflex t => Reflex (ProfiledTimeline t) where
147 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
147 pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
148 pull = Behavior_Profiled . pull . coerce
148 pull = Behavior_Profiled . pull . coerce
149 fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
149 fanG (Event_Profiled e) = EventSelectorG $ coerce $ selectG (fanG $ profileEvent e)
150− mergeG :: forall z (k :: z -> *) q v. GCompare k
150+ mergeG :: forall (k :: z -> *) q v. GCompare k
151 => (forall a. q a -> Event (ProfiledTimeline t) (v a))
151 => (forall a. q a -> Event (ProfiledTimeline t) (v a))
152 -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
152 -> DMap k q -> Event (ProfiledTimeline t) (DMap k v)
153 mergeG nt = Event_Profiled #. mergeG (coerce nt)
153 mergeG nt = Event_Profiled #. mergeG (coerce nt)
src/Reflex/Requester/Base.hs+26−91
@@ -1,7 +1,6 @@
1-- | This module provides 'RequesterT', the standard implementation of
1-- | This module provides 'RequesterT', the standard implementation of
2-- 'Requester'.
2-- 'Requester'.
3{-# LANGUAGE AllowAmbiguousTypes #-}
3{-# LANGUAGE AllowAmbiguousTypes #-}
4−{-# LANGUAGE ApplicativeDo #-}
5{-# LANGUAGE CPP #-}
4{-# LANGUAGE CPP #-}
6{-# LANGUAGE EmptyDataDecls #-}
5{-# LANGUAGE EmptyDataDecls #-}
7{-# LANGUAGE ExistentialQuantification #-}
6{-# LANGUAGE ExistentialQuantification #-}
@@ -33,7 +32,6 @@ module Reflex.Requester.Base
33 , requesterDataToList
32 , requesterDataToList
34 , singletonRequesterData
33 , singletonRequesterData
35 , matchResponsesWithRequests
34 , matchResponsesWithRequests
36− , matchResponseMapWithRequests
37 , multiEntry
35 , multiEntry
38 , unMultiEntry
36 , unMultiEntry
39 , requesting'
37 , requesting'
@@ -58,7 +56,6 @@ import Control.Monad.Ref
58import Control.Monad.State.Strict
56import Control.Monad.State.Strict
59import Data.Bits
57import Data.Bits
60import Data.Coerce
58import Data.Coerce
61−import Data.Constraint
62import Data.Dependent.Map (DMap)
59import Data.Dependent.Map (DMap)
63import qualified Data.Dependent.Map as DMap
60import qualified Data.Dependent.Map as DMap
64import Data.Dependent.Sum (DSum (..))
61import Data.Dependent.Sum (DSum (..))
@@ -84,51 +81,19 @@ newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
84
81
85newtype RequesterData f = RequesterData (TagMap (Entry f))
82newtype RequesterData f = RequesterData (TagMap (Entry f))
86
83
87−emptyRequesterData :: RequesterData f
88−emptyRequesterData = RequesterData $ TagMap IntMap.empty
89−
90data RequesterDataKey a where
84data RequesterDataKey a where
91 RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
85 RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
92 RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead
86 RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead
93− RequesterDataKey_Multi2 :: GCompare k => {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
87+ RequesterDataKey_Multi2 :: {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
94 RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
88 RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
95
89
96singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
90singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
97singletonRequesterData rdk v = case rdk of
91singletonRequesterData rdk v = case rdk of
98 RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
92 RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
99 RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
93 RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
100− RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Multi2Contents
94+ RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
101− { _multi2Contents_values = Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
102− , _multi2Contents_dict = Dict
103− }
104 RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
95 RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
105
96
106−mergeRequesterData :: RequesterData f -> RequesterData f -> RequesterData f
107−mergeRequesterData (RequesterData a) (RequesterData b) = RequesterData $ mergeTagMap a b
108−
109−mergeTagMap :: forall f. TagMap (Entry f) -> TagMap (Entry f) -> TagMap (Entry f)
110−mergeTagMap (TagMap m) (TagMap n) =
111− TagMap $ IntMap.unionWithKey (g' combiner) m n
112− where
113− combiner :: forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a
114− combiner k (Entry a) (Entry b) = Entry $ case myKeyType k of
115− MyTagType_Single -> a
116− MyTagType_Multi -> IntMap.unionWith mergeRequesterData a b
117− MyTagType_Multi2 -> case _multi2Contents_dict a of
118− Dict -> Multi2Contents
119− { _multi2Contents_values = Map.unionWith (IntMap.unionWith mergeRequesterData) (_multi2Contents_values a) (_multi2Contents_values b)
120− , _multi2Contents_dict = Dict
121− }
122− MyTagType_Multi3 -> IntMap.unionWith (IntMap.unionWith mergeRequesterData) a b
123− g' :: (forall a. MyTag a -> Entry f a -> Entry f a -> Entry f a) -> Int -> Any -> Any -> Any
124− g' f rawKey a b =
125− let k = MyTag rawKey :: MyTag a
126− fromAny :: Any -> Entry f a
127− fromAny = unsafeCoerce
128− toAny :: Entry f a -> Any
129− toAny = unsafeCoerce
130− in toAny $ f k (fromAny a) (fromAny b)
131−
132requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
97requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
133requesterDataToList (RequesterData m) = do
98requesterDataToList (RequesterData m) = do
134 k :=> Entry e <- tagMapToList m
99 k :=> Entry e <- tagMapToList m
@@ -138,12 +103,11 @@ requesterDataToList (RequesterData m) = do
138 (k', e') <- IntMap.toList e
103 (k', e') <- IntMap.toList e
139 k'' :=> e'' <- requesterDataToList e'
104 k'' :=> e'' <- requesterDataToList e'
140 return $ RequesterDataKey_Multi k k' k'' :=> e''
105 return $ RequesterDataKey_Multi k k' k'' :=> e''
141− MyTagType_Multi2 -> case _multi2Contents_dict e of
106+ MyTagType_Multi2 -> do
142− Dict -> do
143− (k', e') <- Map.toList $ _multi2Contents_values e
107+ (k', e') <- Map.toList e
144− (k'', e'') <- IntMap.toList e'
108+ (k'', e'') <- IntMap.toList e'
145− k''' :=> e''' <- requesterDataToList e''
109+ k''' :=> e''' <- requesterDataToList e''
146− return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
110+ return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
147 MyTagType_Multi3 -> do
111 MyTagType_Multi3 -> do
148 (k', e') <- IntMap.toList e
112 (k', e') <- IntMap.toList e
149 (k'', e'') <- IntMap.toList e'
113 (k'', e'') <- IntMap.toList e'
@@ -171,13 +135,7 @@ traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWith
171 go k (Entry request) = Entry <$> case myKeyType k of
135 go k (Entry request) = Entry <$> case myKeyType k of
172 MyTagType_Single -> f request
136 MyTagType_Single -> f request
173 MyTagType_Multi -> traverse (traverseRequesterData f) request
137 MyTagType_Multi -> traverse (traverseRequesterData f) request
174− MyTagType_Multi2 -> case request of
175− Multi2Contents { _multi2Contents_values = request', _multi2Contents_dict = Dict } -> do
176− v <- traverse (traverse (traverseRequesterData f)) request'
138+ MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
177− pure $ Multi2Contents
178− { _multi2Contents_values = v
179− , _multi2Contents_dict = Dict
180− }
181 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
139 MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
182
140
183-- | 'traverseRequesterData' with its arguments flipped
141-- | 'traverseRequesterData' with its arguments flipped
@@ -221,14 +179,9 @@ instance MyTagTypeOffset Multi3 where
221type family EntryContents request a where
179type family EntryContents request a where
222 EntryContents request (Single a) = request a
180 EntryContents request (Single a) = request a
223 EntryContents request Multi = IntMap (RequesterData request)
181 EntryContents request Multi = IntMap (RequesterData request)
224− EntryContents request (Multi2 k) = Multi2Contents k request
182+ EntryContents request (Multi2 k) = Map (Some k) (IntMap (RequesterData request))
225 EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
183 EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
226
184
227−data Multi2Contents k request = Multi2Contents
228− { _multi2Contents_dict :: {-# UNPACK #-} !(Dict (GCompare k)) -- This is a Dict instead of an existential context because we only want to use it in certain circumstances
229− , _multi2Contents_values :: {-# UNPACK #-} !(Map (Some k) (IntMap (RequesterData request)))
230− }
231−
232newtype Entry request x = Entry { unEntry :: EntryContents request x }
185newtype Entry request x = Entry { unEntry :: EntryContents request x }
233
186
234{-# INLINE singleEntry #-}
187{-# INLINE singleEntry #-}
@@ -357,7 +310,7 @@ tagRequest req = do
357 return t
310 return t
358
311
359{-# INLINE responseFromTag #-}
312{-# INLINE responseFromTag #-}
360−responseFromTag :: forall m t request response x. Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
313+responseFromTag :: Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
361responseFromTag (MyTagWrap t) = do
314responseFromTag (MyTagWrap t) = do
362 responses :: EventSelectorInt t Any <- RequesterT ask
315 responses :: EventSelectorInt t Any <- RequesterT ask
363 return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
316 return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
@@ -487,9 +440,9 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
487 let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
440 let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
488 responses = fanMap $ fmapCheap unpack response
441 responses = fanMap $ fmapCheap unpack response
489 unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
442 unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
490− unpack = _multi2Contents_values . unEntry
443+ unpack = unEntry
491 pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
444 pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
492− pack m = Entry $ Multi2Contents { _multi2Contents_values = m, _multi2Contents_dict = Dict }
445+ pack = Entry
493 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
446 f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
494 f' k (Compose (n, v)) = do
447 f' k (Compose (n, v)) = do
495 (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
448 (result, myRequests) <- runRequesterT (f k v) $ mapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some k))
@@ -510,30 +463,10 @@ traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchN
510data Decoder rawResponse response =
463data Decoder rawResponse response =
511 forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
464 forall a. Decoder (RequesterDataKey a) (rawResponse -> response a)
512
465
513−matchResponsesWithRequests
514− :: forall t rawRequest rawResponse request response m.
515− ( MonadFix m
516− , MonadHold t m
517− , Reflex t
518− )
519− => (forall a. request a -> (rawRequest, rawResponse -> response a))
520− -- ^ Given a request (from 'Requester'), produces the wire format of the
521− -- request and a function used to process the associated response
522− -> Event t (RequesterData request)
523− -- ^ The outgoing requests
524− -> Event t (Int, rawResponse)
525− -- ^ The incoming responses, tagged by an identifying key
526− -> m ( Event t (Map Int rawRequest)
527− , Event t (RequesterData response)
528− )
529− -- ^ A map of outgoing wire-format requests and an event of responses keyed
530− -- by the 'RequesterData' key of the associated outgoing request
531−matchResponsesWithRequests f send recv = matchResponseMapWithRequests f send $ uncurry Map.singleton <$> recv
532−
533-- | Matches incoming responses with previously-sent requests
466-- | Matches incoming responses with previously-sent requests
534-- and uses the provided request "decoder" function to process
467-- and uses the provided request "decoder" function to process
535-- incoming responses.
468-- incoming responses.
536−matchResponseMapWithRequests
469+matchResponsesWithRequests
537 :: forall t rawRequest rawResponse request response m.
470 :: forall t rawRequest rawResponse request response m.
538 ( MonadFix m
471 ( MonadFix m
539 , MonadHold t m
472 , MonadHold t m
@@ -544,14 +477,14 @@ matchResponseMapWithRequests
544 -- request and a function used to process the associated response
477 -- request and a function used to process the associated response
545 -> Event t (RequesterData request)
478 -> Event t (RequesterData request)
546 -- ^ The outgoing requests
479 -- ^ The outgoing requests
547− -> Event t (Map Int rawResponse)
480+ -> Event t (Int, rawResponse)
548− -- ^ A map of incoming responses, tagged by an identifying key
481+ -- ^ The incoming responses, tagged by an identifying key
549 -> m ( Event t (Map Int rawRequest)
482 -> m ( Event t (Map Int rawRequest)
550 , Event t (RequesterData response)
483 , Event t (RequesterData response)
551 )
484 )
552 -- ^ A map of outgoing wire-format requests and an event of responses keyed
485 -- ^ A map of outgoing wire-format requests and an event of responses keyed
553 -- by the 'RequesterData' key of the associated outgoing request
486 -- by the 'RequesterData' key of the associated outgoing request
554−matchResponseMapWithRequests f send recv = do
487+matchResponsesWithRequests f send recv = do
555 rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
488 rec nextId <- hold 1 $ fmap (\(next, _, _) -> next) outgoing
556 waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
489 waitingFor :: Incremental t (PatchMap Int (Decoder rawResponse response)) <-
557 holdIncremental mempty $ leftmost
490 holdIncremental mempty $ leftmost
@@ -593,15 +526,17 @@ matchResponseMapWithRequests f send recv = do
593 processIncoming
526 processIncoming
594 :: Incremental t (PatchMap Int (Decoder rawResponse response))
527 :: Incremental t (PatchMap Int (Decoder rawResponse response))
595 -- A map of outstanding expected responses
528 -- A map of outstanding expected responses
596− -> Event t (Map Int rawResponse)
529+ -> Event t (Int, rawResponse)
597 -- A incoming response paired with its identifying key
530 -- A incoming response paired with its identifying key
598 -> Event t (RequesterData response, PatchMap Int v)
531 -> Event t (RequesterData response, PatchMap Int v)
599 -- The decoded response and a patch that clears the outstanding responses queue
532 -- The decoded response and a patch that clears the outstanding responses queue
600− processIncoming waitingFor inc = flip push inc $ \rspMap -> do
533+ processIncoming waitingFor inc = flip push inc $ \(n, rawRsp) -> do
601 wf <- sample $ currentIncremental waitingFor
534 wf <- sample $ currentIncremental waitingFor
602− let match rawRsp (Decoder k rspF) =
535+ case Map.lookup n wf of
603− let rsp = rspF rawRsp
536+ Nothing -> return Nothing -- TODO How should lookup failures be handled here? They shouldn't ever happen..
604− in singletonRequesterData k rsp
537+ Just (Decoder k rspF) -> do
605− matches = Map.intersectionWith match rspMap wf
538+ let rsp = rspF rawRsp
606− pure $ if Map.null matches then Nothing else Just
539+ return $ Just
607− (Map.foldl' mergeRequesterData emptyRequesterData matches, PatchMap $ Nothing <$ matches)
540+ ( singletonRequesterData k rsp
541+ , PatchMap $ Map.singleton n Nothing
542+ )
src/Reflex/Spider/Internal.hs+1−1
@@ -2775,7 +2775,7 @@ instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2775 fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2775 fanG e = R.EventSelectorG $ SpiderEvent . selectG (fanG (unSpiderEvent e))
2776 {-# INLINABLE mergeG #-}
2776 {-# INLINABLE mergeG #-}
2777 mergeG
2777 mergeG
2778− :: forall k2 (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2778+ :: forall (k :: k2 -> *) q (v :: k2 -> *). GCompare k
2779 => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2779 => (forall a. q a -> R.Event (SpiderTimeline x) (v a))
2780 -> DMap k q
2780 -> DMap k q
2781 -> R.Event (SpiderTimeline x) (DMap k v)
2781 -> R.Event (SpiderTimeline x) (DMap k v)
src/Reflex/Workflow.hs+122−15
@@ -1,3 +1,6 @@
1+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2+{-# LANGUAGE LambdaCase #-}
3+{-# LANGUAGE RankNTypes #-}
1{-# LANGUAGE RecursiveDo #-}
4{-# LANGUAGE RecursiveDo #-}
2{-# LANGUAGE ScopedTypeVariables #-}
5{-# LANGUAGE ScopedTypeVariables #-}
3-- |
6-- |
@@ -13,33 +16,55 @@ module Reflex.Workflow (
13 , workflowView
16 , workflowView
14 , mapWorkflow
17 , mapWorkflow
15 , mapWorkflowCheap
18 , mapWorkflowCheap
19+ , runWorkflow
20+
21+ -- Combinators
22+ , step
23+ , stop
24+ , label
25+ , label_
26+
27+ -- Runners
28+ , stack
29+ , wizard
30+
16 ) where
31 ) where
17
32
18import Control.Arrow ((***))
33import Control.Arrow ((***))
19−import Control.Monad.Fix (MonadFix)
34+import Control.Monad ((<=<))
20−
35+import Control.Monad.Cont (MonadCont, callCC)
36+import Control.Monad.Fix (MonadFix, fix)
37+import Control.Monad.Free.Church
38+import Data.Bifunctor (bimap)
39+import Data.Functor.Compose (Compose(..))
21import Reflex.Class
40import Reflex.Class
22import Reflex.Adjustable.Class
41import Reflex.Adjustable.Class
23−import Reflex.Network
24−import Reflex.NotReady.Class
25import Reflex.PostBuild.Class
42import Reflex.PostBuild.Class
26
43
44+--------------------------------------------------------------------------------
45+-- Workflow
46+--------------------------------------------------------------------------------
27-- | A widget in a workflow
47-- | A widget in a workflow
28-- When the 'Event' returned by a 'Workflow' fires, the current 'Workflow' is replaced by the one inside the firing 'Event'. A series of 'Workflow's must share the same return type.
48-- When the 'Event' returned by a 'Workflow' fires, the current 'Workflow' is replaced by the one inside the firing 'Event'. A series of 'Workflow's must share the same return type.
29newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
49newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
30
50
31−-- | Runs a 'Workflow' and returns the 'Dynamic' result of the 'Workflow' (i.e., a 'Dynamic' of the value produced by the current 'Workflow' node, and whose update 'Event' fires whenever one 'Workflow' is replaced by another).
51+-- | Runs a 'Workflow' and returns the initial value together with an 'Event' that fires whenever one 'Workflow' is replaced by another.
32−workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
52+runWorkflow :: (Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (a, Event t a)
33−workflow w0 = do
53+runWorkflow w0 = mdo
34− rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
54+ ((a, e0), eResult) <- runWithReplace (unWorkflow w0) (fmap unWorkflow eReplace)
55+ eReplace <- switchHold e0 $ fmap snd eResult
35− return $ fmap fst eResult
56+ return (a, fmap fst eResult)
57+
58+-- | Similar to 'runWorkflow' but combines the result into a 'Dynamic'.
59+workflow :: (Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
60+workflow = uncurry holdDyn <=< runWorkflow
36
61
37−-- | Similar to 'workflow', but outputs an 'Event' that fires at post-build time and whenever the current 'Workflow' is replaced by the next 'Workflow'.
62+-- | Similar to 'runWorkflow', but also puts the initial value in the 'Event'.
38−workflowView :: forall t m a. (Reflex t, NotReady t m, Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
63+workflowView :: (Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
39−workflowView w0 = do
64+workflowView w = do
40− rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
65+ postBuildEv <- getPostBuild
41− eReplace <- fmap switch $ hold never $ fmap snd eResult
66+ (initialValue, replaceEv) <- runWorkflow w
42− return $ fmap fst eResult
67+ pure $ leftmost [initialValue <$ postBuildEv, replaceEv]
43
68
44-- | Map a function over a 'Workflow', possibly changing the return type.
69-- | Map a function over a 'Workflow', possibly changing the return type.
45mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
70mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
@@ -48,3 +73,85 @@ mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
48-- | Map a "cheap" function over a 'Workflow'. Refer to the documentation for 'pushCheap' for more information and performance considerations.
73-- | Map a "cheap" function over a 'Workflow'. Refer to the documentation for 'pushCheap' for more information and performance considerations.
49mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
74mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
50mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
75mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
76+
77+--------------------------------------------------------------------------------
78+-- Internal utils
79+--------------------------------------------------------------------------------
80+nowOrLater :: PostBuild t m => Either (Event t a) a -> m (Event t a)
81+nowOrLater = \case
82+ Left l -> pure l
83+ Right n -> (n <$) <$> getPostBuild
84+
85+lateOrLater :: (MonadHold t m, Reflex t) => Event t (Either (Event t a) a) -> m (Event t a)
86+lateOrLater ev = mdo
87+ let (ltrEv, lt) = fanEither ev
88+ ltr <- switchHold never ltrEv
89+ pure $ leftmost [lt, ltr]
90+
91+--------------------------------------------------------------------------------
92+-- Replacements layer
93+--------------------------------------------------------------------------------
94+newtype Step t m a = Step { unStep :: m (Either (Event t a) a) }
95+instance (Reflex t, Functor m) => Functor (Step t m) where
96+ fmap f = Step . fmap (bimap (fmap f) f) . unStep
97+
98+runStep :: PostBuild t m => Step t m a -> m (Event t a)
99+runStep = nowOrLater <=< unStep
100+
101+newtype Machine t m a = Machine { unMachine :: F (Compose m (Event t)) a } deriving (Functor, Applicative, Monad)
102+
103+bottomUp
104+ :: forall t m a. PostBuild t m
105+ => (forall x. Step t m (Step t m x) -> Step t m x)
106+ -> Machine t m a -> m (Event t a)
107+bottomUp f mm = runStep $ runF root leaf branch
108+ where
109+ root :: F (Compose m (Event t)) a
110+ root = unMachine mm
111+
112+ leaf :: a -> Step t m a
113+ leaf = Step . pure . Right
114+
115+ branch :: Compose m (Event t) (Step t m a) -> Step t m a
116+ branch = f . Step . fmap Left . getCompose
117+
118+--------------------------------------------------------------------------------
119+-- Combinators
120+--------------------------------------------------------------------------------
121+-- | Machine with a single step
122+step :: (Functor m, Reflex t) => m (Event t a) -> Machine t m a
123+step = Machine . wrap . fmap pure . Compose
124+
125+-- | Machine with a single step and no transitions
126+stop :: (Reflex t, Applicative m) => Machine t m a
127+stop = step $ pure never
128+
129+-- TODO: Use upstream when https://github.com/haskell/mtl/pull/87 is merged
130+{- | Introduces a recursive binding to the continuation.
131+Due to the use of @callCC@, calling the continuation will interrupt execution
132+of the current block creating an effect similar to goto/setjmp in C.
133+-}
134+label :: MonadCont m => a -> m (a -> m b, a)
135+label a = callCC $ \k -> let go b = k (go, b) in return (go, a)
136+
137+{- | Simplified version of `label` without arguments -}
138+label_ :: MonadCont m => m (m a)
139+label_ = callCC $ return . fix
140+
141+--------------------------------------------------------------------------------
142+-- Runners
143+--------------------------------------------------------------------------------
144+-- | A wizard only has a single step active at any given point, and any new step replaces its predecessor
145+wizard :: forall t m a. (Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Machine t m a -> m (Event t a)
146+wizard = bottomUp $ \m -> Step $ mdo
147+ (nl, ll) <- runWithReplace (unStep m) (unStep <$> replacement)
148+ replacement <- nowOrLater nl
149+ Left <$> lateOrLater ll
150+
151+-- | A stack can have all steps active at a time, and the first one is always active.
152+-- When a step triggers, it replaces the (possibly empty) pile on top of itself with a single new step
153+stack :: forall t m a. (Adjustable t m, MonadHold t m, PostBuild t m) => Machine t m a -> m (Event t a)
154+stack = bottomUp $ \m -> Step $ do
155+ replacement <- runStep m
156+ ((), ll) <- runWithReplace (pure ()) (unStep <$> replacement)
157+ Left <$> lateOrLater ll