on develop help sign in
50 files changed+2500−5742
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+47 .gitignore+7 .stylish-haskell.yaml+153 .travis.yml−24 CONTRIBUTING.md+66 ChangeLog.md+61 Quickref.md−179 README.md+44−11 bench-cbits/checkCapability.c+11−12 bench/Main.hs−109 bench/RunAll.hs−143 cabal.project+1 default.nix−24 dep/reflex-platform/default.nix+8 dep/reflex-platform/github.json+8 patch.cabal+100 reflex.cabal−120 release.nix+49 src/Data/Functor/Misc.hs+194−52 src/Data/Monoid/DecidablyEmpty.hs+115 src/Data/Patch.hs+95 src/Data/Patch/Class.hs+45 src/Data/Patch/DMap.hs+93 src/Data/Patch/DMapWithMove.hs+371 src/Data/Patch/IntMap.hs+76 src/Data/Patch/Map.hs+72 src/Data/Patch/MapWithMove.hs+312 src/Data/Patch/MapWithPatchingMove.hs+432 src/Data/Semigroup/Additive.hs+54 src/Data/WeakBag.hs−82 src/Reflex.hs−12 src/Reflex/Class.hs−560 src/Reflex/Dynamic.hs−443 src/Reflex/Dynamic/Nub.hs−60 src/Reflex/Dynamic/TH.hs−67 src/Reflex/Host/Class.hs−227 src/Reflex/Spider.hs−8 src/Reflex/Spider/Internal.hs−2267 stylize+4 test/Reflex/Bench/Focused.hs−361 test/Reflex/Plan/Pure.hs−64 test/Reflex/Plan/Reflex.hs−126 test/Reflex/Pure.hs−155 test/Reflex/Test.hs−61 test/Reflex/Test/CrossImpl.hs−228 test/Reflex/Test/Micro.hs−258 test/Reflex/Test/RunAll.hs−44 test/Reflex/TestPlan.hs−45 test/hlint.hs+39 test/tests.hs+43
.github/workflows/haskell.ymlnew file+47
@@ -0,0 +1,47 @@
1+name: github-action
2+
3+on: [push, pull_request]
4+
5+jobs:
6+ build:
7+ strategy:
8+ matrix:
9+ ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2']
10+ os: ['ubuntu-latest', 'macos-latest']
11+ exclude:
12+ # There are some linker warnings in 802 on darwin that
13+ # cause compilation to fail
14+ # See https://github.com/NixOS/nixpkgs/issues/25139
15+ - ghc: '8.0.2'
16+ os: 'macos-latest'
17+ runs-on: ${{ matrix.os }}
18+
19+ name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
20+ steps:
21+ - uses: actions/checkout@v2
22+ - uses: actions/setup-haskell@v1
23+ with:
24+ ghc-version: ${{ matrix.ghc }}
25+ - name: Cache
26+ uses: actions/cache@v1
27+ env:
28+ cache-name: cache-cabal
29+ with:
30+ path: ~/.cabal
31+ key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
32+ restore-keys: |
33+ ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
34+ ${{ runner.os }}-${{ matrix.ghc }}-build-
35+ ${{ runner.os }}-${{ matrix.ghc }}-
36+ ${{ runner.os }}
37+
38+ - name: Install dependencies
39+ run: |
40+ cabal update
41+ cabal build --only-dependencies --enable-tests --enable-benchmarks
42+ - name: Build
43+ run: cabal build --enable-tests --enable-benchmarks all
44+ - name: Run tests
45+ run: cabal test --enable-tests all
46+ - name: Build Docs
47+ run: cabal haddock
.gitignore+7
@@ -1,4 +1,7 @@
11dist
2+cabal.sandbox.config
3+.cabal-sandbox/
4+dist-*
25cabal-dev
36*.o
47*.hi
@@ -29,3 +32,7 @@ tags
2932hsenv.log
3033\#*#
3134.#*
35+/shell.nix
36+/ghci-tmp
37+*.dump-*
38+*.verbose-core2core
.stylish-haskell.yamlnew file+153
@@ -0,0 +1,153 @@
1+# stylish-haskell configuration file
2+# ==================================
3+
4+# The stylish-haskell tool is mainly configured by specifying steps. These steps
5+# are a list, so they have an order, and one specific step may appear more than
6+# once (if needed). Each file is processed by these steps in the given order.
7+steps:
8+ # Convert some ASCII sequences to their Unicode equivalents. This is disabled
9+ # by default.
10+ # - unicode_syntax:
11+ # # In order to make this work, we also need to insert the UnicodeSyntax
12+ # # language pragma. If this flag is set to true, we insert it when it's
13+ # # not already present. You may want to disable it if you configure
14+ # # language extensions using some other method than pragmas. Default:
15+ # # true.
16+ # add_language_pragma: true
17+
18+ # Import cleanup
19+ - imports:
20+ # There are different ways we can align names and lists.
21+ #
22+ # - global: Align the import names and import list throughout the entire
23+ # file.
24+ #
25+ # - file: Like global, but don't add padding when there are no qualified
26+ # imports in the file.
27+ #
28+ # - group: Only align the imports per group (a group is formed by adjacent
29+ # import lines).
30+ #
31+ # - none: Do not perform any alignment.
32+ #
33+ # Default: global.
34+ align: none
35+
36+ # Folowing options affect only import list alignment.
37+ #
38+ # List align has following options:
39+ #
40+ # - after_alias: Import list is aligned with end of import including
41+ # 'as' and 'hiding' keywords.
42+ #
43+ # > import qualified Data.List as List (concat, foldl, foldr, head,
44+ # > init, last, length)
45+ #
46+ # - with_alias: Import list is aligned with start of alias or hiding.
47+ #
48+ # > import qualified Data.List as List (concat, foldl, foldr, head,
49+ # > init, last, length)
50+ #
51+ # - new_line: Import list starts always on new line.
52+ #
53+ # > import qualified Data.List as List
54+ # > (concat, foldl, foldr, head, init, last, length)
55+ #
56+ # Default: after alias
57+ list_align: after_alias
58+
59+ # Long list align style takes effect when import is too long. This is
60+ # determined by 'columns' setting.
61+ #
62+ # - inline: This option will put as much specs on same line as possible.
63+ #
64+ # - new_line: Import list will start on new line.
65+ #
66+ # - new_line_multiline: Import list will start on new line when it's
67+ # short enough to fit to single line. Otherwise it'll be multiline.
68+ #
69+ # - multiline: One line per import list entry.
70+ # Type with contructor list acts like single import.
71+ #
72+ # > import qualified Data.Map as M
73+ # > ( empty
74+ # > , singleton
75+ # > , ...
76+ # > , delete
77+ # > )
78+ #
79+ # Default: inline
80+ long_list_align: inline
81+
82+ # List padding determines indentation of import list on lines after import.
83+ # This option affects 'list_align' and 'long_list_align'.
84+ list_padding: 2
85+
86+ # Separate lists option affects formating of import list for type
87+ # or class. The only difference is single space between type and list
88+ # of constructors, selectors and class functions.
89+ #
90+ # - true: There is single space between Foldable type and list of it's
91+ # functions.
92+ #
93+ # > import Data.Foldable (Foldable (fold, foldl, foldMap))
94+ #
95+ # - false: There is no space between Foldable type and list of it's
96+ # functions.
97+ #
98+ # > import Data.Foldable (Foldable(fold, foldl, foldMap))
99+ #
100+ # Default: true
101+ separate_lists: true
102+
103+ # Language pragmas
104+ - language_pragmas:
105+ # We can generate different styles of language pragma lists.
106+ #
107+ # - vertical: Vertical-spaced language pragmas, one per line.
108+ #
109+ # - compact: A more compact style.
110+ #
111+ # - compact_line: Similar to compact, but wrap each line with
112+ # `{-#LANGUAGE #-}'.
113+ #
114+ # Default: vertical.
115+ style: vertical
116+
117+ # Align affects alignment of closing pragma brackets.
118+ #
119+ # - true: Brackets are aligned in same collumn.
120+ #
121+ # - false: Brackets are not aligned together. There is only one space
122+ # between actual import and closing bracket.
123+ #
124+ # Default: true
125+ align: false
126+
127+ # stylish-haskell can detect redundancy of some language pragmas. If this
128+ # is set to true, it will remove those redundant pragmas. Default: true.
129+ remove_redundant: true
130+
131+ # Align the types in record declarations
132+ # - records: {}
133+
134+ # Replace tabs by spaces. This is disabled by default.
135+ # - tabs:
136+ # # Number of spaces to use for each tab. Default: 8, as specified by the
137+ # # Haskell report.
138+ # spaces: 8
139+
140+ # Remove trailing whitespace
141+ - trailing_whitespace: {}
142+
143+# A common setting is the number of columns (parts of) code will be wrapped
144+# to. Different steps take this into account. Default: 80.
145+columns: 110
146+
147+# Sometimes, language extensions are specified in a cabal file or from the
148+# command line instead of using language pragmas in the file. stylish-haskell
149+# needs to be aware of these, so it can parse the file correctly.
150+#
151+# No language extensions are enabled by default.
152+language_extensions:
153+ - TemplateHaskell
.travis.ymldeleted−24
@@ -1,24 +0,0 @@
1−sudo: false
2−before_install:
3− - travis_retry cabal-$CABALVER update
4− - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:$PATH
5−install:
6− - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
7−script:
8− - cabal-$CABALVER configure -v2 --enable-tests
9− - cabal-$CABALVER build
10− - cabal-$CABALVER test
11− - cabal-$CABALVER sdist # tests that a source-distribution can be generated
12−# Check that the resulting source distribution can be built & installed.
13−# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
14−# `cabal install --force-reinstalls dist/*-*.tar.gz`
15− - SRC_TGZ=$(cabal-$CABALVER info . | awk '{print $2;exit}').tar.gz &&
16− (cd dist && cabal-$CABALVER install --force-reinstalls "$SRC_TGZ")
17−
18−matrix:
19− include:
20− - env: CABALVER=1.18 GHCVER=7.8.4
21− addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
22− - env: CABALVER=1.22 GHCVER=7.10.1
23− addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24− fast_finish: true
CONTRIBUTING.mdnew file+66
@@ -0,0 +1,66 @@
1+# Contribution Guide
2+
3+Contributions and issue reports are encouraged and appreciated!
4+
5+- [Opening Issues](#opening-issues)
6+- [Submitting Changes](#submitting-changes)
7+ - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
9+ - [Code Quality](#code-quality)
10+ - [Documentation](#documentation)
11+
12+## Opening Issues
13+
14+Before opening an issue, please check whether your issue has already been reported. Assuming it has not:
15+
16+* Describe the issue you're encountering or the suggestion you're making
17+* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
18+* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
19+* Describe how you're building patch (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
20+
21+## Submitting Changes
22+
23+### Guidelines for Commit Messages
24+
25+#### Summary Line
26+The summary line of your commit message should summarize the changes being made. Commit messages should be written in the imperative mood and should describe what happens when the commit is applied.
27+
28+One way to think about it is that your commit message should be able to complete the sentence:
29+"When applied, this commit will..."
30+
31+#### Body
32+For 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.
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+
38+### Code Quality
39+
40+#### Warnings
41+
42+Your pull request should add no new warnings to the project. It should also generally not disable any warnings.
43+
44+#### Build and Test
45+
46+Make 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!
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 patch.cabal also need to be added to default.nix.
51+
52+### Documentation
53+
54+#### In the code
55+We're always striving to improve documentation. Please include [haddock](https://haskell-haddock.readthedocs.io/en/latest/index.html) documentation for any added code, and update the documentation for any code you modify.
56+
57+#### In the [Changelog](ChangeLog.md)
58+Add an entry to the changelog when your PR:
59+* Adds a feature
60+* Deprecates something
61+* Includes a breaking change
62+* Makes any other change that will impact users
63+
64+#### In the [Readme](README.md)
65+The readme is the first place a lot of people look for information about the repository. Update any parts of the readme that are affected by your PR.
66+
ChangeLog.mdnew file+61
@@ -0,0 +1,61 @@
1+# Revision history for patch
2+
3+## Unreleased
4+
5+* New dep of `data-orphans` for old GHC to get instances honestly instead of
6+ via `monoidal-containers`.
7+
8+## 0.0.5.0 - 2021-12-17
9+
10+* `Additive` now lives in `Data.Semigroup.Additive`, but is still reexported
11+ from `Data.Patch` for compatability.
12+
13+* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
14+ Care is taken to make this not a breaking change.
15+ In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
16+ There are complete constructor and field patterns too, and everything is
17+ exported under the newtype as real constructors and fields would be.
18+
19+## 0.0.4.0 - 2021-04-20
20+
21+* Enable PolyKinds
22+
23+## 0.0.3.2 - 2020-11-06
24+
25+* Update version bounds
26+
27+## 0.0.3.1 - 2020-02-05
28+
29+* Replace `fromJust` with something easier to debug.
30+
31+## 0.0.3.0 - 2020-02-05
32+
33+* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
34+
35+* Create `DecidablyEmpty` subclass of `Monoid`.
36+
37+## 0.0.2.0 - 2020-01-17
38+
39+* Consistently provide:
40+
41+ - `Wrapped` instances
42+
43+ - `*WithIndex` instances
44+
45+ - `un*` newtype unwrappers
46+
47+ for `PatchMap`, `PatchIntMap`, and `PatchMapWithMove`.
48+
49+## 0.0.1.0 - 2020-01-09
50+
51+* Support older GHCs with `split-these` flag.
52+
53+* Additional instances for the `Group` class for basic types.
54+
55+## 0.0.0.1 - 2020-01-08
56+
57+* Remove unneeded dependencies
58+
59+## 0.0.0.0 - 2020-01-08
60+
61+* Extract patching functionality from Reflex.
Quickref.mddeleted−179
@@ -1,179 +0,0 @@
1−# Reflex Quick(ish) Reference
2−
3−## Typeclasses
4−
5−Many Reflex functions operate in monadic context `m a`, where the monad 'm' supports various additional typeclasses such as MonadWidget, MonadHold, or MonadSample in addition to Monad itself. The actual 'm' in use will be determined by the top-level entry point of the FRP host (such as Reflex-Dom -- see the bottom of the Reflex-Dom quick reference for details).
6−
7−The function signatures here have been simplified by removing many typeclass constraints and adding simple annotations to each function. Also the ubiquitous 't' type parameter has been removed.
8−
9−Some of these functions are *pure*: They operate on Events, Behaviors, or Dynamics uniformly without regard to the "current time". Other functions must operate in some monadic context, because they produce a result "as of now" (e.g., any function that takes an "initial" value, or returns a "current" value). Annotations are used to distinguish these cases:
10−
11−```haskell
12−[ ] -- Pure function
13−[S] -- Function runs in any monad supporting MonadSample
14−[H] -- Function runs in any monad supporting MonadHold
15−```
16−
17−Since MonadHold depends on MonadSample, any [S] function also runs in [H] context.
18−
19−## Functions producing Event
20−
21−```haskell
22−-- Trivial Event
23−[ ] never :: Event a
24−
25−-- Extract Event from Dynamic
26−[ ] updated :: Dynamic a -> Event a
27−
28−-- Transform Event to Event using function
29−[ ] fmap :: (a -> b) -> Event a -> Event b
30−[ ] fmapMaybe :: (a -> Maybe b) -> Event a -> Event b
31−[ ] ffilter :: (a -> Bool) -> Event a -> Event a
32−[ ] ffor :: Event a -> (a -> b) -> Event b
33−[ ] fforMaybe :: Event a -> (a -> Maybe b) -> Event b
34−
35−-- Event to identical Event with debug trace. (Only prints if Event is ultimately used.)
36−[ ] traceEvent :: Show a => String -> Event a -> Event a
37−[ ] traceEventWith :: (a -> String) -> Event a -> Event a
38−
39−-- Transform Event to Event by sampling Behavior or Dynamic
40−[ ] gate :: Behavior Bool -> Event a -> Event a
41−[ ] tag :: Behavior a -> Event b -> Event a
42−[ ] tagDyn :: Dynamic a -> Event b -> Event a
43−[ ] attach :: Behavior a -> Event b -> Event (a, b)
44−[ ] attachDyn :: Dynamic a -> Event b -> Event (a, b)
45−[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46−[ ] attachDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47−[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48−[ ] attachDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49−
50−-- Combine multiple Events
51−[ ] <> :: Monoid a => Event a -> Event a -> Event a
52−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
53−[ ] leftmost :: [Event a] -> Event a
54−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
55−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
56−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
57−
58−-- Efficient one-to-many fanout
59−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
60−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
61−[ ] select :: EventSelector k -> k a -> Event a
62−
63−-- Event to Event via function that can sample current values
64−[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
65−[ ] pushAlways :: (a -> m b ) -> Event a -> Event b
66− -- Note supplied function operates in [H] context
67−
68−-- Split Event into next occurrence and all other future occurrences, as of now.
69−[H] headE :: Event a -> m (Event a)
70−[H] tailE :: Event a -> m (Event a)
71−[H] headTailE :: Event a -> m (Event a, Event a)
72−```
73−
74−## Functions producing Behavior
75−
76−```haskell
77−-- Trivial Behavior
78−[ ] constant :: a -> Behavior a
79−
80−-- Extract Behavior from Dynamic
81−[ ] current :: Dynamic a -> Behavior a
82−
83−-- Create Behavior with given initial value, updated when the Event fires.
84−[H] hold :: a -> Event a -> m (Behavior a)
85−
86−-- Transform Behavior to Behavior using function
87−[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
88−[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
89−[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
90−[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
91−[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
92−-- ... plus many more due to typeclass membership
93−
94−-- Behavior to Behavior by sampling current values
95−[S] sample :: Behavior a -> m a
96−[ ] pull :: m a -> Behavior a
97− -- Note supplied value is in [S] context
98−```
99−
100−## Functions producing Dynamic
101−
102−```haskell
103−-- Trivial Dynamic
104−[ ] constDyn :: a -> Dynamic a
105−
106−-- Create Dynamic with given initial value, updated (various ways) when the Event fires.
107−[H] holdDyn :: a -> Event a -> m (Dynamic a)
108−[H] foldDyn :: (a -> b -> b ) -> b -> Event a -> m (Dynamic b)
109−[H] foldDynMaybe :: (a -> b -> Maybe b ) -> b -> Event a -> m (Dynamic b)
110−[H] foldDynM :: (a -> b -> m' b ) -> b -> Event a -> m (Dynamic b)
111−[H] foldDynMaybeM :: (a -> b -> m' (Maybe b)) -> b -> Event a -> m (Dynamic b)
112− -- Note m' supplies [H] context
113−
114−-- Initial value is 0; counts Event firings from now.
115−[H] count :: Num b => Event a -> m (Dynamic b)
116−-- Initial Bool value is supplied; toggles at each Event firing.
117−[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
118−
119−-- Transform Dynamic to Dynamic using function
120−[H] forDyn :: Dynamic a -> (a -> b) -> m (Dynamic b)
121−[H] mapDyn :: (a -> b) -> Dynamic a -> m (Dynamic b)
122−[H] mapDynM :: (a -> m' b) -> Dynamic a -> m (Dynamic b)
123− -- Note m' supplies [S] context
124−[H] splitDyn :: Dynamic (a, b) -> m (Dynamic a, Dynamic b)
125−
126−-- Combine multiple Dynamics
127−[H] mconcatDyn :: Monoid a => [Dynamic a] -> m (Dynamic a)
128−[H] distributeDMapOverDyn :: GCompare k => DMap (WrapArg Dynamic k) -> m (Dynamic (DMap k))
129−[H] combineDyn :: (a -> b -> c) -> Dynamic a -> Dynamic b -> m (Dynamic c)
130−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
131−
132−-- Efficient one-to-many fanout
133−[ ] demux :: Ord k => Dynamic k -> Demux k
134−[H] getDemuxed :: Eq k => Demux k -> k -> m (Dynamic Bool)
135−
136−-- Dynamic to Dynamic, removing updates w/o value change
137−[ ] nubDyn :: Eq a => Dynamic a -> Dynamic a
138−
139−-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
140−[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
141−[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
142−```
143−
144−## Flattening functions
145−
146−These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
147−
148−For Events, the returned Event fires whenever the latest Event supplied by the wrapper fires. There are differences in how the functions handle *coincidences* -- situations where the old or new Event fires at the same instant that we switch from old to new. In some cases, the output Event tracks the old Event at the instant of switchover, while in other cases it tracks the new Event.
149−
150−```haskell
151−-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
152−[ ] switch :: Behavior (Event a) -> Event a
153−
154−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
155−[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
156−
157−-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
158−[ ] coincidence :: Event (Event a) -> Event a
159−
160−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
161−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
162−-- a Behavior whose value is equal to the current value of the current value of
163−-- the input.
164−[ ] join :: Behavior (Behavior a) -> Behavior a
165−
166−
167−-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
168−-- Output updated whenever inner OR outer Dynamic updates.
169−[ ] joinDyn :: Dynamic (Dynamic a) -> Dynamic a
170−[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
171−
172−-- Analogous to 'hold': Create a Behavior that is initially identical to the
173−-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
174−[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
175−
176−-- Similar to above, for Events. Created Event initially tracks the first argument.
177−-- At switchover, the output Event immediately tracks the new Event.
178−[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
179−```
README.md+44−11
@@ -1,19 +1,52 @@
1−## Reflex
2−### Practical Functional Reactive Programming
1+# patch
32
4−Reflex is an fully-deterministic, higher-order Functional Reactive Programming (FRP) interface and an engine that efficiently implements that interface.
3+[![Haskell](https://img.shields.io/badge/language-Haskell-orange.svg)](https://haskell.org) [![Hackage](https://img.shields.io/hackage/v/patch.svg)](https://hackage.haskell.org/package/patch) [![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/patch/badge)](https://matrix.hackage.haskell.org/#/package/patch) [![Travis CI](https://api.travis-ci.org/reflex-frp/patch.svg?branch=develop)](https://travis-ci.org/reflex-frp/patch) [![BSD3 License](https://img.shields.io/badge/license-BSD3-blue.svg)](https://github.com/reflex-frp/patch/LICENSE)
54
6−[Reflex-DOM](https://github.com/reflex-frp/reflex-dom) is a framework built on Reflex that facilitates the development of web pages, including highly-interactive single-page apps.
5+Data structures for describing changes to other data structures.
76
8−Comprehensive documentation is still a work in progress, but a tutorial for Reflex and Reflex-DOM is available at https://github.com/reflex-frp/reflex-platform and an introductory talk given at the New York Haskell Meetup is available here: [Part 1](https://www.youtube.com/watch?v=mYvkcskJbc4) / [Part 2](https://www.youtube.com/watch?v=3qfc9XFVo2c).
7+A `Patch` type represents a kind of change made to a data structure.
98
10−A summary of Reflex functions is available in the [quick reference](Quickref.md).
9+```haskell
10+class Patch p where
11+ type PatchTarget p :: *
12+ -- | Apply the patch p a to the value a. If no change is needed, return
13+ -- 'Nothing'.
14+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
15+```
1116
12−### Additional resources
13−[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
17+## Patching Maps
18+For example, `Data.Patch.Map` defines the `PatchMap` type which can be used to patch `Map`s. A `PatchMap` represents updates to a `Map` that can insert, remove, or replace items in the `Map`. In this example, the `Map` is the `PatchTarget` and the `PatchMap` is the `Patch`. Keep in mind that there are many other possible `Patch`es that can be applied to a `Map` (i.e., `Map` can be the `PatchTarget` for many different `Patch` instances).
1419
15−[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
20+`PatchMap` is defined as:
1621
17−[hackage](https://hackage.haskell.org/package/reflex)
22+```haskell
23+newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
24+```
1825
19−[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
26+The `Maybe` around the value is used to represent insertion/updates or deletion of the element at a given key.
27+
28+Its `Patch` instance begins with:
29+
30+```haskell
31+instance Ord k => Patch (PatchMap k v) where
32+ type PatchTarget (PatchMap k v) = Map k v
33+ ...
34+```
35+
36+When a `PatchMap` is applied to its `PatchTarget`, the following changes can occur:
37+
38+- If the key is present in the `Patch` and the `PatchTarget`...
39+
40+ - And the `Patch` value at that key is `Nothing`: delete that key from the `PatchTarget`.
41+
42+ - And the `Patch` value at that key is `Just x`: update the value at that key in the `PatchTarget` to `x`.
43+
44+- If the key is present in the `Patch` and not present in the `PatchTarget`...
45+
46+ - And the `Patch` value at that key is `Nothing`: do nothing because we're trying to delete a key that doesn't exist in the target in the first place.
47+
48+ - And the `Patch` value at that key is `Just x`: insert the key and the value `x` into the `PatchTarget`
49+
50+- If the key is *not* present in the `Patch` but present in the `PatchTarget`: do nothing.
51+
52+There are, of course, more complicated ways of patching maps involving, for example, moving values from one key to another. You can find the code for that in `Data.Patch.PatchMapWithMove`. Note that the `PatchTarget` type associated with the `PatchMapWithMove` patch instance is still `Map k v`!
bench-cbits/checkCapability.c+11−12
@@ -42,7 +42,7 @@ typedef struct Task_ {
4242 // or just continue immediately. It's a workaround for the fact
4343 // that signalling a condition variable doesn't do anything if the
4444 // thread is already running, but we want it to be sticky.
45− rtsBool wakeup;
45+ HsBool wakeup;
4646#endif
4747
4848 // This points to the Capability that the Task "belongs" to. If
@@ -62,14 +62,14 @@ typedef struct Task_ {
6262 // The current top-of-stack InCall
6363 struct InCall_ *incall;
6464
65− nat n_spare_incalls;
65+ uint32_t n_spare_incalls;
6666 struct InCall_ *spare_incalls;
6767
68− rtsBool worker; // == rtsTrue if this is a worker Task
69− rtsBool stopped; // this task has stopped or exited Haskell
68+ HsBool worker; // == rtsTrue if this is a worker Task
69+ HsBool stopped; // this task has stopped or exited Haskell
7070
7171 // So that we can detect when a finalizer illegally calls back into Haskell
72− rtsBool running_finalizers;
72+ HsBool running_finalizers;
7373
7474 // Links tasks on the returning_tasks queue of a Capability, and
7575 // on spare_workers.
@@ -88,7 +88,7 @@ struct Capability_ {
8888 StgFunTable f;
8989 StgRegTable r;
9090
91− nat no; // capability number.
91+ uint32_t no; // capability number.
9292
9393 // The Task currently holding this Capability. This task has
9494 // exclusive access to the contents of this Capability (apart from
@@ -98,12 +98,12 @@ struct Capability_ {
9898
9999 // true if this Capability is running Haskell code, used for
100100 // catching unsafe call-ins.
101− rtsBool in_haskell;
101+ HsBool in_haskell;
102102
103103 // Has there been any activity on this Capability since the last GC?
104− nat idle;
104+ uint32_t idle;
105105
106− rtsBool disabled;
106+ HsBool disabled;
107107
108108 // The run queue. The Task owning this Capability has exclusive
109109 // access to its run queue, so can wake up threads without
@@ -159,7 +159,7 @@ struct Capability_ {
159159#if defined(THREADED_RTS)
160160 // Worker Tasks waiting in the wings. Singly-linked.
161161 Task *spare_workers;
162− nat n_spare_workers; // count of above
162+ uint32_t n_spare_workers; // count of above
163163
164164 // This lock protects:
165165 // running_task
@@ -192,10 +192,9 @@ struct Capability_ {
192192
193193 // Per-capability STM-related data
194194 StgTVarWatchQueue *free_tvar_watch_queues;
195− StgInvariantCheckQueue *free_invariant_check_queues;
196195 StgTRecChunk *free_trec_chunks;
197196 StgTRecHeader *free_trec_headers;
198− nat transaction_tokens;
197+ uint32_t transaction_tokens;
199198} // typedef Capability is defined in RtsAPI.h
200199 // We never want a Capability to overlap a cache line with anything
201200 // else, so round it up to a cache line size:
bench/Main.hsdeleted−109
@@ -1,109 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE BangPatterns #-}
3−{-# LANGUAGE TupleSections #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TypeFamilies #-}
6−
7−-- The instance for NFData (TVar a) is an orphan, but necessary here
8−{-# OPTIONS_GHC -fno-warn-orphans #-}
9−
10−module Main where
11−
12−import Data.Functor.Misc
13−import Control.Monad.IO.Class
14−import Control.Monad.Identity
15−import Data.Dependent.Sum
16−import Control.Concurrent.STM
17−import Data.IORef
18−import Control.DeepSeq
19−import Control.Exception (evaluate)
20−import Reflex
21−import Reflex.Host.Class
22−import Criterion.Main
23−import Data.Dependent.Map (DMap)
24−import qualified Data.Dependent.Map as DMap
25−
26−main :: IO ()
27−main = defaultMain
28− [ bgroup "micro" micros ]
29−
30−#if !(MIN_VERSION_deepseq(1,4,2))
31−instance NFData (IORef a) where
32− rnf x = seq x ()
33−#endif
34−
35−instance NFData (TVar a) where
36− rnf x = seq x ()
37−
38−newtype WHNF a = WHNF a
39−instance NFData (WHNF a) where
40− rnf (WHNF a) = seq a ()
41−
42−withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
43−withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
44− bench name . nfIO $ runSpiderHost (action a)
45−
46−withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
47−withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
48− bench name . whnfIO $ runSpiderHost (action a)
49−
50−micros :: [Benchmark]
51−micros =
52− [ bench "newIORef" $ whnfIO $ void $ newIORef ()
53− , env (newIORef (42 :: Int)) (bench "readIORef" . whnfIO . readIORef)
54− , bench "newTVar" $ whnfIO $ void $ newTVarIO ()
55− , env (newTVarIO (42 :: Int)) (bench "readTVar" . whnfIO . readTVarIO)
56− , bench "newEventWithTrigger" $ whnfIO . void $ runSpiderHost $ newEventWithTrigger $
57− \trigger -> return () <$ evaluate trigger
58− , bench "newEventWithTriggerRef" $ whnfIO . void $ runSpiderHost newEventWithTriggerRef
59− , withSetupWHNF "subscribeEvent" newEventWithTriggerRef $ subscribeEvent . fst
60− , withSetupWHNF "subscribeSwitch"
61− (join $ hold <$> fmap fst newEventWithTriggerRef <*> fmap fst newEventWithTriggerRef)
62− (subscribeEvent . switch)
63− , withSetupWHNF "subscribeMerge(1)" (setupMerge 1) $ \(ev,_) -> subscribeEvent ev
64− , withSetupWHNF "subscribeMerge(100)" (setupMerge 100) (subscribeEvent . fst)
65− , withSetupWHNF "subscribeMerge(10000)" (setupMerge 10000) (subscribeEvent . fst)
66− , bench "runHostFrame" $ whnfIO $ runSpiderHost $ runHostFrame $ return ()
67− , withSetupWHNF "fireEventsAndRead(single/single)"
68− (newEventWithTriggerRef >>= subscribePair)
69− (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
70− , withSetupWHNF "fireEventsOnly"
71− (newEventWithTriggerRef >>= subscribePair)
72− (\(_, trigger) -> do
73− Just key <- liftIO $ readIORef trigger
74− fireEvents [key :=> Identity (42 :: Int)])
75− , withSetupWHNF "fireEventsAndRead(head/merge1)"
76− (setupMerge 1 >>= subscribePair)
77− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
78− , withSetupWHNF "fireEventsAndRead(head/merge100)"
79− (setupMerge 100 >>= subscribePair)
80− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
81− , withSetupWHNF "fireEventsAndRead(head/merge10000)"
82− (setupMerge 10000 >>= subscribePair)
83− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
84− , withSetupWHNF "fireEventsOnly(head/merge100)"
85− (setupMerge 100 >>= subscribePair)
86− (\(_, t:_) -> do
87− Just key <- liftIO $ readIORef t
88− fireEvents [key :=> Identity (42 :: Int)])
89− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
90− , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
91− ]
92−
93−setupMerge :: Int
94− -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
95− , [IORef (Maybe (EventTrigger Spider a))]
96− )
97−setupMerge num = do
98− (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
99− let !m = DMap.fromList [(Const2 i) :=> v | (i,v) <- zip [0..] evs]
100− pure (merge m, triggers)
101−
102−subscribePair :: (Event (SpiderEnv Global) a, b) -> SpiderHost Global (EventHandle (SpiderEnv Global) a, b)
103−subscribePair (ev, b) = (,b) <$> subscribeEvent ev
104−
105−fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
106− -> SpiderHost Global (Maybe b)
107−fireAndRead trigger val subd = do
108− Just key <- liftIO $ readIORef trigger
109− fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
bench/RunAll.hsdeleted−143
@@ -1,143 +0,0 @@
1−{-# LANGUAGE ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving, ForeignFunctionInterface, ViewPatterns, TemplateHaskell, PatternSynonyms #-}
2−{-# OPTIONS_GHC -fno-warn-orphans #-}
3−
4−module Main where
5−
6−import Criterion.Main
7−import Criterion.Types
8−
9−import Reflex
10−import Reflex.Host.Class
11−
12−import Reflex.TestPlan
13−import Reflex.Plan.Reflex
14−
15−import Reflex.Spider.Internal (SpiderEventHandle)
16−import qualified Reflex.Bench.Focused as Focused
17−
18−import Control.Applicative
19−import Control.DeepSeq (NFData (..))
20−
21−import System.IO
22−import System.Mem
23−import Prelude
24−
25−import Data.IORef
26−import System.Mem.Weak
27−import Data.Function
28−import Control.Concurrent
29−import Data.Time.Clock
30−import GHC.Stats
31−import Data.Monoid
32−import Control.Arrow
33−import Control.Monad
34−import Data.Bool
35−import Control.Concurrent.STM
36−import Data.Int
37−import Control.Exception
38−import Text.Read
39−import System.Environment
40−import Debug.Trace.LocationTH
41−import Control.Monad.Trans
42−import System.Process
43−
44−import Unsafe.Coerce
45−
46−import Data.Map (Map)
47−import qualified Data.Map as Map
48−
49−type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
50−
51−
52−setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (EventHandle t a, Schedule t)
53−setupFiring p = do
54− (e, s) <- runPlan p
55− h <- subscribeEvent e
56− return (h, s)
57−
58−-- Hack to avoid the NFData constraint for EventHandle which is a synonym
59−newtype Ignore a = Ignore a
60−instance NFData (Ignore a) where
61− rnf !_ = ()
62−
63−instance NFData (SpiderEventHandle a) where
64− rnf !_ = ()
65−
66−instance NFData (Behavior t a) where
67− rnf !_ = ()
68−
69−instance NFData (Firing t) where
70− rnf !(Firing _ _) = ()
71−
72−-- Measure the running time
73−benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
74−benchFiring runHost tc n = runHost $ do
75− let runIterations :: m a -> m ()
76− runIterations test = replicateM_ (10*n) $ do
77− result <- test
78− liftIO $ evaluate result
79− case tc of
80− TestE p -> do
81− (h, s) <- setupFiring p
82− runIterations $ readSchedule_ s $ readEvent' h
83− TestB p -> do
84− (b, s) <- runPlan p
85− runIterations $ readSchedule_ (makeDense s) $ sample b
86−
87−waitForFinalizers :: IO ()
88−waitForFinalizers = do
89− performGC
90− x <- getCurrentTime
91− isFinalized <- newIORef False
92− mkWeakPtr x $ Just $ writeIORef isFinalized True
93− performGC
94− fix $ \loop -> do
95− f <- readIORef isFinalized
96− if f then return () else do
97− threadDelay 1
98− loop
99−
100−benchmarks :: [(String, Int -> IO ())]
101−benchmarks = implGroup "spider" runSpiderHost cases
102− where
103− implGroup :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> [(String, TestCase)] -> [(String, Int -> IO ())]
104− implGroup name runHost = group name . fmap (second (benchFiring runHost))
105− group name = fmap $ first ((name <> "/") <>)
106− sub n frames = group ("subscribing " ++ show (n, frames)) $ Focused.subscribing n frames
107− firing n = group ("firing " <> show n) $ Focused.firing n
108− merging n = group ("merging " <> show n) $ Focused.merging n
109− dynamics n = group ("dynamics " <> show n) $ Focused.dynamics n
110− cases = concat
111− [ sub 100 40
112− , dynamics 100
113− , dynamics 1000
114− , firing 1000
115− , firing 10000
116− , merging 10
117− , merging 50
118− , merging 100
119− , merging 200
120− ]
121−
122−pattern RunTestCaseFlag = "--run-test-case"
123−
124−spawnBenchmark :: String -> Benchmark
125−spawnBenchmark name = Benchmark name $ Benchmarkable $ \n -> do
126− self <- getExecutablePath
127− callProcess self [RunTestCaseFlag, name, show n, "+RTS", "-N1"]
128−
129−foreign import ccall unsafe "myCapabilityHasOtherRunnableThreads" myCapabilityHasOtherRunnableThreads :: IO Bool
130−
131−main :: IO ()
132−main = do
133− args <- getArgs
134− case args of
135− RunTestCaseFlag : t -> case t of
136− [name, readMaybe -> Just count] -> do
137− case lookup name benchmarks of
138− Just testCase -> testCase count
139− performGC
140− fix $ \loop -> bool (return ()) (yield >> loop) =<< myCapabilityHasOtherRunnableThreads
141− return ()
142− _ -> $failure "--run-test-case: expected test name and iteration count to follow"
143− _ -> defaultMainWith (defaultConfig { timeLimit = 20, csvFile = Just "dmap-original.csv", reportFile = Just "report.html" }) $ fmap (spawnBenchmark . fst) benchmarks
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
default.nixdeleted−24
@@ -1,24 +0,0 @@
1−{ mkDerivation, base, containers, dependent-map, dependent-sum
2−, exception-transformers, haskell-src-exts, haskell-src-meta
3−, MemoTrie, mtl, primitive, ref-tf, semigroups, stdenv, syb
4−, template-haskell, these, transformers, transformers-compat
5−, criterion, deepseq, split, stm, loch-th
6−}:
7−mkDerivation {
8− pname = "reflex";
9− version = "0.4.0";
10− src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
11− libraryHaskellDepends = [
12− base containers dependent-map dependent-sum exception-transformers
13− haskell-src-exts haskell-src-meta mtl primitive ref-tf semigroups
14− syb template-haskell these transformers transformers-compat
15− ];
16− testHaskellDepends = [
17− base containers dependent-map MemoTrie mtl ref-tf
18− # Benchmark dependencies
19− criterion deepseq split stm loch-th
20− ];
21− homepage = "https://github.com/reflex-frp/reflex";
22− description = "Higher-order Functional Reactive Programming";
23− license = stdenv.lib.licenses.bsd3;
24−}
dep/reflex-platform/default.nixnew file+8
@@ -0,0 +1,8 @@
1+# DO NOT HAND-EDIT THIS FILE
2+let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
3+ if !fetchSubmodules && !private then builtins.fetchTarball {
4+ url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
5+ } else (import <nixpkgs> {}).fetchFromGitHub {
6+ inherit owner repo rev sha256 fetchSubmodules private;
7+ };
8+in import (fetch (builtins.fromJSON (builtins.readFile ./github.json)))
dep/reflex-platform/github.jsonnew file+8
@@ -0,0 +1,8 @@
1+{
2+ "owner": "reflex-frp",
3+ "repo": "reflex-platform",
4+ "branch": "master",
5+ "private": false,
6+ "rev": "c9d11db1b98855fe8ab24a3ff6a5dbe0ad902ad9",
7+ "sha256": "0sfzkqdvyah5mwvmli0wq1nl0b8cvk2cmfgfy4rz57wv42x3099y"
8+}
patch.cabalnew file+100
@@ -0,0 +1,100 @@
1+Name: patch
2+Version: 0.0.5.0
3+Synopsis: Data structures for describing changes to other data structures.
4+Description:
5+ Data structures for describing changes to other data structures.
6+ .
7+ In this library, a patch is something which can be applied, analogous to a
8+ function, and which distinguishes returning the argument it was provided from
9+ returning something else.
10+License: BSD3
11+License-file: LICENSE
12+Author: Ryan Trinkle
13+Maintainer: maintainer@obsidian.systems
14+Stability: Experimental
15+Category: FRP
16+Build-type: Simple
17+Cabal-version: >=1.10
18+homepage: https://obsidian.systems
19+bug-reports: https://github.com/reflex-frp/patch/issues
20+extra-source-files:
21+ README.md
22+ ChangeLog.md
23+
24+tested-with:
25+ GHC ==8.0.2 || ==8.2.2 || ==8.4.4 || ==8.6.5 || ==8.8.1
26+ GHCJS ==8.4
27+
28+flag split-these
29+ description: Use split these/semialign packages
30+ manual: False
31+ default: True
32+
33+library
34+ hs-source-dirs: src
35+ default-language: Haskell2010
36+ build-depends: base >= 4.9 && < 4.15
37+ , constraints-extras >= 0.3 && < 0.4
38+ , containers >= 0.6 && < 0.7
39+ , dependent-map >= 0.3 && < 0.5
40+ , dependent-sum >= 0.6 && < 0.8
41+ , lens >= 4.7 && < 5
42+ , semigroupoids >= 4.0 && < 6
43+ , transformers >= 0.5.6.0 && < 0.6
44+ , witherable >= 0.3 && < 0.5
45+
46+ if impl(ghc < 8.6) -- really, if base < 8.12
47+ build-depends: base-orphans >= 0.8 && < 0.9
48+
49+ exposed-modules: Data.Functor.Misc
50+ , Data.Monoid.DecidablyEmpty
51+ , Data.Patch
52+ , Data.Patch.Class
53+ , Data.Patch.DMap
54+ , Data.Patch.DMapWithMove
55+ , Data.Patch.IntMap
56+ , Data.Patch.Map
57+ , Data.Patch.MapWithMove
58+ , Data.Patch.MapWithPatchingMove
59+ , Data.Semigroup.Additive
60+
61+ ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs
62+ default-extensions: PolyKinds
63+
64+ if flag(split-these)
65+ build-depends: these >= 1 && <1.2
66+ , semialign >=1 && <1.2
67+ , monoidal-containers >= 0.6 && < 0.7
68+ else
69+ build-depends: these >= 0.4 && <0.9
70+ , monoidal-containers == 0.4.0.0
71+
72+test-suite tests
73+ default-language: Haskell2010
74+ type: exitcode-stdio-1.0
75+ main-is: tests.hs
76+ hs-source-dirs: test
77+ build-depends: base
78+ , patch
79+ , containers
80+ , hedgehog
81+ , HUnit
82+ if impl(ghcjs)
83+ buildable: False
84+
85+test-suite hlint
86+ default-language: Haskell2010
87+ type: exitcode-stdio-1.0
88+ main-is: hlint.hs
89+ hs-source-dirs: test
90+ build-depends: base
91+ , directory
92+ , filepath
93+ , filemanip
94+ , hlint (< 2.1 || >= 2.2.2) && < 3.3
95+ if impl(ghcjs)
96+ buildable: False
97+
98+source-repository head
99+ type: git
100+ location: https://github.com/reflex-frp/patch
reflex.cabaldeleted−120
@@ -1,120 +0,0 @@
1−Name: reflex
2−Version: 0.4.0
3−Synopsis: Higher-order Functional Reactive Programming
4−Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5−License: BSD3
6−License-file: LICENSE
7−Author: Ryan Trinkle
8−Maintainer: ryan.trinkle@gmail.com
9−Stability: Experimental
10−Category: FRP
11−Build-type: Simple
12−Cabal-version: >=1.9.2
13−homepage: https://github.com/reflex-frp/reflex
14−bug-reports: https://github.com/reflex-frp/reflex/issues
15−
16−library
17− hs-source-dirs: src
18− build-depends:
19− base >= 4.7 && < 4.10,
20− bifunctors >= 5.2 && < 5.4,
21− dependent-sum == 0.3.*,
22− dependent-map >= 0.2.2 && < 0.3,
23− semigroups >= 0.16 && < 0.19,
24− mtl >= 2.1 && < 2.3,
25− containers == 0.5.*,
26− these >= 0.4 && < 0.8,
27− primitive >= 0.5 && < 0.7,
28− template-haskell >= 2.9 && < 2.12,
29− ref-tf == 0.4.*,
30− exception-transformers == 0.4.*,
31− transformers >= 0.2,
32− transformers-compat >= 0.3,
33− haskell-src-exts >= 1.16 && < 1.18,
34− haskell-src-meta == 0.6.*,
35− syb >= 0.5 && < 0.7,
36− stm == 2.4.*
37−
38− exposed-modules:
39− Reflex,
40− Reflex.Spider,
41− Reflex.Spider.Internal,
42− Reflex.Class,
43− Reflex.Dynamic,
44− Reflex.Dynamic.Nub,
45− Reflex.Dynamic.TH,
46− Reflex.Host.Class,
47− Data.Functor.Misc,
48− Data.WeakBag
49−
50− other-extensions: TemplateHaskell
51− ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
52− ghc-prof-options: -auto-all
53−
54−
55−test-suite all
56− type: exitcode-stdio-1.0
57− main-is: Reflex/Test/RunAll.hs
58− ghc-options: -O2 -Wall -rtsopts
59− hs-source-dirs: test
60− build-depends:
61− base,
62− reflex,
63− ref-tf,
64− mtl,
65− containers,
66− dependent-map,
67− dependent-sum,
68− transformers >= 0.3,
69− MemoTrie == 0.6.*,
70− deepseq >= 1.3 && < 1.5,
71− bifunctors,
72− split
73−
74−
75−benchmark spider-bench
76− type: exitcode-stdio-1.0
77− hs-source-dirs: bench
78− main-is: Main.hs
79− ghc-options: -Wall -O2 -rtsopts
80− build-depends:
81− base,
82− dependent-sum,
83− dependent-map,
84− transformers >= 0.3,
85− stm == 2.4.*,
86− deepseq >= 1.3 && < 1.5,
87− mtl,
88− primitive,
89− criterion == 1.1.*,
90− reflex
91−
92−
93−benchmark saulzar-bench
94− type: exitcode-stdio-1.0
95− hs-source-dirs: bench test
96− c-sources: bench-cbits/checkCapability.c
97− main-is: RunAll.hs
98− ghc-options: -Wall -O2 -rtsopts -threaded
99− ghc-prof-options: -auto-all
100− build-depends:
101− base,
102− containers == 0.5.*,
103− ref-tf == 0.4,
104− dependent-sum,
105− dependent-map,
106− transformers >= 0.3,
107− stm == 2.4.*,
108− deepseq >= 1.3 && < 1.5,
109− mtl,
110− primitive,
111− criterion == 1.1.*,
112− split,
113− reflex,
114− time,
115− loch-th,
116− process
117−
118−source-repository head
119− type: git
120− location: https://github.com/reflex-frp/reflex
release.nixnew file+49
@@ -0,0 +1,49 @@
1+{ reflex-platform-fun ? import ./dep/reflex-platform
2+}:
3+
4+let
5+ native-reflex-platform = reflex-platform-fun {};
6+ inherit (native-reflex-platform.nixpkgs) lib;
7+ systems = ["x86_64-linux" "x86_64-darwin"];
8+
9+ perPlatform = lib.genAttrs systems (system: let
10+ reflex-platform = reflex-platform-fun { inherit system; };
11+ compilers = [
12+ "ghc"
13+ "ghcjs"
14+ ] ++ lib.optionals (reflex-platform.androidSupport) [
15+ "ghcAndroidAarch64"
16+ "ghcAndroidAarch32"
17+ ] ++ lib.optionals (reflex-platform.iosSupport) [
18+ "ghcIosAarch64"
19+ ];
20+ compilerPkgs = lib.genAttrs compilers (ghc: let
21+ reflex-platform = reflex-platform-fun {
22+ inherit system;
23+ haskellOverlays = [
24+ # Use this package's source for reflex
25+ (self: super: {
26+ _dep = super._dep // {
27+ patch = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
28+ "release.nix"
29+ ".git"
30+ "dist"
31+ "dist-newstyle"
32+ "cabal.haskell-ci"
33+ "cabal.project"
34+ ".travis.yml"
35+ ])) ./.;
36+ };
37+ })
38+ ];
39+ };
40+ in reflex-platform.${ghc}.patch);
41+ in compilerPkgs // {
42+ cache = reflex-platform.pinBuildInputs "patch-${system}"
43+ (builtins.attrValues compilerPkgs);
44+ });
45+
46+ metaCache = native-reflex-platform.pinBuildInputs "patch-everywhere"
47+ (map (a: a.cache) (builtins.attrValues perPlatform));
48+
49+in perPlatform // { inherit metaCache; }
src/Data/Functor/Misc.hs+194−52
@@ -1,41 +1,79 @@
1−{-# LANGUAGE KindSignatures, GADTs, DeriveDataTypeable, RankNTypes, ScopedTypeVariables, PolyKinds, FlexibleInstances, MultiParamTypeClasses, FlexibleContexts, StandaloneDeriving #-}
2−module Data.Functor.Misc where
3−
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveFunctor #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GADTs #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE StandaloneDeriving #-}
13+-- | This module provides types and functions with no particular theme, but
14+-- which are relevant to the use of 'Functor'-based datastructures like
15+-- 'Data.Dependent.Map.DMap'.
16+module Data.Functor.Misc
17+ ( -- * Const2
18+ Const2 (..)
19+ , unConst2
20+ , dmapToMap
21+ , dmapToIntMap
22+ , dmapToMapWith
23+ , mapToDMap
24+ , weakenDMapWith
25+ -- * WrapArg
26+ , WrapArg (..)
27+ -- * Convenience functions for DMap
28+ , mapWithFunctorToDMap
29+ , intMapWithFunctorToDMap
30+ , mapKeyValuePairsMonotonic
31+ , combineDMapsWithKey
32+ , EitherTag (..)
33+ , dmapToThese
34+ , eitherToDSum
35+ , dsumToEither
36+ , ComposeMaybe (..)
37+ ) where
38+
39+import Control.Applicative ((<$>))
40+import Data.Dependent.Map (DMap)
41+import qualified Data.Dependent.Map as DMap
42+import Data.Dependent.Sum
43+import Data.Functor.Identity
444import Data.GADT.Compare
45+import Data.GADT.Show
46+import Data.IntMap (IntMap)
47+import qualified Data.IntMap as IntMap
548import Data.Map (Map)
649import qualified Data.Map as Map
7−import Data.GADT.Show
8−import Data.Dependent.Sum
9−import Data.Dependent.Map (DMap)
10−import qualified Data.Dependent.Map as DMap
11−import Data.Typeable hiding (Refl)
50+import Data.Some (Some, mkSome)
1251import Data.These
13−import Control.Monad.Identity
14−
15−data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
16− WrapArg :: f a -> WrapArg g f (g a)
17−
18−instance GEq f => GEq (WrapArg g f) where
19− geq (WrapArg a) (WrapArg b) = fmap (\Refl -> Refl) $ geq a b
52+import Data.Type.Equality ((:~:)(Refl))
53+import Data.Typeable hiding (Refl)
2054
21−instance GCompare f => GCompare (WrapArg g f) where
22− gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
23− GLT -> GLT
24− GEQ -> GEQ
25− GGT -> GGT
55+--------------------------------------------------------------------------------
56+-- Const2
57+--------------------------------------------------------------------------------
2658
27−data Const2 :: * -> * -> * -> * where
59+-- | 'Const2' stores a value of a given type 'k' and ensures that a particular
60+-- type 'v' is always given for the last type parameter
61+data Const2 :: * -> x -> x -> * where
2862 Const2 :: k -> Const2 k v v
2963 deriving (Typeable)
3064
31−deriving instance Show k => Show (Const2 k v v)
65+-- | Extract the value from a Const2
66+unConst2 :: Const2 k v v' -> k
67+unConst2 (Const2 k) = k
68+
69+deriving instance Eq k => Eq (Const2 k v v')
70+deriving instance Ord k => Ord (Const2 k v v')
71+deriving instance Show k => Show (Const2 k v v')
72+deriving instance Read k => Read (Const2 k v v)
3273
3374instance Show k => GShow (Const2 k v) where
3475 gshowsPrec n x@(Const2 _) = showsPrec n x
3576
36−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
37− showTaggedPrec (Const2 _) = showsPrec
38−
3977instance Eq k => GEq (Const2 k v) where
4078 geq (Const2 a) (Const2 b) =
4179 if a == b
@@ -48,13 +86,81 @@ instance Ord k => GCompare (Const2 k v) where
4886 EQ -> GEQ
4987 GT -> GGT
5088
51−{-# INLINE sequenceDmap #-}
52−sequenceDmap :: (Monad m, GCompare f) => DMap f m -> m (DMap f Identity)
53−sequenceDmap = DMap.foldrWithKey (\k mv mx -> mx >>= \x -> mv >>= \v -> return $ DMap.insert k (Identity v) x)
54− (return DMap.empty)
89+-- | Convert a 'DMap' to a regular 'Map'
90+dmapToMap :: DMap (Const2 k v) Identity -> Map k v
91+dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
92+
93+-- | Convert a 'DMap' to an 'IntMap'
94+dmapToIntMap :: DMap (Const2 IntMap.Key v) Identity -> IntMap v
95+dmapToIntMap = IntMap.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
96+
97+-- | Convert a 'DMap' to a regular 'Map', applying the given function to remove
98+-- the wrapping 'Functor'
99+dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v'
100+dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList
101+
102+-- | Convert a regular 'Map' to a 'DMap'
103+mapToDMap :: Map k v -> DMap (Const2 k v) Identity
104+mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
105+
106+-- | Convert a regular 'Map', where the values are already wrapped in a functor,
107+-- to a 'DMap'
108+mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
109+mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
110+
111+-- | Convert a regular 'IntMap', where the values are already wrapped in a
112+-- functor, to a 'DMap'
113+intMapWithFunctorToDMap :: IntMap (f v) -> DMap (Const2 IntMap.Key v) f
114+intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . IntMap.toAscList
115+
116+-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
117+-- the keys, using a function to remove the wrapping 'Functor'
118+weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
119+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
120+
121+--------------------------------------------------------------------------------
122+-- WrapArg
123+--------------------------------------------------------------------------------
124+
125+-- | 'WrapArg' can be used to tag a value in one functor with a type
126+-- representing another functor. This was primarily used with dependent-map <
127+-- 0.2, in which the value type was not wrapped in a separate functor.
128+data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
129+ WrapArg :: f a -> WrapArg g f (g a)
130+
131+deriving instance Eq (f a) => Eq (WrapArg g f (g' a))
132+deriving instance Ord (f a) => Ord (WrapArg g f (g' a))
133+deriving instance Show (f a) => Show (WrapArg g f (g' a))
134+deriving instance Read (f a) => Read (WrapArg g f (g a))
135+
136+instance GEq f => GEq (WrapArg g f) where
137+ geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
138+
139+instance GCompare f => GCompare (WrapArg g f) where
140+ gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
141+ GLT -> GLT
142+ GEQ -> GEQ
143+ GGT -> GGT
144+
145+--------------------------------------------------------------------------------
146+-- Convenience functions for DMap
147+--------------------------------------------------------------------------------
148+
149+-- | Map over all key/value pairs in a 'DMap', potentially altering the key as
150+-- well as the value. The provided function MUST preserve the ordering of the
151+-- keys, or the resulting 'DMap' will be malformed.
152+mapKeyValuePairsMonotonic :: (DSum k v -> DSum k' v') -> DMap k v -> DMap k' v'
153+mapKeyValuePairsMonotonic f = DMap.fromDistinctAscList . map f . DMap.toAscList
55154
56155{-# INLINE combineDMapsWithKey #-}
57−combineDMapsWithKey :: forall f g h i. GCompare f => (forall (a :: *). f a -> These (g a) (h a) -> i a) -> DMap f g -> DMap f h -> DMap f i
156+-- | Union two 'DMap's of different types, yielding another type. Each key that
157+-- is present in either input map will be present in the output.
158+combineDMapsWithKey :: forall f g h i.
159+ GCompare f
160+ => (forall a. f a -> These (g a) (h a) -> i a)
161+ -> DMap f g
162+ -> DMap f h
163+ -> DMap f i
58164combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList mh)
59165 where go :: [DSum f g] -> [DSum f h] -> [DSum f i]
60166 go [] hs = map (\(hk :=> hv) -> hk :=> f hk (That hv)) hs
@@ -64,26 +170,62 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
64170 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
65171 GGT -> (hk :=> f hk (That hv)) : go gs hs'
66172
67−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
68−wrapDMap f = DMap.mapWithKey $ \_ -> f . runIdentity
69−
70−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
71−rewrapDMap f = DMap.mapWithKey $ \_ -> f
72−
73−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
74−unwrapDMap f = DMap.mapWithKey $ \_ -> Identity . f
75−
76−unwrapDMapMaybe :: (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
77−unwrapDMapMaybe f m = DMap.fromDistinctAscList [k :=> Identity w | (k :=> v) <- DMap.toAscList m, Just w <- [f v]]
78−
79−mapToDMap :: Map k v -> DMap (Const2 k v) Identity
80−mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
81−
82−mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
83−mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
84−
85−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
86−extractFunctorDMap = DMap.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> Const2 k :=> v) . DMap.toAscList
173+-- | Extract the values of a 'DMap' of 'EitherTag's.
174+dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
175+dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
176+ (Nothing, Nothing) -> Nothing
177+ (Just (Identity a), Nothing) -> Just $ This a
178+ (Nothing, Just (Identity b)) -> Just $ That b
179+ (Just (Identity a), Just (Identity b)) -> Just $ These a b
180+
181+-- | Tag type for 'Either' to use it as a 'DSum'.
182+data EitherTag l r a where
183+ LeftTag :: EitherTag l r l
184+ RightTag :: EitherTag l r r
185+ deriving (Typeable)
87186
88−dmapToMap :: DMap (Const2 k v) Identity -> Map k v
89−dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
187+deriving instance Show (EitherTag l r a)
188+deriving instance Eq (EitherTag l r a)
189+deriving instance Ord (EitherTag l r a)
190+
191+instance GEq (EitherTag l r) where
192+ geq a b = case (a, b) of
193+ (LeftTag, LeftTag) -> Just Refl
194+ (RightTag, RightTag) -> Just Refl
195+ _ -> Nothing
196+
197+instance GCompare (EitherTag l r) where
198+ gcompare a b = case (a, b) of
199+ (LeftTag, LeftTag) -> GEQ
200+ (LeftTag, RightTag) -> GLT
201+ (RightTag, LeftTag) -> GGT
202+ (RightTag, RightTag) -> GEQ
203+
204+instance GShow (EitherTag l r) where
205+ gshowsPrec _ a = case a of
206+ LeftTag -> showString "LeftTag"
207+ RightTag -> showString "RightTag"
208+
209+-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
210+eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
211+eitherToDSum = \case
212+ Left a -> (LeftTag :=> Identity a)
213+ Right b -> (RightTag :=> Identity b)
214+
215+-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
216+dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
217+dsumToEither = \case
218+ (LeftTag :=> Identity a) -> Left a
219+ (RightTag :=> Identity b) -> Right b
220+
221+--------------------------------------------------------------------------------
222+-- ComposeMaybe
223+--------------------------------------------------------------------------------
224+
225+-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
226+-- make the 'f' parameter have a nominal type role. We need f to be
227+-- representational so that we can use safe 'coerce'.
228+newtype ComposeMaybe f a =
229+ ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
230+
231+deriving instance Functor f => Functor (ComposeMaybe f)
src/Data/Monoid/DecidablyEmpty.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+{-# LANGUAGE TypeOperators #-}
6+
7+-- TODO upstream somwhere else?
8+module Data.Monoid.DecidablyEmpty where
9+
10+import Data.Functor.Identity
11+import Data.Functor.Const
12+import Data.Monoid
13+import Data.Maybe (isNothing)
14+#if MIN_VERSION_base(4,11,0)
15+import Data.Ord
16+#endif
17+import Data.Proxy
18+import Data.Semigroup hiding (First, Last)
19+#if MIN_VERSION_base(4,12,0)
20+import GHC.Generics
21+#endif
22+
23+import qualified Data.IntSet as IntSet
24+import qualified Data.IntMap as IntMap
25+import qualified Data.Map as Map
26+import qualified Data.Sequence as Seq
27+import qualified Data.Set as Set
28+
29+import Data.GADT.Compare
30+import qualified Data.Dependent.Map as DMap
31+
32+-- | A 'DecidablyEmpty' is one where it can be computed whether or not an
33+-- arbitrary value is 'mempty'.
34+--
35+-- By using this class rather than 'Eq', we avoid unnecessary constraining the
36+-- contents of 'Functor's. This makes it possible to efficiently combine and/or
37+-- nest patch maps with 'Eq'-lacking values (e.g. functions) at the leaves.
38+class Monoid a => DecidablyEmpty a where
39+ isEmpty :: a -> Bool
40+ default isEmpty :: Eq a => a -> Bool
41+ isEmpty = (==) mempty
42+
43+-- base
44+
45+instance DecidablyEmpty Ordering
46+instance DecidablyEmpty ()
47+instance DecidablyEmpty Any
48+instance DecidablyEmpty All
49+-- instance DecidablyEmpty Lifetime
50+-- instance DecidablyEmpty Event
51+instance DecidablyEmpty [a] where
52+ isEmpty = null
53+instance
54+#if MIN_VERSION_base(4,11,0)
55+ Semigroup a
56+#else
57+ Monoid a
58+#endif
59+ => DecidablyEmpty (Maybe a) where
60+ isEmpty = isNothing
61+deriving instance (Num a, DecidablyEmpty a) => DecidablyEmpty (Product a)
62+deriving instance (DecidablyEmpty a, Num a) => DecidablyEmpty (Sum a)
63+deriving instance DecidablyEmpty a => DecidablyEmpty (Dual a)
64+instance DecidablyEmpty (First a) where
65+ isEmpty (First a) = isNothing a
66+instance DecidablyEmpty (Last a) where
67+ isEmpty (Last a) = isNothing a
68+deriving instance DecidablyEmpty a => DecidablyEmpty (Identity a)
69+instance Semigroup a => DecidablyEmpty (Option a) where
70+ isEmpty (Option a) = isNothing a
71+deriving instance DecidablyEmpty m => DecidablyEmpty (WrappedMonoid m)
72+instance (Ord a, Bounded a) => DecidablyEmpty (Max a)
73+instance (Ord a, Bounded a) => DecidablyEmpty (Min a)
74+instance DecidablyEmpty (Proxy s)
75+deriving instance DecidablyEmpty a => DecidablyEmpty (Const a b)
76+#if MIN_VERSION_base(4,11,0)
77+deriving instance DecidablyEmpty a => DecidablyEmpty (Down a)
78+#endif
79+#if MIN_VERSION_base(4,12,0)
80+deriving instance DecidablyEmpty p => DecidablyEmpty (Par1 p)
81+instance DecidablyEmpty (U1 p)
82+deriving instance DecidablyEmpty (f p) => DecidablyEmpty (Rec1 f p)
83+deriving instance DecidablyEmpty (f p) => DecidablyEmpty (M1 i c f p)
84+deriving instance DecidablyEmpty c => DecidablyEmpty (K1 i c p)
85+instance (DecidablyEmpty (f p), DecidablyEmpty (g p)) => DecidablyEmpty ((f :*: g) p) where
86+ isEmpty (x :*: y) = isEmpty x && isEmpty y
87+deriving instance DecidablyEmpty (f (g p)) => DecidablyEmpty ((f :.: g) p)
88+#endif
89+
90+instance (DecidablyEmpty a, DecidablyEmpty b) => DecidablyEmpty (a, b) where
91+ isEmpty (a, b) = isEmpty a && isEmpty b
92+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c) => DecidablyEmpty (a, b, c) where
93+ isEmpty (a, b, c) = isEmpty a && isEmpty b && isEmpty c
94+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d) => DecidablyEmpty (a, b, c, d) where
95+ isEmpty (a, b, c, d) = isEmpty a && isEmpty b && isEmpty c && isEmpty d
96+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d, DecidablyEmpty e) => DecidablyEmpty (a, b, c, d, e) where
97+ isEmpty (a, b, c, d, e) = isEmpty a && isEmpty b && isEmpty c && isEmpty d && isEmpty e
98+
99+-- containers
100+
101+instance DecidablyEmpty IntSet.IntSet where
102+ isEmpty = IntSet.null
103+instance DecidablyEmpty (IntMap.IntMap v) where
104+ isEmpty = IntMap.null
105+instance Ord k => DecidablyEmpty (Map.Map k v) where
106+ isEmpty = Map.null
107+instance DecidablyEmpty (Seq.Seq v) where
108+ isEmpty = Seq.null
109+instance Ord k => DecidablyEmpty (Set.Set k) where
110+ isEmpty = Set.null
111+
112+-- dependent-map
113+
114+instance GCompare k => DecidablyEmpty (DMap.DMap k v) where
115+ isEmpty = DMap.null
src/Data/Patch.hsnew file+95
@@ -0,0 +1,95 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE StandaloneDeriving #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE TypeOperators #-}
6+-- |
7+-- Module:
8+-- Data.Patch
9+-- Description:
10+-- This module defines the 'Patch' class.
11+module Data.Patch
12+ ( module Data.Patch
13+ , module X
14+ ) where
15+
16+import Control.Applicative
17+import Data.Functor.Const (Const (..))
18+import Data.Functor.Identity
19+import Data.Map.Monoidal (MonoidalMap)
20+import Data.Proxy
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
23+#endif
24+import GHC.Generics
25+
26+import Data.Semigroup.Additive as X
27+import Data.Patch.Class as X
28+import Data.Patch.DMap as X hiding (getDeletions)
29+import Data.Patch.DMapWithMove as X
30+ ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
31+ , patchDMapWithMoveToPatchMapWithMoveWith
32+ , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
33+ , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
34+ )
35+import Data.Patch.IntMap as X hiding (getDeletions)
36+import Data.Patch.Map as X
37+import Data.Patch.MapWithMove as X
38+ ( PatchMapWithMove, patchMapWithMoveNewElements
39+ , patchMapWithMoveNewElementsMap, unPatchMapWithMove
40+ , unsafePatchMapWithMove
41+ )
42+
43+-- | A 'Group' is a 'Monoid' where every element has an inverse.
44+class (Semigroup q, Monoid q) => Group q where
45+ negateG :: q -> q
46+ (~~) :: q -> q -> q
47+ r ~~ s = r <> negateG s
48+
49+-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
50+newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
51+
52+instance Additive p => Patch (AdditivePatch p) where
53+ type PatchTarget (AdditivePatch p) = p
54+ apply (AdditivePatch p) q = Just $ p <> q
55+
56+instance (Ord k, Group q) => Group (MonoidalMap k q) where
57+ negateG = fmap negateG
58+
59+-- | Trivial group.
60+instance Group () where
61+ negateG _ = ()
62+ _ ~~ _ = ()
63+
64+-- | Product group. A Pair of groups gives rise to a group
65+instance (Group a, Group b) => Group (a, b) where
66+ negateG (a, b) = (negateG a, negateG b)
67+ (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
68+
69+-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
70+-- Base does not define Monoid (Compose f g a) so this is the best we can
71+-- really do for functor composition.
72+instance Group (f (g a)) => Group ((f :.: g) a) where
73+ negateG (Comp1 xs) = Comp1 (negateG xs)
74+ Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
75+
76+-- | Product of groups, Functor style.
77+instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
78+ negateG (a :*: b) = negateG a :*: negateG b
79+ (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
80+
81+-- | Trivial group, Functor style
82+instance Group (Proxy x) where
83+ negateG _ = Proxy
84+ _ ~~ _ = Proxy
85+
86+-- | Const lifts groups into a functor.
87+deriving instance Group a => Group (Const a x)
88+
89+-- | Identity lifts groups pointwise (at only one point)
90+deriving instance Group a => Group (Identity a)
91+
92+-- | Functions lift groups pointwise.
93+instance Group b => Group (a -> b) where
94+ negateG f = negateG . f
95+ (~~) = liftA2 (~~)
src/Data/Patch/Class.hsnew file+45
@@ -0,0 +1,45 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- | The interface for types which represent changes made to other types
5+module Data.Patch.Class where
6+
7+import Data.Functor.Identity
8+import Data.Kind (Type)
9+import Data.Maybe
10+#if !MIN_VERSION_base(4,11,0)
11+import Data.Semigroup (Semigroup(..))
12+#endif
13+import Data.Proxy
14+
15+-- | A 'Patch' type represents a kind of change made to a datastructure.
16+--
17+-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
18+-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
19+class Patch p where
20+ type PatchTarget p :: Type
21+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
22+ -- 'Nothing'.
23+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
24+
25+-- | Apply a 'Patch'; if it does nothing, return the original value
26+applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
27+applyAlways p t = fromMaybe t $ apply p t
28+
29+-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
30+instance Patch (Identity a) where
31+ type PatchTarget (Identity a) = a
32+ apply (Identity a) _ = Just a
33+
34+-- | 'Proxy' can be used as a 'Patch' that does nothing.
35+instance forall (a :: Type). Patch (Proxy a) where
36+ type PatchTarget (Proxy a) = a
37+ apply ~Proxy _ = Nothing
38+
39+-- | Like '(.)', but composes functions that return patches rather than
40+-- functions that return new values. The Semigroup instance for patches must
41+-- apply patches right-to-left, like '(.)'.
42+composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
43+composePatchFunctions g f a =
44+ let fp = f a
45+ in g (applyAlways fp a) <> fp
src/Data/Patch/DMap.hsnew file+93
@@ -0,0 +1,93 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE PolyKinds #-}
5+{-# LANGUAGE Rank2Types #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
11+module Data.Patch.DMap where
12+
13+import Data.Patch.Class
14+import Data.Patch.IntMap
15+import Data.Patch.Map
16+
17+import Data.Dependent.Map (DMap)
18+import Data.Dependent.Sum (DSum (..))
19+import Data.GADT.Compare (GCompare (..))
20+import qualified Data.Dependent.Map as DMap
21+import Data.Functor.Constant
22+import Data.Functor.Misc
23+import qualified Data.IntMap as IntMap
24+import qualified Data.Map as Map
25+import Data.Monoid.DecidablyEmpty
26+#if !MIN_VERSION_base(4,11,0)
27+import Data.Semigroup (Semigroup (..))
28+#endif
29+import Data.Some (Some)
30+
31+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
32+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
33+-- while deletions are represented as @'ComposeMaybe' Nothing@.
34+newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
35+
36+deriving instance GCompare k => Semigroup (PatchDMap k v)
37+
38+deriving instance GCompare k => Monoid (PatchDMap k v)
39+
40+-- It won't let me derive for some reason
41+instance GCompare k => DecidablyEmpty (PatchDMap k v) where
42+ isEmpty (PatchDMap m) = DMap.null m
43+
44+-- | Apply the insertions or deletions to a given 'DMap'.
45+instance GCompare k => Patch (PatchDMap k v) where
46+ type PatchTarget (PatchDMap k v) = DMap k v
47+ apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
48+ where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
49+ deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
50+ nothingToJust = \case
51+ Nothing -> Just $ Constant ()
52+ Just _ -> Nothing
53+
54+-- | Map a function @v a -> v' a@ over any inserts/updates in the given
55+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
56+mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
57+mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
58+
59+-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
60+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
61+traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
62+traversePatchDMap f = traversePatchDMapWithKey $ const f
63+
64+-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
65+-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
66+traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
67+traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
68+
69+-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
70+-- @v a -> v'@ to weaken each value contained in the patch.
71+weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
72+weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
73+
74+-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
75+-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
76+patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
77+patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
78+
79+-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
80+const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
81+const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
82+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
83+ g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
84+
85+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
86+const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
87+const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
88+ where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
89+ g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
90+
91+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
92+getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
93+getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Data/Patch/DMapWithMove.hsnew file+371
@@ -0,0 +1,371 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+
14+-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
15+-- move values between keys.
16+module Data.Patch.DMapWithMove where
17+
18+import Data.Patch.Class
19+import Data.Patch.MapWithMove (PatchMapWithMove (..))
20+import qualified Data.Patch.MapWithMove as MapWithMove
21+
22+import Data.Constraint.Extras
23+import Data.Dependent.Map (DMap)
24+import Data.Dependent.Sum (DSum (..))
25+import qualified Data.Dependent.Map as DMap
26+import Data.Functor.Constant
27+import Data.Functor.Misc
28+import Data.Functor.Product
29+import Data.GADT.Compare (GEq (..), GCompare (..))
30+import Data.GADT.Show (GShow, gshow)
31+import qualified Data.Map as Map
32+import Data.Maybe
33+import Data.Monoid.DecidablyEmpty
34+#if !MIN_VERSION_base(4,11,0)
35+import Data.Semigroup (Semigroup (..))
36+#endif
37+import Data.Some (Some, mkSome)
38+import Data.These
39+
40+-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
41+-- destinations.
42+--
43+-- Invariants:
44+--
45+-- * A key should not move to itself.
46+-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
47+newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
48+
49+-- It won't let me derive for some reason
50+instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
51+ isEmpty (PatchDMapWithMove m) = DMap.null m
52+
53+-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
54+-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
55+data NodeInfo k v a = NodeInfo
56+ { _nodeInfo_from :: !(From k v a)
57+ -- ^Change applying to the current key, be it an insert, move, or delete.
58+ , _nodeInfo_to :: !(To k a)
59+ -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
60+ }
61+ deriving (Show)
62+
63+-- |Structure describing a particular change to a key, be it inserting a new key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting a
64+-- key (@From_Delete@), or moving a key (@From_Move@).
65+data From (k :: a -> *) (v :: a -> *) :: a -> * where
66+ -- |Insert a new or update an existing key with the given value @v a@
67+ From_Insert :: v a -> From k v a
68+ -- |Delete the existing key
69+ From_Delete :: From k v a
70+ -- |Move the value from the given key @k a@ to this key. The source key should also have an entry in the patch giving the current key as @_nodeInfo_to@,
71+ -- usually but not necessarily with @From_Delete@.
72+ From_Move :: !(k a) -> From k v a
73+ deriving (Show, Read, Eq, Ord)
74+
75+-- |Type alias for the "to" part of a 'NodeInfo'. @'ComposeMaybe' ('Just' k)@ means the key is moving to another key, @ComposeMaybe Nothing@ for any other
76+-- operation.
77+type To = ComposeMaybe
78+
79+-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
80+validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
81+validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
82+
83+-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
84+validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
85+validationErrorsForPatchDMapWithMove m =
86+ noSelfMoves <> movesBalanced
87+ where
88+ noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
89+ selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
90+ selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
91+ selfMove _ = Nothing
92+ movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
93+ unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
94+ case DMap.lookup src m of
95+ Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
96+ Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
97+ if isNothing (dst' `geq` dst)
98+ then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
99+ else Nothing
100+ _ ->
101+ Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
102+ unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
103+ case DMap.lookup dst m of
104+ Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
105+ Just (NodeInfo (From_Move src') _) ->
106+ if isNothing (src' `geq` src)
107+ then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
108+ else Nothing
109+
110+ _ ->
111+ Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
112+ unbalancedMove _ = Nothing
113+
114+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
115+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
116+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
117+
118+-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
119+data Pair1 f g a = Pair1 (f a) (g a)
120+
121+-- |Helper data structure used for composing patches using the monoid instance.
122+data Fixup k v a
123+ = Fixup_Delete
124+ | Fixup_Update (These (From k v a) (To k a))
125+
126+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
127+instance GCompare k => Semigroup (PatchDMapWithMove k v) where
128+ PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
129+ where
130+ connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
131+ h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
132+ h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
133+ (Just toAfter, From_Move fromBefore)
134+ | isJust $ fromBefore `geq` toAfter
135+ -> [toAfter :=> Fixup_Delete]
136+ | otherwise
137+ -> [ toAfter :=> Fixup_Update (This editBefore)
138+ , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
139+ ]
140+ (Nothing, From_Move fromBefore) -> [fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
141+ (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
142+ (Nothing, _) -> []
143+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
144+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
145+ | This x <- a, That y <- b
146+ = Fixup_Update $ These x y
147+ | That y <- a, This x <- b
148+ = Fixup_Update $ These x y
149+ mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups"
150+ fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
151+ combineNodeInfos _ nia nib = NodeInfo
152+ { _nodeInfo_from = _nodeInfo_from nia
153+ , _nodeInfo_to = _nodeInfo_to nib
154+ }
155+ applyFixup _ ni = \case
156+ Fixup_Delete -> Nothing
157+ Fixup_Update u -> Just $ NodeInfo
158+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
159+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
160+ }
161+ m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
162+ getHere :: These a b -> Maybe a
163+ getHere = \case
164+ This a -> Just a
165+ These a _ -> Just a
166+ That _ -> Nothing
167+ getThere :: These a b -> Maybe b
168+ getThere = \case
169+ This _ -> Nothing
170+ These _ b -> Just b
171+ That b -> Just b
172+
173+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
174+instance GCompare k => Monoid (PatchDMapWithMove k v) where
175+ mempty = PatchDMapWithMove mempty
176+ mappend = (<>)
177+
178+{-
179+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
180+PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
181+ where
182+ getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
183+ removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
184+ removeRedundantDst _ a = Just a
185+ f :: forall a. k a -> From k v a -> Maybe (From k v a)
186+ f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
187+ From_Move fromKey -> getDstAction fromKey dstBefore
188+ nonMove -> nonMove
189+ dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
190+ getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
191+ removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
192+ removeRedundantSrc _ a = Just a
193+ g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
194+ g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
195+ ComposeMaybe Nothing -> ComposeMaybe Nothing
196+ ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
197+ src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
198+-}
199+
200+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @v a@ to the given key @k a@, like 'DMap.insert'.
201+insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
202+insertDMapKey k v =
203+ PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
204+
205+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to:
206+--
207+-- @
208+-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
209+-- @
210+moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
211+moveDMapKey src dst = case src `geq` dst of
212+ Nothing -> PatchDMapWithMove $ DMap.fromList
213+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
214+ , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
215+ ]
216+ Just _ -> mempty
217+
218+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
219+--
220+-- @
221+-- let aMay = DMap.lookup a dmap
222+-- bMay = DMap.lookup b dmap
223+-- in maybe id (DMap.insert a) (bMay <> aMay)
224+-- . maybe id (DMap.insert b) (aMay <> bMay)
225+-- . DMap.delete a . DMap.delete b $ dmap
226+-- @
227+swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
228+swapDMapKey src dst = case src `geq` dst of
229+ Nothing -> PatchDMapWithMove $ DMap.fromList
230+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
231+ , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
232+ ]
233+ Just _ -> mempty
234+
235+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'DMap.delete'.
236+deleteDMapKey :: k a -> PatchDMapWithMove k v
237+deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
238+
239+{-
240+k1, k2 :: Const2 Int () ()
241+k1 = Const2 1
242+k2 = Const2 2
243+p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity
244+p1 = moveDMapKey k1 k2
245+p2 = moveDMapKey k2 k1
246+p12 = p1 <> p2
247+p21 = p2 <> p1
248+p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2
249+p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1
250+
251+testPatchDMapWithMove = do
252+ print p1
253+ print p2
254+ print $ p12 == deleteDMapKey k1
255+ print $ p21 == deleteDMapKey k2
256+ print $ p12Slow == deleteDMapKey k1
257+ print $ p21Slow == deleteDMapKey k2
258+
259+dst (PatchDMapWithMove x _) = x
260+src (PatchDMapWithMove _ x) = x
261+-}
262+
263+-- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'.
264+unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
265+unPatchDMapWithMove (PatchDMapWithMove p) = p
266+
267+-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove', without checking any invariants.
268+--
269+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
270+unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
271+unsafePatchDMapWithMove = PatchDMapWithMove
272+
273+-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while checking invariants. If the invariants are satisfied, @Right p@ is returned
274+-- otherwise @Left errors@.
275+patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
276+patchDMapWithMove dm =
277+ case validationErrorsForPatchDMapWithMove dm of
278+ [] -> Right $ unsafePatchDMapWithMove dm
279+ errs -> Left errs
280+
281+-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
282+mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
283+mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
284+ DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
285+ where g :: forall a. From k v a -> From k v' a
286+ g = \case
287+ From_Insert v -> From_Insert $ f v
288+ From_Delete -> From_Delete
289+ From_Move k -> From_Move k
290+
291+-- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
292+traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
293+traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
294+
295+-- |Map an effectful function @forall a. k a -> v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
296+traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
297+traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
298+ where g :: forall a. k a -> From k v a -> m (From k v' a)
299+ g k = \case
300+ From_Insert v -> From_Insert <$> f k v
301+ From_Delete -> pure From_Delete
302+ From_Move fromKey -> pure $ From_Move fromKey
303+
304+-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
305+nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
306+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
307+
308+-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
309+nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
310+nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
311+
312+-- |Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys from @k a@ to @'Some' k@ and applying a given weakening function @v a -> v'@ to
313+-- values.
314+weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
315+weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
316+ where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
317+ g ni = MapWithMove.NodeInfo
318+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
319+ From_Insert v -> MapWithMove.From_Insert $ f v
320+ From_Delete -> MapWithMove.From_Delete
321+ From_Move k -> MapWithMove.From_Move $ mkSome k
322+ , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
323+ }
324+
325+-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
326+-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
327+patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v'
328+patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
329+ where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
330+ g ni = MapWithMove.NodeInfo
331+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
332+ From_Insert v -> MapWithMove.From_Insert $ f v
333+ From_Delete -> MapWithMove.From_Delete
334+ From_Move (Const2 k) -> MapWithMove.From_Move k
335+ , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
336+ }
337+
338+-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
339+-- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@.
340+-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
341+const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v'
342+const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
343+ where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
344+ g (k, ni) = Const2 k :=> NodeInfo
345+ { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
346+ MapWithMove.From_Insert v -> From_Insert $ f v
347+ MapWithMove.From_Delete -> From_Delete
348+ MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
349+ , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
350+ }
351+
352+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
353+instance GCompare k => Patch (PatchDMapWithMove k v) where
354+ type PatchTarget (PatchDMapWithMove k v) = DMap k v
355+ apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
356+ where insertions = DMap.mapMaybeWithKey insertFunc p
357+ insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
358+ insertFunc _ ni = case _nodeInfo_from ni of
359+ From_Insert v -> Just v
360+ From_Move k -> DMap.lookup k old
361+ From_Delete -> Nothing
362+ deletions = DMap.mapMaybeWithKey deleteFunc p
363+ deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
364+ deleteFunc _ ni = case _nodeInfo_from ni of
365+ From_Delete -> Just $ Constant ()
366+ _ -> Nothing
367+
368+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
369+getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
370+getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
371+ where f _ v ni = Pair v $ _nodeInfo_to ni
src/Data/Patch/IntMap.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE StandaloneDeriving #-}
7+{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
11+-- insert/update or delete of associations.
12+module Data.Patch.IntMap where
13+
14+import Control.Lens
15+import Data.IntMap.Strict (IntMap)
16+import qualified Data.IntMap.Strict as IntMap
17+import Data.Maybe
18+import Data.Monoid.DecidablyEmpty
19+#if !MIN_VERSION_base(4,11,0)
20+import Data.Semigroup (Semigroup (..))
21+#endif
22+import Data.Patch.Class
23+
24+-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
25+-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
26+-- and @Nothing@ means delete.
27+newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
28+ deriving ( Show, Read, Eq, Ord
29+ , Functor, Foldable, Traversable
30+ , Monoid, DecidablyEmpty
31+ )
32+
33+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
34+-- If the same key is modified by both patches, the one on the left will take
35+-- precedence.
36+deriving instance Semigroup (PatchIntMap v)
37+
38+makeWrapped ''PatchIntMap
39+
40+-- | Apply the insertions or deletions to a given 'IntMap'.
41+instance Patch (PatchIntMap a) where
42+ type PatchTarget (PatchIntMap a) = IntMap a
43+ apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
44+ let removes = IntMap.filter isNothing p
45+ adds = IntMap.mapMaybe id p
46+ in IntMap.union adds $ v `IntMap.difference` removes
47+
48+instance FunctorWithIndex Int PatchIntMap
49+instance FoldableWithIndex Int PatchIntMap
50+instance TraversableWithIndex Int PatchIntMap where
51+ itraverse = itraversed . Indexed
52+ itraversed = _Wrapped .> itraversed <. traversed
53+
54+-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
55+-- (that is, all inserts/updates), producing a @PatchIntMap b@.
56+mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
57+mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
58+
59+-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
60+-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
61+traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
62+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
63+
64+-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
65+patchIntMapNewElements :: PatchIntMap a -> [a]
66+patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
67+
68+-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
69+-- the inserts/updates in the given patch.
70+patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
71+patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
72+
73+-- | Subset the given @'IntMap' a@ to contain only the keys that would be
74+-- deleted by the given @'PatchIntMap' a@.
75+getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
76+getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Data/Patch/Map.hsnew file+72
@@ -0,0 +1,72 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TemplateHaskell #-}
9+{-# LANGUAGE TypeFamilies #-}
10+
11+-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
12+-- and deletions
13+module Data.Patch.Map where
14+
15+import Data.Patch.Class
16+
17+import Control.Lens
18+import Data.Map (Map)
19+import qualified Data.Map as Map
20+import Data.Maybe
21+import Data.Monoid.DecidablyEmpty
22+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
23+
24+-- | A set of changes to a 'Map'. Any element may be inserted/updated or
25+-- deleted. Insertions are represented as values wrapped in 'Just', while
26+-- deletions are represented as 'Nothing's
27+newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
28+ deriving ( Show, Read, Eq, Ord
29+ , Foldable, Traversable
30+ , DecidablyEmpty
31+ )
32+
33+-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
34+-- Deletions are unaffected.
35+deriving instance Functor (PatchMap k)
36+-- | The empty 'PatchMap' contains no insertions or deletions
37+deriving instance Ord k => Monoid (PatchMap k v)
38+
39+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
40+-- If the same key is modified by both patches, the one on the left will take
41+-- precedence.
42+instance Ord k => Semigroup (PatchMap k v) where
43+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
44+ -- PatchMap is idempotent, so stimes n is id for every n
45+ stimes = stimesIdempotentMonoid
46+
47+-- | Apply the insertions or deletions to a given 'Map'.
48+instance Ord k => Patch (PatchMap k v) where
49+ type PatchTarget (PatchMap k v) = Map k v
50+ {-# INLINABLE apply #-}
51+ apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
52+ where insertions = Map.mapMaybeWithKey (const id) p
53+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
54+ nothingToJust = \case
55+ Nothing -> Just ()
56+ Just _ -> Nothing
57+
58+instance FunctorWithIndex k (PatchMap k)
59+instance FoldableWithIndex k (PatchMap k)
60+instance TraversableWithIndex k (PatchMap k) where
61+ itraverse = itraversed . Indexed
62+ itraversed = _Wrapped .> itraversed <. traversed
63+
64+-- | Returns all the new elements that will be added to the 'Map'
65+patchMapNewElements :: PatchMap k v -> [v]
66+patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
67+
68+-- | Returns all the new elements that will be added to the 'Map'
69+patchMapNewElementsMap :: PatchMap k v -> Map k v
70+patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71+
72+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hsnew file+312
@@ -0,0 +1,312 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PatternGuards #-}
9+{-# LANGUAGE PatternSynonyms #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE StandaloneDeriving #-}
12+{-# LANGUAGE TemplateHaskell #-}
13+{-# LANGUAGE TypeApplications #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE ViewPatterns #-}
16+
17+-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
18+-- another
19+module Data.Patch.MapWithMove
20+ ( PatchMapWithMove
21+ ( PatchMapWithMove
22+ , unPatchMapWithMove
23+ , ..
24+ )
25+ , patchMapWithMove
26+ , patchMapWithMoveInsertAll
27+ , insertMapKey
28+ , moveMapKey
29+ , swapMapKey
30+ , deleteMapKey
31+ , unsafePatchMapWithMove
32+ , patchMapWithMoveNewElements
33+ , patchMapWithMoveNewElementsMap
34+ , patchThatSortsMapWith
35+ , patchThatChangesAndSortsMapWith
36+ , patchThatChangesMap
37+
38+ -- * Node Info
39+ , NodeInfo
40+ ( NodeInfo
41+ , _nodeInfo_to
42+ , _nodeInfo_from
43+ , ..
44+ )
45+ , bitraverseNodeInfo
46+ , nodeInfoMapFrom
47+ , nodeInfoMapMFrom
48+ , nodeInfoSetTo
49+
50+ -- * From
51+ , From
52+ ( From_Insert
53+ , From_Delete
54+ , From_Move
55+ , ..
56+ )
57+ , bitraverseFrom
58+
59+ -- * To
60+ , To
61+ ) where
62+
63+import Data.Coerce
64+import Data.Kind (Type)
65+import Data.Patch.Class
66+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67+import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
68+
69+import Control.Lens
70+import Data.List
71+import Data.Map (Map)
72+import qualified Data.Map as Map
73+import Data.Proxy
74+#if !MIN_VERSION_base(4,11,0)
75+import Data.Semigroup (Semigroup (..))
76+#endif
77+import Data.Traversable (foldMapDefault)
78+
79+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
80+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
81+-- and vice versa. There should never be any unpaired From/To keys.
82+newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
83+ { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
84+ unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
85+ }
86+ deriving ( Show, Read, Eq, Ord
87+-- Haddock cannot handle documentation here before GHC 8.6
88+ ,
89+#if __GLASGOW_HASKELL__ >= 806
90+ -- | Compose patches having the same effect as applying the
91+ -- patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p .
92+ -- 'applyAlways' q@
93+#endif
94+ Semigroup
95+ , Monoid
96+ )
97+
98+pattern Coerce :: Coercible a b => a -> b
99+pattern Coerce x <- (coerce -> x)
100+ where Coerce x = coerce x
101+
102+{-# COMPLETE PatchMapWithMove #-}
103+pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
104+-- | Extract the representation of the 'PatchMapWithMove' as a map of
105+-- 'NodeInfo'.
106+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
107+pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
108+
109+_PatchMapWithMove
110+ :: Iso
111+ (PatchMapWithMove k0 v0)
112+ (PatchMapWithMove k1 v1)
113+ (Map k0 (NodeInfo k0 v0))
114+ (Map k1 (NodeInfo k1 v1))
115+_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
116+
117+instance Functor (PatchMapWithMove k) where
118+ fmap f = runIdentity . traverse (Identity . f)
119+
120+instance Foldable (PatchMapWithMove k) where
121+ foldMap = foldMapDefault
122+
123+instance Traversable (PatchMapWithMove k) where
124+ traverse =
125+ _PatchMapWithMove .
126+ traverse .
127+ traverse
128+
129+instance FunctorWithIndex k (PatchMapWithMove k)
130+instance FoldableWithIndex k (PatchMapWithMove k)
131+instance TraversableWithIndex k (PatchMapWithMove k) where
132+ itraverse = itraversed . Indexed
133+ itraversed =
134+ _PatchMapWithMove .>
135+ itraversed <.
136+ traverse
137+
138+-- | Create a 'PatchMapWithMove', validating it
139+patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
140+patchMapWithMove = fmap PatchMapWithMove' . PM.patchMapWithPatchingMove . coerce
141+
142+-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
143+patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
144+patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
145+
146+-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
147+insertMapKey :: k -> v -> PatchMapWithMove k v
148+insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
149+
150+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
151+--
152+-- @
153+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
154+-- @
155+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
156+moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
157+
158+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
159+--
160+-- @
161+-- let aMay = Map.lookup a map
162+-- bMay = Map.lookup b map
163+-- in maybe id (Map.insert a) (bMay <> aMay)
164+-- . maybe id (Map.insert b) (aMay <> bMay)
165+-- . Map.delete a . Map.delete b $ map
166+-- @
167+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
168+swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
169+
170+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
171+deleteMapKey :: k -> PatchMapWithMove k v
172+deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
173+
174+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
175+--
176+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
177+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
178+unsafePatchMapWithMove = coerce PM.unsafePatchMapWithPatchingMove
179+
180+-- | Apply the insertions, deletions, and moves to a given 'Map'
181+instance Ord k => Patch (PatchMapWithMove k v) where
182+ type PatchTarget (PatchMapWithMove k v) = Map k v
183+ apply (PatchMapWithMove' p) = apply p
184+
185+-- | Returns all the new elements that will be added to the 'Map'.
186+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
187+patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
188+
189+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
190+patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
191+patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
192+
193+-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
194+-- its values using the given ordering function. The set keys of the 'Map' is
195+-- not changed.
196+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
197+patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
198+
199+-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
200+-- will produce a 'Map' with the same values as the second 'Map' but with the
201+-- values sorted with the given ordering function.
202+patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
203+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
204+ where newList = Map.toList newByIndexUnsorted
205+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
206+
207+-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
208+-- will produce the second 'Map'.
209+patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
210+patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
211+ PM.patchThatChangesMap oldByIndex newByIndex
212+
213+--
214+-- NodeInfo
215+--
216+
217+-- | Holds the information about each key: where its new value should come from,
218+-- and where its old value should go to
219+newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
220+
221+deriving instance (Show k, Show p) => Show (NodeInfo k p)
222+deriving instance (Read k, Read p) => Read (NodeInfo k p)
223+deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
224+deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
225+
226+{-# COMPLETE NodeInfo #-}
227+pattern NodeInfo :: To k -> From k v -> NodeInfo k v
228+_nodeInfo_to :: NodeInfo k v -> To k
229+_nodeInfo_from :: NodeInfo k v -> From k v
230+pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
231+ PM.NodeInfo
232+ { PM._nodeInfo_to = _nodeInfo_to
233+ , PM._nodeInfo_from = Coerce _nodeInfo_from
234+ }
235+
236+_NodeInfo
237+ :: Iso
238+ (NodeInfo k0 v0)
239+ (NodeInfo k1 v1)
240+ (PM.NodeInfo k0 (Proxy v0))
241+ (PM.NodeInfo k1 (Proxy v1))
242+_NodeInfo = iso unNodeInfo' NodeInfo'
243+
244+instance Functor (NodeInfo k) where
245+ fmap f = runIdentity . traverse (Identity . f)
246+
247+instance Foldable (NodeInfo k) where
248+ foldMap = foldMapDefault
249+
250+instance Traversable (NodeInfo k) where
251+ traverse = bitraverseNodeInfo pure
252+
253+bitraverseNodeInfo
254+ :: Applicative f
255+ => (k0 -> f k1)
256+ -> (v0 -> f v1)
257+ -> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
258+bitraverseNodeInfo fk fv = fmap NodeInfo'
259+ . PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
260+ . coerce
261+
262+-- | Change the 'From' value of a 'NodeInfo'
263+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
264+nodeInfoMapFrom f = coerce $ PM.nodeInfoMapFrom (unFrom' . f . From')
265+
266+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
267+-- 'Applicative', 'Monad', etc.) action to get the new value
268+nodeInfoMapMFrom
269+ :: Functor f
270+ => (From k v -> f (From k v))
271+ -> NodeInfo k v -> f (NodeInfo k v)
272+nodeInfoMapMFrom f = fmap NodeInfo'
273+ . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
274+ . coerce
275+
276+-- | Set the 'To' field of a 'NodeInfo'
277+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
278+nodeInfoSetTo = coerce . PM.nodeInfoSetTo
279+
280+--
281+-- From
282+--
283+
284+-- | Describe how a key's new value should be produced
285+newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
286+
287+{-# COMPLETE From_Insert, From_Delete, From_Move #-}
288+
289+-- | Insert the given value here
290+pattern From_Insert :: v -> From k v
291+pattern From_Insert v = From' (PM.From_Insert v)
292+
293+-- | Delete the existing value, if any, from here
294+pattern From_Delete :: From k v
295+pattern From_Delete = From' PM.From_Delete
296+
297+-- | Move the value here from the given key
298+pattern From_Move :: k -> From k v
299+pattern From_Move k = From' (PM.From_Move k Proxy)
300+
301+bitraverseFrom
302+ :: Applicative f
303+ => (k0 -> f k1)
304+ -> (v0 -> f v1)
305+ -> From k0 v0 -> f (From k1 v1)
306+bitraverseFrom fk fv = fmap From'
307+ . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
308+ . coerce
309+
310+makeWrapped ''PatchMapWithMove
311+makeWrapped ''NodeInfo
312+makeWrapped ''From
src/Data/Patch/MapWithPatchingMove.hsnew file+432
@@ -0,0 +1,432 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE StandaloneDeriving #-}
10+{-# LANGUAGE TemplateHaskell #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+
14+-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15+-- another
16+module Data.Patch.MapWithPatchingMove
17+ ( PatchMapWithPatchingMove (..)
18+ , patchMapWithPatchingMove
19+ , patchMapWithPatchingMoveInsertAll
20+ , insertMapKey
21+ , moveMapKey
22+ , swapMapKey
23+ , deleteMapKey
24+ , unsafePatchMapWithPatchingMove
25+ , patchMapWithPatchingMoveNewElements
26+ , patchMapWithPatchingMoveNewElementsMap
27+ , patchThatSortsMapWith
28+ , patchThatChangesAndSortsMapWith
29+ , patchThatChangesMap
30+
31+ -- * Node Info
32+ , NodeInfo (..)
33+ , bitraverseNodeInfo
34+ , nodeInfoMapFrom
35+ , nodeInfoMapMFrom
36+ , nodeInfoSetTo
37+
38+ -- * From
39+ , From(..)
40+ , bitraverseFrom
41+
42+ -- * To
43+ , To
44+
45+ -- TODO internals module
46+ , Fixup (..)
47+ ) where
48+
49+import Data.Patch.Class
50+
51+import Control.Lens hiding (from, to)
52+import Control.Lens.TH (makeWrapped)
53+import Data.Align (align)
54+import Data.Foldable (toList)
55+import Data.Function
56+import Data.List
57+import Data.Map (Map)
58+import qualified Data.Map as Map
59+import Data.Maybe
60+#if !MIN_VERSION_base(4,11,0)
61+import Data.Semigroup (Semigroup (..))
62+#endif
63+import Data.Monoid.DecidablyEmpty
64+import Data.Set (Set)
65+import qualified Data.Set as Set
66+import Data.These (These (..))
67+
68+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
69+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
70+-- and vice versa. There should never be any unpaired From/To keys.
71+newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
72+ { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
73+ unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
74+ }
75+
76+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
77+deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
78+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
79+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
80+
81+deriving instance ( Ord k
82+#if !MIN_VERSION_base(4,11,0)
83+ , Semigroup p
84+#endif
85+ , DecidablyEmpty p
86+ , Patch p
87+ ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
88+
89+-- | Create a 'PatchMapWithPatchingMove', validating it
90+patchMapWithPatchingMove
91+ :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
92+patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
93+ where valid = forwardLinks == backwardLinks
94+ forwardLinks = Map.mapMaybe _nodeInfo_to m
95+ backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
96+ case _nodeInfo_from p of
97+ From_Move from _ -> Just (from, to)
98+ _ -> Nothing
99+
100+-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
101+patchMapWithPatchingMoveInsertAll
102+ :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
103+patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
104+ { _nodeInfo_from = From_Insert v
105+ , _nodeInfo_to = Nothing
106+ }
107+
108+-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109+insertMapKey
110+ :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
111+insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
112+
113+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
114+--
115+-- @
116+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
117+-- @
118+moveMapKey
119+ :: ( DecidablyEmpty p
120+#if !MIN_VERSION_base(4,11,0)
121+ , Semigroup p
122+#endif
123+ , Patch p
124+ )
125+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
126+moveMapKey src dst
127+ | src == dst = mempty
128+ | otherwise =
129+ PatchMapWithPatchingMove $ Map.fromList
130+ [ (dst, NodeInfo (From_Move src mempty) Nothing)
131+ , (src, NodeInfo From_Delete (Just dst))
132+ ]
133+
134+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
135+--
136+-- @
137+-- let aMay = Map.lookup a map
138+-- bMay = Map.lookup b map
139+-- in maybe id (Map.insert a) (bMay <> aMay)
140+-- . maybe id (Map.insert b) (aMay <> bMay)
141+-- . Map.delete a . Map.delete b $ map
142+-- @
143+swapMapKey
144+ :: ( DecidablyEmpty p
145+#if !MIN_VERSION_base(4,11,0)
146+ , Semigroup p
147+#endif
148+ , Patch p
149+ )
150+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
151+swapMapKey src dst
152+ | src == dst = mempty
153+ | otherwise =
154+ PatchMapWithPatchingMove $ Map.fromList
155+ [ (dst, NodeInfo (From_Move src mempty) (Just src))
156+ , (src, NodeInfo (From_Move dst mempty) (Just dst))
157+ ]
158+
159+-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
160+-- the mapping, equivalent to 'Map.delete'.
161+deleteMapKey
162+ :: k -> PatchMapWithPatchingMove k v
163+deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
164+
165+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
166+--
167+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
168+unsafePatchMapWithPatchingMove
169+ :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
170+unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
171+
172+-- | Apply the insertions, deletions, and moves to a given 'Map'
173+instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
174+ type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
175+ -- TODO: return Nothing sometimes
176+ -- Note: the strict application here is critical to ensuring that incremental
177+ -- merges don't hold onto all their prerequisite events forever; can we make
178+ -- this more robust?
179+ apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
180+ where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
181+ From_Insert v -> Just v
182+ From_Move k p -> applyAlways p <$> Map.lookup k old
183+ From_Delete -> Nothing
184+ deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
185+ From_Delete -> Just ()
186+ _ -> Nothing
187+
188+-- | Returns all the new elements that will be added to the 'Map'
189+patchMapWithPatchingMoveNewElements
190+ :: PatchMapWithPatchingMove k p -> [PatchTarget p]
191+patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
192+
193+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
194+patchMapWithPatchingMoveNewElementsMap
195+ :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
196+patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
197+ where f ni = case _nodeInfo_from ni of
198+ From_Insert v -> Just v
199+ From_Move _ _ -> Nothing
200+ From_Delete -> Nothing
201+
202+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
203+-- its values using the given ordering function. The set keys of the 'Map' is
204+-- not changed.
205+patchThatSortsMapWith
206+ :: (Ord k, Monoid p)
207+ => (PatchTarget p -> PatchTarget p -> Ordering)
208+ -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
209+patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
210+ where unsorted = Map.toList m
211+ sorted = sortBy (cmp `on` snd) unsorted
212+ f (to, _) (from, _) = if to == from then Nothing else
213+ Just (from, to)
214+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
215+ g (to, _) (from, _) = if to == from then Nothing else
216+ let Just movingTo = Map.lookup from reverseMapping
217+ in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
218+
219+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
220+-- will produce a 'Map' with the same values as the second 'Map' but with the
221+-- values sorted with the given ordering function.
222+patchThatChangesAndSortsMapWith
223+ :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
224+ => (PatchTarget p -> PatchTarget p -> Ordering)
225+ -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
226+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
227+ where newList = Map.toList newByIndexUnsorted
228+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
229+
230+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
231+-- will produce the second 'Map'.
232+-- Note: this will never produce a patch on a value.
233+patchThatChangesMap
234+ :: forall k p
235+ . (Ord k, Ord (PatchTarget p), Monoid p)
236+ => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
237+patchThatChangesMap oldByIndex newByIndex = patch
238+ where invert :: Map k (PatchTarget p) -> Map (PatchTarget p) (Set k)
239+ invert = Map.fromListWith (<>) . fmap (\(k, v) -> (v, Set.singleton k)) . Map.toList
240+ -- In the places where we use unionDistinct, a non-distinct key indicates a bug in this function
241+ unionDistinct :: forall k' v'. Ord k' => Map k' v' -> Map k' v' -> Map k' v'
242+ unionDistinct = Map.unionWith (error "patchThatChangesMap: non-distinct keys")
243+ unionPairDistinct :: (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k))
244+ unionPairDistinct (oldFroms, oldTos) (newFroms, newTos) = (unionDistinct oldFroms newFroms, unionDistinct oldTos newTos)
245+ -- Generate patch info for a single value
246+ -- Keys that are found in both the old and new sets will not be patched
247+ -- Keys that are found in only the old set will be moved to a new position if any are available; otherwise they will be deleted
248+ -- Keys that are found in only the new set will be populated by moving an old key if any are available; otherwise they will be inserted
249+ patchSingleValue :: PatchTarget p -> Set k -> Set k -> (Map k (From k p), Map k (To k))
250+ patchSingleValue v oldKeys newKeys = foldl' unionPairDistinct mempty $ align (toList $ oldKeys `Set.difference` newKeys) (toList $ newKeys `Set.difference` oldKeys) <&> \case
251+ This oldK -> (mempty, Map.singleton oldK Nothing) -- There's nowhere for this value to go, so we know we are deleting it
252+ That newK -> (Map.singleton newK $ From_Insert v, mempty) -- There's nowhere fo this value to come from, so we know we are inserting it
253+ These oldK newK -> (Map.singleton newK $ From_Move oldK mempty, Map.singleton oldK $ Just newK)
254+ -- Run patchSingleValue on a These. Missing old or new sets are considered empty
255+ patchSingleValueThese :: PatchTarget p -> These (Set k) (Set k) -> (Map k (From k p), Map k (To k))
256+ patchSingleValueThese v = \case
257+ This oldKeys -> patchSingleValue v oldKeys mempty
258+ That newKeys -> patchSingleValue v mempty newKeys
259+ These oldKeys newKeys -> patchSingleValue v oldKeys newKeys
260+ -- Generate froms and tos for all values, then merge them together
261+ (froms, tos) = foldl' unionPairDistinct mempty $ Map.mapWithKey patchSingleValueThese $ align (invert oldByIndex) (invert newByIndex)
262+ patch = unsafePatchMapWithPatchingMove $ align froms tos <&> \case
263+ This from -> NodeInfo from Nothing -- Since we don't have a 'to' record for this key, that must mean it isn't being moved anywhere, so it should be deleted.
264+ That to -> NodeInfo From_Delete to -- Since we don't have a 'from' record for this key, it must be getting deleted
265+ These from to -> NodeInfo from to
266+
267+--
268+-- NodeInfo
269+--
270+
271+-- | Holds the information about each key: where its new value should come from,
272+-- and where its old value should go to
273+data NodeInfo k p = NodeInfo
274+ { _nodeInfo_from :: !(From k p)
275+ -- ^ Where do we get the new value for this key?
276+ , _nodeInfo_to :: !(To k)
277+ -- ^ If the old value is being kept (i.e. moved rather than deleted or
278+ -- replaced), where is it going?
279+ }
280+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
281+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
282+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
283+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
284+
285+-- | Traverse the 'NodeInfo' over the key, patch, and patch target. Because of
286+-- the type families here, this doesn't it any bi- or tri-traversal class.
287+bitraverseNodeInfo
288+ :: Applicative f
289+ => (k0 -> f k1)
290+ -> (p0 -> f p1)
291+ -> (PatchTarget p0 -> f (PatchTarget p1))
292+ -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
293+bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
294+ <$> bitraverseFrom fk fp fpt from
295+ <*> traverse fk to
296+
297+-- | Change the 'From' value of a 'NodeInfo'
298+nodeInfoMapFrom
299+ :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
300+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
301+
302+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
303+-- 'Applicative', 'Monad', etc.) action to get the new value
304+nodeInfoMapMFrom
305+ :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
306+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
307+
308+-- | Set the 'To' field of a 'NodeInfo'
309+nodeInfoSetTo
310+ :: To k -> NodeInfo k v -> NodeInfo k v
311+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
312+
313+--
314+-- From
315+--
316+
317+-- | Describe how a key's new value should be produced
318+data From k p
319+ = From_Insert (PatchTarget p) -- ^ Insert the given value here
320+ | From_Delete -- ^ Delete the existing value, if any, from here
321+ | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
322+
323+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
324+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
325+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
326+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
327+
328+-- | Traverse the 'From' over the key, patch, and patch target. Because of
329+-- the type families here, this doesn't it any bi- or tri-traversal class.
330+bitraverseFrom
331+ :: Applicative f
332+ => (k0 -> f k1)
333+ -> (p0 -> f p1)
334+ -> (PatchTarget p0 -> f (PatchTarget p1))
335+ -> From k0 p0 -> f (From k1 p1)
336+bitraverseFrom fk fp fpt = \case
337+ From_Insert pt -> From_Insert <$> fpt pt
338+ From_Delete -> pure From_Delete
339+ From_Move k p -> From_Move <$> fk k <*> fp p
340+
341+--
342+-- To
343+--
344+
345+-- | Describe where a key's old value will go. If this is 'Just', that means
346+-- the key's old value will be moved to the given other key; if it is 'Nothing',
347+-- that means it will be deleted.
348+type To = Maybe
349+
350+--
351+-- Fixup
352+--
353+
354+-- | Helper data structure used for composing patches using the monoid instance.
355+data Fixup k v
356+ = Fixup_Delete
357+ | Fixup_Update (These (From k v) (To k))
358+
359+-- | Compose patches having the same effect as applying the patches in turn:
360+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
361+instance ( Ord k
362+#if !MIN_VERSION_base(4,11,0)
363+ , Semigroup p
364+#endif
365+ , DecidablyEmpty p
366+ , Patch p
367+ ) => Semigroup (PatchMapWithPatchingMove k p) where
368+ PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
369+ where
370+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
371+ h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
372+ h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
373+ (Just toAfter, From_Move fromBefore p)
374+ | fromBefore == toAfter && isEmpty p
375+ -> [(toAfter, Fixup_Delete)]
376+ | otherwise
377+ -> [ (toAfter, Fixup_Update (This editBefore))
378+ , (fromBefore, Fixup_Update (That mToAfter))
379+ ]
380+ (Nothing, From_Move fromBefore _) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
381+ (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
382+ (Nothing, _) -> []
383+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
384+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
385+ | This x <- a, That y <- b
386+ = Fixup_Update $ These x y
387+ | That y <- a, This x <- b
388+ = Fixup_Update $ These x y
389+ mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
390+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
391+ combineNodeInfos _ nia nib = NodeInfo
392+ { _nodeInfo_from = _nodeInfo_from nia
393+ , _nodeInfo_to = _nodeInfo_to nib
394+ }
395+ applyFixup _ ni = \case
396+ Fixup_Delete -> Nothing
397+ Fixup_Update u -> Just $ NodeInfo
398+ { _nodeInfo_from = case _nodeInfo_from ni of
399+ f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
400+ Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
401+ Just (From_Insert v) -> From_Insert $ applyAlways p' v
402+ Just From_Delete -> From_Delete
403+ Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
404+ _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
405+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
406+ }
407+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
408+ getHere :: These a b -> Maybe a
409+ getHere = \case
410+ This a -> Just a
411+ These a _ -> Just a
412+ That _ -> Nothing
413+ getThere :: These a b -> Maybe b
414+ getThere = \case
415+ This _ -> Nothing
416+ These _ b -> Just b
417+ That b -> Just b
418+
419+--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
420+-- | Compose patches having the same effect as applying the patches in turn:
421+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
422+instance ( Ord k
423+#if !MIN_VERSION_base(4,11,0)
424+ , Semigroup p
425+#endif
426+ , DecidablyEmpty p
427+ , Patch p
428+ ) => Monoid (PatchMapWithPatchingMove k p) where
429+ mempty = PatchMapWithPatchingMove mempty
430+ mappend = (<>)
431+
432+makeWrapped ''PatchMapWithPatchingMove
src/Data/Semigroup/Additive.hsnew file+54
@@ -0,0 +1,54 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeOperators #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- |
5+-- Module:
6+-- Data.Semigroup.Additive
7+-- Description:
8+-- This module defines a class for commutative semigroups, until it is moved
9+-- to another library.
10+module Data.Semigroup.Additive
11+ ( Additive
12+ ) where
13+
14+import Data.Functor.Const (Const (..))
15+import Data.Functor.Identity
16+import Data.Proxy
17+#if !MIN_VERSION_base(4,12,0)
18+-- for :*: and :.: semigroup instances
19+import Data.Orphans ()
20+#endif
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
23+#endif
24+import GHC.Generics
25+
26+-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
27+class Semigroup q => Additive q where
28+
29+-- | Trivial additive semigroup.
30+instance Additive ()
31+
32+-- | Product additive semigroup.
33+-- A Pair of additive semigroups gives rise to a additive semigroup
34+instance (Additive a, Additive b) => Additive (a, b)
35+
36+-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
37+-- Base does not define Monoid (Compose f g a) so this is the best we can
38+-- really do for functor composition.
39+instance Additive (f (g a)) => Additive ((f :.: g) a)
40+
41+-- | Product of additive semigroups, Functor style.
42+instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
43+
44+-- | Trivial additive semigroup, Functor style
45+instance Additive (Proxy x)
46+
47+-- | Const lifts additive semigroups into a functor.
48+instance Additive a => Additive (Const a x)
49+
50+-- | Identity lifts additive semigroups pointwise (at only one point)
51+instance Additive a => Additive (Identity a)
52+
53+-- | Functions lift additive semigroups pointwise.
54+instance Additive b => Additive (a -> b)
src/Data/WeakBag.hsdeleted−82
@@ -1,82 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−module Data.WeakBag (WeakBag, WeakBagTicket, empty, singleton, insert, Data.WeakBag.traverse, remove) where
3−
4−import Control.Concurrent.STM
5−import Control.Exception
6−import Control.Monad hiding (forM_, mapM_)
7−import Control.Monad.IO.Class
8−import Data.Foldable (forM_, mapM_)
9−import Data.IORef
10−import System.Mem.Weak
11−import Data.IntMap.Strict (IntMap)
12−import qualified Data.IntMap.Strict as IntMap
13−
14−data WeakBag a = WeakBag
15− { _weakBag_nextId :: {-# UNPACK #-} !(TVar Int) --TODO: what if this wraps around?
16− , _weakBag_children :: {-# UNPACK #-} !(TVar (IntMap (Weak a)))
17− }
18−
19−data WeakBagTicket a = WeakBagTicket
20− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
21− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
22− }
23−
24−{-# INLINE insert #-}
25−insert :: a -> WeakBag a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBagTicket a)
26−insert a (WeakBag nextId children) wbRef finalizer = do
27− a' <- evaluate a
28− wbRef' <- evaluate wbRef
29− myId <- atomically $ do
30− myId <- readTVar nextId
31− writeTVar nextId $! succ myId
32− return myId
33− let cleanup = do
34− wb <- readIORef wbRef'
35− mb <- deRefWeak wb
36− forM_ mb $ \b -> do
37− isLastNode <- atomically $ do --TODO: Should this run even when mb is Nothing?
38− cs <- readTVar children
39− let csWithoutMe = IntMap.delete myId cs
40− writeTVar children $! csWithoutMe
41− return $ IntMap.size csWithoutMe == 0
42− when isLastNode $ finalizer b
43− return ()
44− return ()
45− wa <- mkWeakPtr a' $ Just cleanup
46− atomically $ modifyTVar' children $ IntMap.insert myId wa
47− return $ WeakBagTicket
48− { _weakBagTicket_weakItem = wa
49− , _weakBagTicket_item = a'
50− }
51−
52−{-# INLINE empty #-}
53−empty :: IO (WeakBag a)
54−empty = do
55− nextId <- newTVarIO 1
56− children <- newTVarIO IntMap.empty
57− let bag = WeakBag
58− { _weakBag_nextId = nextId
59− , _weakBag_children = children
60− }
61− return bag
62−
63−{-# INLINE singleton #-}
64−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket a)
65−singleton a wbRef finalizer = do
66− bag <- empty
67− ticket <- insert a bag wbRef finalizer
68− return (bag, ticket)
69−
70−{-# INLINE traverse #-}
71−-- | Visit every node in the given list. If new nodes are appended during the traversal, they will not be visited.
72−-- Every live node that was in the list when the traversal began will be visited exactly once; however, no guarantee is made about the order of the traversal.
73−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
74−traverse (WeakBag _ children) f = do
75− cs <- liftIO $ readTVarIO children
76− forM_ cs $ \c -> do
77− ma <- liftIO $ deRefWeak c
78− mapM_ f ma
79−
80−{-# INLINE remove #-}
81−remove :: WeakBagTicket a -> IO ()
82−remove = finalize . _weakBagTicket_weakItem
src/Reflex.hsdeleted−12
@@ -1,12 +0,0 @@
1−module Reflex ( module Reflex.Class
2− , module Reflex.Dynamic
3− , module Reflex.Dynamic.Nub
4− , module Reflex.Dynamic.TH
5− , module Reflex.Spider
6− ) where
7−
8−import Reflex.Class
9−import Reflex.Dynamic
10−import Reflex.Dynamic.Nub
11−import Reflex.Dynamic.TH
12−import Reflex.Spider
src/Reflex/Class.hsdeleted−560
@@ -1,560 +0,0 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, CPP #-}
2−module Reflex.Class where
3−
4−import Control.Applicative
5−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
6−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
7−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
8−import Control.Monad.Trans.Writer (WriterT())
9−import Control.Monad.Trans.Except (ExceptT())
10−import Control.Monad.Trans.Cont (ContT())
11−import Control.Monad.Trans.RWS (RWST())
12−import Data.List.NonEmpty (NonEmpty (..))
13−import Data.These
14−import Data.Align
15−import Data.GADT.Compare (GEq (..), (:~:) (..))
16−import Data.GADT.Show (GShow (..))
17−import Data.Dependent.Sum (ShowTag (..))
18−import Data.Map (Map)
19−import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
20−import qualified Data.Dependent.Map as DMap
21−import Data.Functor.Misc
22−import Data.Semigroup (Semigroup, (<>), sconcat, stimes, stimesIdempotentMonoid)
23−import Data.Traversable
24−import Data.Functor.Compose
25−import Data.Functor.Constant
26−import Data.Foldable
27−import Data.Maybe
28−import Data.Bifunctor
29−
30−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
31−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
32−
33−import Debug.Trace (trace)
34−
35−class (MonadHold t (PushM t), MonadSample t (PullM t), MonadFix (PushM t), Functor (Event t), Functor (Behavior t), Functor (Dynamic t), Monad (Dynamic t)) => Reflex t where
36− -- | A container for a value that can change over time. Behaviors can be sampled at will, but it is not possible to be notified when they change
37− data Behavior t :: * -> *
38− -- | A stream of occurrences. During any given frame, an Event is either occurring or not occurring; if it is occurring, it will contain a value of the given type (its "occurrence type")
39− data Event t :: * -> *
40− -- | A container for a value that can change over time and allows notifications on changes.
41− -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
42− -- change if and only if the Event fires.
43− data Dynamic t :: * -> *
44− -- | A container for a value that can change over time and allows notifications on changes.
45− -- Basically a combination of a 'Behavior' and an 'Event', with a rule that the Behavior will
46− -- change if and only if the Event fires.
47− data Incremental t :: (* -> *) -> * -> *
48− -- | An Event with no occurrences
49− never :: Event t a
50− -- | Create a Behavior that always has the given value
51− constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of Behaviors doesn't have a negative performance impact
52− -- | Create an Event from another Event; the provided function can sample Behaviors and hold Events, and use the results to produce a occurring (Just) or non-occurring (Nothing) result
53− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
54− -- | A monad for doing complex push-based calculations efficiently
55− type PushM t :: * -> *
56− -- | Create a Behavior by reading from other Behaviors; the result will be recomputed whenever any of the read Behaviors changes
57− pull :: PullM t a -> Behavior t a
58− -- | A monad for doing complex pull-based calculations efficiently
59− type PullM t :: * -> *
60− -- | Merge a collection of events; the resulting Event will only occur if at least one input event is occuring, and will contain all of the input keys that are occurring simultaneously
61− 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
62− -- | Efficiently fan-out an event to many destinations. This function should be partially applied, and then the result applied repeatedly to create child events
63− fan :: GCompare k => Event t (DMap k Identity) -> EventSelector t k --TODO: Can we help enforce the partial application discipline here? The combinator is worthless without it
64− -- | Create an Event that will occur whenever the currently-selected input Event occurs
65− switch :: Behavior t (Event t a) -> Event t a
66− -- | Create an Event that will occur whenever the input event is occurring and its occurrence value, another Event, is also occurring
67− coincidence :: Event t (Event t a) -> Event t a
68− -- | Extract the 'Behavior' of a 'Dynamic'.
69− current :: Dynamic t a -> Behavior t a
70− -- | Extract the 'Event' of the 'Dynamic'.
71− updated :: Dynamic t a -> Event t a
72− -- | Create a new 'Dynamic'. The given PullM must always return the most recent firing of the given Event, if any.
73− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
74− -- | Create a new 'Incremental'. The given PullM's value must always change in the same way that the accumulated application of patches would change that value.
75− unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
76− -- | Create a merge whose parents can change over time
77− mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
78− -- | Extract the 'Behavior' component of an 'Incremental'
79− currentIncremental :: Patch p => Incremental t p a -> Behavior t a
80− -- | Extract the 'Event' component of an 'Incremental'
81− updatedIncremental :: Patch p => Incremental t p a -> Event t (p a)
82− -- | Convert an 'Incremental' to a 'Dynamic'
83− incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
84−
85−class Patch p where
86− apply :: p a -> a -> Maybe a -- Return Nothing if no change is necessary
87−
88−instance Patch Identity where
89− apply (Identity a) _ = Just a
90−
91−data PatchDMap a where
92− PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
93−
94−instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
95− PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
96− -- PatchDMap is idempotent, so stimes n is id for every n
97−#if MIN_VERSION_semigroups(0,17,0)
98− stimes = stimesIdempotentMonoid
99−#else
100− times1p n x = case compare n 0 of
101− LT -> error "stimesIdempotentMonoid: negative multiplier"
102− EQ -> mempty
103− GT -> x
104−#endif
105−
106−instance GCompare k => Monoid (PatchDMap (DMap k v)) where
107− mempty = PatchDMap mempty
108− mappend = (<>)
109−
110−instance Patch PatchDMap where
111− apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
112− where insertions = DMap.mapMaybeWithKey (const $ id . getCompose) diff
113− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
114− nothingToJust = \case
115− Nothing -> Just $ Constant ()
116− Just _ -> Nothing
117−
118−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
119−unsafeDynamic = unsafeBuildDynamic . sample
120−
121−constDyn :: Reflex t => a -> Dynamic t a
122−constDyn = pure
123−
124−class (Applicative m, Monad m) => MonadSample t m | m -> t where
125− -- | Get the current value in the Behavior
126− sample :: Behavior t a -> m a
127−
128−class MonadSample t m => MonadHold t m where
129− -- | Create a new Behavior whose value will initially be equal to the given value and will be updated whenever the given Event occurs. The update takes effect immediately after the Event occurs; if the occurrence that sets the Behavior (or one that is simultaneous with it) is used to sample the Behavior, it will see the *old* value of the Behavior, not the new one.
130− hold :: a -> Event t a -> m (Behavior t a)
131− -- | Create a 'Dynamic' value using the given initial value that changes every
132− -- time the 'Event' occurs.
133− holdDyn :: a -> Event t a -> m (Dynamic t a)
134− -- | Create an 'Incremental' value using the given initial value that changes every
135− -- time the 'Event' occurs.
136− holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
137−
138−newtype EventSelector t k = EventSelector { select :: forall a. k a -> Event t a }
139−
140−--------------------------------------------------------------------------------
141−-- Instances
142−--------------------------------------------------------------------------------
143−
144−instance MonadSample t m => MonadSample t (ReaderT r m) where
145− sample = lift . sample
146−
147−instance MonadHold t m => MonadHold t (ReaderT r m) where
148− hold a0 = lift . hold a0
149− holdDyn a0 = lift . holdDyn a0
150− holdIncremental a0 = lift . holdIncremental a0
151−
152−instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
153− sample = lift . sample
154−
155−instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
156− hold a0 = lift . hold a0
157− holdDyn a0 = lift . holdDyn a0
158− holdIncremental a0 = lift . holdIncremental a0
159−
160−instance MonadSample t m => MonadSample t (StateT s m) where
161− sample = lift . sample
162−
163−instance MonadHold t m => MonadHold t (StateT s m) where
164− hold a0 = lift . hold a0
165− holdDyn a0 = lift . holdDyn a0
166− holdIncremental a0 = lift . holdIncremental a0
167−
168−instance MonadSample t m => MonadSample t (ExceptT e m) where
169− sample = lift . sample
170−
171−instance MonadHold t m => MonadHold t (ExceptT e m) where
172− hold a0 = lift . hold a0
173− holdDyn a0 = lift . holdDyn a0
174− holdIncremental a0 = lift . holdIncremental a0
175−
176−instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
177− sample = lift . sample
178−
179−instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
180− hold a0 = lift . hold a0
181− holdDyn a0 = lift . holdDyn a0
182− holdIncremental a0 = lift . holdIncremental a0
183−
184−instance MonadSample t m => MonadSample t (ContT r m) where
185− sample = lift . sample
186−
187−instance MonadHold t m => MonadHold t (ContT r m) where
188− hold a0 = lift . hold a0
189− holdDyn a0 = lift . holdDyn a0
190− holdIncremental a0 = lift . holdIncremental a0
191−
192−--------------------------------------------------------------------------------
193−-- Convenience functions
194−--------------------------------------------------------------------------------
195−
196−-- | Create an Event from another Event.
197−-- The provided function can sample 'Behavior's and hold 'Event's.
198−pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
199−pushAlways f = push (liftM Just . f)
200−
201−-- | Flipped version of 'fmap'.
202−ffor :: Functor f => f a -> (a -> b) -> f b
203−ffor = flip fmap
204−
205−instance Reflex t => Functor (Behavior t) where
206− fmap f = pull . liftM f . sample
207−
208−instance Reflex t => Applicative (Behavior t) where
209− pure = constant
210− f <*> x = pull $ sample f `ap` sample x
211− _ *> b = b
212− a <* _ = a
213−
214−instance Reflex t => Monad (Behavior t) where
215− a >>= f = pull $ sample a >>= sample . f
216− -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine Behaviors other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
217− return = constant
218− fail = error "Monad (Behavior t) does not support fail"
219−
220−instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
221− a <> b = pull $ liftM2 (<>) (sample a) (sample b)
222− sconcat = pull . liftM sconcat . mapM sample
223−#if MIN_VERSION_semigroups(0,17,0)
224− stimes n = fmap $ stimes n
225−#else
226− times1p n = fmap $ times1p n
227−#endif
228−
229−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
230− mempty = constant mempty
231− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
232− mconcat = pull . liftM mconcat . mapM sample
233−
234−--TODO: See if there's a better class in the standard libraries already
235−-- | A class for values that combines filtering and mapping using 'Maybe'.
236−class FunctorMaybe f where
237− -- | Combined mapping and filtering function.
238− fmapMaybe :: (a -> Maybe b) -> f a -> f b
239−
240−instance FunctorMaybe [] where
241− fmapMaybe f = catMaybes . fmap f
242−
243−-- | Flipped version of 'fmapMaybe'.
244−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
245−fforMaybe = flip fmapMaybe
246−
247−-- | Filter 'f a' using the provided predicate.
248−-- Relies on 'fforMaybe'.
249−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
250−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
251−
252−instance Reflex t => FunctorMaybe (Event t) where
253− fmapMaybe f = push $ return . f
254−
255−instance Reflex t => Functor (Event t) where
256− fmap f = fmapMaybe $ Just . f
257−
258−-- | Replace each occurrence value of the 'Event' with the value of the
259−-- 'Behavior' at the time of that occurrence.
260−tag :: Reflex t => Behavior t b -> Event t a -> Event t b
261−tag b = pushAlways $ \_ -> sample b
262−
263−-- | Create a new 'Event' that combines occurences of supplied 'Event'
264−-- with the current value of the 'Behavior'.
265−attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
266−attach = attachWith (,)
267−
268−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs
269−-- by combining it with the current value of the 'Behavior'.
270−attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
271−attachWith f = attachWithMaybe $ \a b -> Just $ f a b
272−
273−-- | Create a new 'Event' by combining each occurence with the current
274−-- value of the 'Behavior'. The occurrence is discarded if the combining function
275−-- returns Nothing
276−attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
277−attachWithMaybe f b e = flip push e $ \o -> liftM (flip f o) $ sample b
278−
279−-- | Alias for 'headE'
280−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
281−onceE = headE
282−
283−-- | Create a new 'Event' that only occurs on the first occurence of
284−-- the supplied 'Event'.
285−headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
286−headE e = do
287− rec be <- hold e $ fmap (const never) e'
288− let e' = switch be
289− e' `seq` return ()
290− return e'
291−
292−-- | Create a new 'Event' that occurs on all but the first occurence
293−-- of the supplied 'Event'.
294−tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
295−tailE e = liftM snd $ headTailE e
296−
297−-- | Create a tuple of two 'Event's with the first one occuring only
298−-- the first time the supplied 'Event' occurs and the second occuring
299−-- on all but the first occurence.
300−headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
301−headTailE e = do
302− eHead <- headE e
303− be <- hold never $ fmap (const e) eHead
304− return (eHead, switch be)
305−
306−-- | Split the supplied 'Event' into two individual 'Event's occuring
307−-- at the same time with the respective values from the tuple.
308−splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
309−splitE e = (fmap fst e, fmap snd e)
310−
311−-- | Print the supplied 'String' and the value of the 'Event' on each
312−-- occurence. This should /only/ be used for debugging.
313−--
314−-- Note: As with Debug.Trace.trace, the message will only be printed if
315−-- the 'Event' is actually used.
316−traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
317−traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
318−
319−-- | Print the output of the supplied function on each occurence of
320−-- the 'Event'. This should /only/ be used for debugging.
321−--
322−-- Note: As with Debug.Trace.trace, the message will only be printed if
323−-- the 'Event' is actually used.
324−traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
325−traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
326−
327−-- | Tag type for 'Either' to use it as a 'DSum'.
328−data EitherTag l r a where
329− LeftTag :: EitherTag l r l
330− RightTag :: EitherTag l r r
331−
332−instance GEq (EitherTag l r) where
333− geq a b = case (a, b) of
334− (LeftTag, LeftTag) -> Just Refl
335− (RightTag, RightTag) -> Just Refl
336− _ -> Nothing
337−
338−instance GCompare (EitherTag l r) where
339− gcompare a b = case (a, b) of
340− (LeftTag, LeftTag) -> GEQ
341− (LeftTag, RightTag) -> GLT
342− (RightTag, LeftTag) -> GGT
343− (RightTag, RightTag) -> GEQ
344−
345−instance GShow (EitherTag l r) where
346− gshowsPrec _ a = case a of
347− LeftTag -> showString "LeftTag"
348− RightTag -> showString "RightTag"
349−
350−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
351− showTaggedPrec t n (Identity a) = case t of
352− LeftTag -> showsPrec n a
353− RightTag -> showsPrec n a
354−
355−-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
356−eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
357−eitherToDSum = \case
358− Left a -> (LeftTag :=> Identity a)
359− Right b -> (RightTag :=> Identity b)
360−
361−-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
362−dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
363−dsumToEither = \case
364− (LeftTag :=> Identity a) -> Left a
365− (RightTag :=> Identity b) -> Right b
366−
367−-- | Extract the values of a 'DMap' of 'EitherTag's.
368−dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
369−dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
370− (Nothing, Nothing) -> Nothing
371− (Just (Identity a), Nothing) -> Just $ This a
372− (Nothing, Just (Identity b)) -> Just $ That b
373− (Just (Identity a), Just (Identity b)) -> Just $ These a b
374−
375−-- | Create a new 'Event' that occurs if at least one of the supplied
376−-- 'Event's occurs. If both occur at the same time they are combined
377−-- using 'mappend'.
378−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
379−appendEvents e1 e2 = mergeThese mappend <$> align e1 e2
380−
381−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
382−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
383−sequenceThese t = case t of
384− This ma -> liftM This ma
385− These ma mb -> liftM2 These ma mb
386− That mb -> liftM That mb
387−
388−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
389− (<>) = alignWith (mergeThese (<>))
390− sconcat = fmap sconcat . mergeList . toList
391−#if MIN_VERSION_semigroups(0,17,0)
392− stimes n = fmap $ stimes n
393−#else
394− times1p n = fmap $ times1p n
395−#endif
396−
397−instance (Semigroup a, Reflex t) => Monoid (Event t a) where
398− mempty = never
399− mappend = (<>)
400− mconcat = fmap sconcat . mergeList
401−
402−-- | Create a new 'Event' that occurs if at least one of the 'Event's
403−-- in the list occurs. If multiple occur at the same time they are
404−-- folded from the left with the given function.
405−mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
406−mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
407− . merge
408− . DMap.fromDistinctAscList
409− . map (\(k, v) -> (Const2 k) :=> v)
410− $ zip [0 :: Int ..] es
411−
412−-- | Create a new 'Event' that occurs if at least one of the 'Event's
413−-- in the list occurs. If multiple occur at the same time the value is
414−-- the value of the leftmost event.
415−leftmost :: Reflex t => [Event t a] -> Event t a
416−leftmost = mergeWith const
417−
418−-- | Create a new 'Event' that occurs if at least one of the 'Event's
419−-- in the list occurs and has a list of the values of all 'Event's
420−-- occuring at that time.
421−mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
422−mergeList [] = never
423−mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
424−
425−-- | Create a new 'Event' combining the map of 'Event's into an
426−-- 'Event' that occurs if at least one of them occurs and has a map of
427−-- values of all 'Event's occuring at that time.
428−mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
429−mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
430−
431−-- | Split the event into an 'EventSelector' that allows efficient
432−-- selection of the individual 'Event's.
433−fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
434−fanMap = fan . fmap mapToDMap
435−
436−-- | Switches to the new event whenever it receives one; the new event is used immediately, on the same frame that it is switched to
437−switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
438−switchPromptly ea0 eea = do
439− bea <- hold ea0 eea
440− let eLag = switch bea
441− eCoincidences = coincidence eea
442− return $ leftmost [eCoincidences, eLag]
443−
444−instance Reflex t => Align (Event t) where
445− nil = never
446− align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
447−
448−-- | Create a new 'Event' that only occurs if the supplied 'Event'
449−-- occurs and the 'Behavior' is true at the time of occurence.
450−gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
451−gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
452−
453−-- | Create a new behavior given a starting behavior and switch to a the
454−-- behvior carried by the event when it fires.
455−switcher :: (Reflex t, MonadHold t m)
456− => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
457−switcher b eb = pull . (sample <=< sample) <$> hold b eb
458−
459−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
460−zipDynWith f da db =
461− let eab = align (updated da) (updated db)
462− ec = flip push eab $ \o -> do
463− (a, b) <- case o of
464− This a -> do
465− b <- sample $ current db
466− return (a, b)
467− That b -> do
468− a <- sample $ current da
469− return (a, b)
470− These a b -> return (a, b)
471− return $ Just $ f a b
472− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
473−
474−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
475− mconcat = fmap (mconcat . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
476− mempty = constDyn mempty
477− mappend = zipDynWith mappend
478−
479−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
480−distributeDMapOverDynPure dm = case DMap.toList dm of
481− [] -> constDyn DMap.empty
482− [k :=> v] -> fmap (DMap.singleton k . Identity) v
483− _ ->
484− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
485− edmPre = merge $ rewrapDMap updated dm
486− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
487− olds <- sample $ current result
488− return $ DMap.unionWithKey (\_ _ new -> new) olds news
489− in result
490−
491−class Reflex t => Accumulator t f | f -> t where
492− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
493− accum f = accumMaybe $ \v o -> Just $ f v o
494− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
495− accumM f = accumMaybeM $ \v o -> Just <$> f v o
496− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
497− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
498− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
499− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
500− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
501− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
502− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
503− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
504− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
505− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
506−
507−mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
508−mapAccum_ f z e = do
509− (_ :: Behavior t a, result) <- mapAccum f z e
510− return result
511−
512−mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
513−mapAccumMaybe_ f z e = do
514− (_ :: Behavior t a, result) <- mapAccumMaybe f z e
515− return result
516−
517−mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
518−mapAccumM_ f z e = do
519− (_ :: Behavior t a, result) <- mapAccumM f z e
520− return result
521−
522−mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
523−mapAccumMaybeM_ f z e = do
524− (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
525− return result
526−
527−instance Reflex t => Accumulator t (Dynamic t) where
528− accumMaybeM f z e = do
529− rec let e' = flip push e $ \o -> do
530− v <- sample $ current d'
531− f v o
532− d' <- holdDyn z e'
533− return d'
534− mapAccumMaybeM f z e = do
535− rec let e' = flip push e $ \o -> do
536− v <- sample $ current d'
537− result <- f v o
538− return $ case result of
539− (Nothing, Nothing) -> Nothing
540− _ -> Just result
541− d' <- holdDyn z $ fmapMaybe fst e'
542− return (d', fmapMaybe snd e')
543−
544−instance Reflex t => Accumulator t (Behavior t) where
545− accumMaybeM f z e = current <$> accumMaybeM f z e
546− mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
547−
548−instance Reflex t => Accumulator t (Event t) where
549− accumMaybeM f z e = updated <$> accumMaybeM f z e
550− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
551−
552−-- | Create a new 'Event' by combining each occurence with the next value
553−-- of the list using the supplied function. If the list runs out of items,
554−-- all subsequent 'Event' occurrences will be ignored.
555−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
556−zipListWithEvent f l e = do
557− let f' a b = case a of
558− h:t -> (Just t, Just $ f h b)
559− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
560− mapAccumMaybe_ f' l e
src/Reflex/Dynamic.hsdeleted−443
@@ -1,443 +0,0 @@
1−{-# LANGUAGE CPP, TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, DataKinds, PolyKinds #-}
2−module Reflex.Dynamic ( Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
3− , current
4− , updated
5− , constDyn
6− , holdDyn
7− , nubDyn
8− , count
9− , toggle
10− , switchPromptlyDyn
11− , tagDyn
12− , attachDyn
13− , attachDynWith
14− , attachDynWithMaybe
15− , mapDyn
16− , forDyn
17− , foldDyn
18− , foldDynM
19− , foldDynMaybe
20− , foldDynMaybeM
21− , combineDyn
22− , apDyn
23− , collectDyn
24− , mconcatDyn
25− , distributeDMapOverDyn
26− , joinDyn
27− , joinDynThroughMap
28− , traceDyn
29− , traceDynWith
30− , splitDyn
31− , splitDynPure
32− , Demux
33− , demux
34− , getDemuxed
35− -- Things that probably aren't very useful:
36− , HList (..)
37− , FHList (..)
38− , distributeFHListOverDyn
39− -- Unsafe
40− , unsafeDynamic
41− ) where
42−
43−import Prelude hiding (mapM, mapM_)
44−
45−import Reflex.Class
46−import Data.Functor.Misc
47−
48−import Control.Monad hiding (mapM, mapM_, forM, forM_)
49−import Control.Monad.Fix
50−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_)
51−import Data.These
52−import Data.Align
53−import Data.Map (Map)
54−import Data.Dependent.Map (DMap)
55−import qualified Data.Dependent.Map as DMap
56−import Data.Dependent.Sum (DSum (..))
57−import Data.GADT.Compare (GCompare (..), GEq (..), (:~:) (..), GOrdering (..))
58−import Data.Monoid
59−
60−import Debug.Trace
61−
62−data HList (l::[*]) where
63− HNil :: HList '[]
64− HCons :: e -> HList l -> HList (e ': l)
65−
66−infixr 2 `HCons`
67−
68−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
69−type instance HRevApp '[] l = l
70−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
71−
72−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
73−hRevApp HNil l = l
74−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
75−
76−hReverse :: HList l -> HList (HRevApp l '[])
77−hReverse l = hRevApp l HNil
78−
79−hBuild :: (HBuild' '[] r) => r
80−hBuild = hBuild' HNil
81−
82−class HBuild' l r where
83− hBuild' :: HList l -> r
84−
85−instance (l' ~ HRevApp l '[])
86− => HBuild' l (HList l') where
87− hBuild' l = hReverse l
88−
89−instance HBuild' (a ': l) r
90− => HBuild' l (a->r) where
91− hBuild' l x = hBuild' (HCons x l)
92−
93−-- | Create a new 'Dynamic' that only signals changes if the values
94−-- actually changed.
95−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
96−nubDyn d =
97− let e' = attachWithMaybe (\x x' -> if x' == x then Nothing else Just x') (current d) (updated d)
98− in unsafeDynamic (current d) e' --TODO: Avoid invalidating the outgoing Behavior
99−
100−-- | Map a function over a 'Dynamic'.
101−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
102−mapDyn f = return . fmap f
103−
104−-- | Flipped version of 'mapDyn'.
105−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
106−forDyn = flip mapDyn
107−
108−-- | Create a 'Dynamic' using the initial value and change it each
109−-- time the 'Event' occurs using a folding function on the previous
110−-- value and the value of the 'Event'.
111−foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
112−foldDyn = accum . flip
113−
114−-- | Create a 'Dynamic' using the initial value and change it each
115−-- time the 'Event' occurs using a monadic folding function on the
116−-- previous value and the value of the 'Event'.
117−foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
118−foldDynM = accumM . flip
119−
120−foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
121−foldDynMaybe = accumMaybe . flip
122−
123−foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
124−foldDynMaybeM = accumMaybeM . flip
125−
126−-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
127−count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
128−count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
129−
130−-- | Create a new 'Dynamic' using the initial value that flips its
131−-- value every time the 'Event' occurs.
132−toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
133−toggle = foldDyn (const not)
134−
135−-- | Switches to the new 'Event' whenever it receives one. Switching
136−-- occurs *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the old and new
137−-- inner Events fire simultaneously, the output will fire with the value of the *new* 'Event'.
138−switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
139−switchPromptlyDyn de =
140− let eLag = switch $ current de
141− eCoincidences = coincidence $ updated de
142− in leftmost [eCoincidences, eLag]
143−
144−{-
145−
146−mergeEventsWith :: Reflex t m => (a -> a -> a) -> Event t a -> Event t a -> m (Event t a)
147−mergeEventsWith f ea eb = mapE (mergeThese f) =<< alignEvents ea eb
148−
149−firstE :: (Reflex t m) => [Event t a] -> m (Event t a)
150−firstE [] = return never
151−firstE (h:t) = mergeEventsLeftBiased h =<< firstE t
152−
153−concatEventsWith :: (Reflex t m) => (a -> a -> a) -> [Event t a] -> m (Event t a)
154−concatEventsWith _ [] = return never
155−concatEventsWith _ [e] = return e
156−concatEventsWith f es = mapEM (fmap (foldl1 f . map (\(Const2 _ :=> v) -> v) . DMap.toList) . sequenceDmap) <=< mergeEventDMap $ DMap.fromList $ map (\(k, v) -> WrapArg (Const2 k) :=> v) $ zip [0 :: Int ..] es
157−--concatEventsWith f (h:t) = mergeEventsWith f h =<< concatEventsWith f t
158−
159−mconcatE :: (Reflex t m, Monoid a) => [Event t a] -> m (Event t a)
160−mconcatE = concatEventsWith mappend
161−-}
162−
163−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the
164−-- respective value of the tuple.
165−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
166−splitDyn = return . splitDynPure
167−
168−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
169−splitDynPure d = (fmap fst d, fmap snd d)
170−
171−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
172−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
173−mconcatDyn = return . mconcat
174−
175−-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of
176−-- Dynamic values.
177−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
178−distributeDMapOverDyn = return . distributeDMapOverDynPure
179−
180−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
181−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
182−
183−-- | Merge two 'Dynamic's into a new one using the provided
184−-- function. The new 'Dynamic' changes its value each time one of the
185−-- original 'Dynamic's changes its value.
186−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
187−combineDyn f da db = return $ zipDynWith f da db
188−
189−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
190−--
191−-- > do
192−-- > person <- Person `mapDyn` dynFirstName
193−-- > `apDyn` dynListName
194−-- > `apDyn` dynAge
195−-- > `apDyn` dynAddress
196−apDyn :: forall t m a b. (Reflex t, Monad m)
197− => m (Dynamic t (a -> b))
198− -> Dynamic t a
199− -> m (Dynamic t b)
200−apDyn m a = do
201− r <- m
202− return $ r <*> a
203−
204−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
205−joinDyn = join
206−
207−--TODO: Generalize this to functors other than Maps
208−-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
209−-- with the current values of the 'Dynamic's in a map.
210−joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
211−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
212−
213−-- | Print the value of the 'Dynamic' on each change and prefix it
214−-- with the provided string. This should /only/ be used for debugging.
215−--
216−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
217−-- else in the system is depending on it.
218−traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
219−traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
220−
221−-- | Print the result of applying the provided function to the value
222−-- of the 'Dynamic' on each change. This should /only/ be used for
223−-- debugging.
224−--
225−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
226−-- else in the system is depending on it.
227−traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
228−traceDynWith f d =
229− let e' = traceEventWith f $ updated d
230− getV0 = do
231− x <- sample $ current d
232− trace (f x) $ return x
233− in unsafeBuildDynamic getV0 e'
234−
235−-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
236−-- each time the 'Event' occurs.
237−--
238−-- Note: `tagDyn d e` differs from `tag (current d) e` in the case that `e` is firing
239−-- at the same time that `d` is changing. With `tagDyn d e`, the *new* value of `d`
240−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
241−-- will be used, since the 'Behavior' won't be updated until the end of the frame.
242−-- Additionally, this means that the output 'Event' may not be used to directly change
243−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
244−-- cyclic data flows, generally `tag (current d) e` is preferred.
245−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
246−tagDyn = attachDynWith const
247−
248−-- | Attach the current value of the 'Dynamic' to the value of the
249−-- 'Event' each time it occurs.
250−--
251−-- Note: `attachDyn d` is not the same as `attach (current d)`. See 'tagDyn' for details.
252−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
253−attachDyn = attachDynWith (,)
254−
255−-- | Combine the current value of the 'Dynamic' with the value of the
256−-- 'Event' each time it occurs.
257−--
258−-- Note: `attachDynWith f d` is not the same as `attachWith f (current d)`. See 'tagDyn' for details.
259−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
260−attachDynWith f = attachDynWithMaybe $ \a b -> Just $ f a b
261−
262−-- | Create a new 'Event' by combining the value at each occurence
263−-- with the current value of the 'Dynamic' value and possibly
264−-- filtering if the combining function returns 'Nothing'.
265−--
266−-- Note: `attachDynWithMaybe f d` is not the same as `attachWithMaybe f (current d)`. See 'tagDyn' for details.
267−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
268−attachDynWithMaybe f d e =
269− let e' = attach (current d) e
270− in fforMaybe (align e' $ updated d) $ \case
271− This (a, b) -> f a b -- Only the tagging event is firing, so use that
272− These (_, b) a -> f a b -- Both events are firing, so use the newer value
273− That _ -> Nothing -- The tagging event isn't firing, so don't fire
274−
275−--------------------------------------------------------------------------------
276−-- Demux
277−--------------------------------------------------------------------------------
278−
279−-- | Represents a time changing value together with an 'EventSelector'
280−-- that can efficiently detect when the underlying Dynamic has a particular value.
281−-- This is useful for representing data like the current selection of a long list.
282−--
283−-- Semantically,
284−-- > getDemuxed (demux d) k === mapDyn (== k) d
285−-- However, the when getDemuxed is used multiple times, the complexity is only /O(log(n))/,
286−-- rather than /O(n)/ for mapDyn.
287−data Demux t k = Demux { demuxValue :: Behavior t k
288− , demuxSelector :: EventSelector t (Const2 k Bool)
289− }
290−
291−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given time, whichever output is indicated by the given 'Dynamic' will be 'True'.
292−demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
293−demux k = Demux (current k)
294− (fan $ attachWith (\k0 k1 -> if k0 == k1
295− then DMap.empty
296− else DMap.fromList [Const2 k0 :=> Identity False,
297− Const2 k1 :=> Identity True])
298− (current k) (updated k))
299−
300−--TODO: The pattern of using hold (sample b0) can be reused in various places as a safe way of building certain kinds of Dynamics; see if we can factor this out
301−-- | Select a particular output of the 'Demux'; this is equivalent to (but much faster than)
302−-- mapping over the original 'Dynamic' and checking whether it is equal to the given key.
303−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
304−getDemuxed d k = return $ demuxed d k
305−
306−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
307−demuxed d k =
308− let e = select (demuxSelector d) (Const2 k)
309− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
310−
311−--------------------------------------------------------------------------------
312−-- collectDyn
313−--------------------------------------------------------------------------------
314−
315−--TODO: This whole section is badly in need of cleanup
316−
317−data FHList f l where
318− FHNil :: FHList f '[]
319− FHCons :: f e -> FHList f l -> FHList f (e ': l)
320−
321−instance GEq (HListPtr l) where
322− HHeadPtr `geq` HHeadPtr = Just Refl
323− HHeadPtr `geq` HTailPtr _ = Nothing
324− HTailPtr _ `geq` HHeadPtr = Nothing
325− HTailPtr a `geq` HTailPtr b = a `geq` b
326−
327−instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
328− HHeadPtr `gcompare` HHeadPtr = GEQ
329− HHeadPtr `gcompare` HTailPtr _ = GLT
330− HTailPtr _ `gcompare` HHeadPtr = GGT
331− HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
332−
333−data HListPtr l a where
334− HHeadPtr :: HListPtr (h ': t) h
335− HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
336−
337−fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
338−fhlistToDMap = DMap.fromList . go
339− where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
340− go = \case
341− FHNil -> []
342− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> (HTailPtr p) :=> v) (go t)
343−
344−class RebuildSortedHList l where
345− rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
346− rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
347−
348−instance RebuildSortedHList '[] where
349− rebuildSortedFHList l = case l of
350− [] -> FHNil
351− _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
352− rebuildSortedHList l = case l of
353− [] -> HNil
354− _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
355−
356−instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
357− rebuildSortedFHList l = case l of
358− ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
359− _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
360− rebuildSortedHList l = case l of
361− ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
362− _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
363−
364−dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
365−dmapToHList = rebuildSortedHList . DMap.toList
366−
367−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
368−distributeFHListOverDyn = return . distributeFHListOverDynPure
369−
370−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
371−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
372−
373−class AllAreFunctors (f :: a -> *) (l :: [a]) where
374− type FunctorList f l :: [*]
375− toFHList :: HList (FunctorList f l) -> FHList f l
376− fromFHList :: FHList f l -> HList (FunctorList f l)
377−
378−instance AllAreFunctors f '[] where
379− type FunctorList f '[] = '[]
380− toFHList l = case l of
381− HNil -> FHNil
382−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
383− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
384−#endif
385− fromFHList FHNil = HNil
386−
387−instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
388− type FunctorList f (h ': t) = f h ': FunctorList f t
389− toFHList l = case l of
390− a `HCons` b -> a `FHCons` toFHList b
391−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
392− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
393−#endif
394− fromFHList (a `FHCons` b) = a `HCons` fromFHList b
395−
396−collectDyn :: ( RebuildSortedHList (HListElems b)
397− , IsHList a, IsHList b
398− , AllAreFunctors (Dynamic t) (HListElems b)
399− , Reflex t, Monad m
400− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
401− ) => a -> m (Dynamic t b)
402−collectDyn = return . collectDynPure
403−
404−collectDynPure :: ( RebuildSortedHList (HListElems b)
405− , IsHList a, IsHList b
406− , AllAreFunctors (Dynamic t) (HListElems b)
407− , Reflex t
408− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
409− ) => a -> Dynamic t b
410−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
411−
412−-- Poor man's Generic
413−class IsHList a where
414− type HListElems a :: [*]
415− toHList :: a -> HList (HListElems a)
416− fromHList :: HList (HListElems a) -> a
417−
418−instance IsHList (a, b) where
419− type HListElems (a, b) = [a, b]
420− toHList (a, b) = hBuild a b
421− fromHList l = case l of
422− a `HCons` b `HCons` HNil -> (a, b)
423−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
424− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
425−#endif
426−
427−instance IsHList (a, b, c, d) where
428− type HListElems (a, b, c, d) = [a, b, c, d]
429− toHList (a, b, c, d) = hBuild a b c d
430− fromHList l = case l of
431− a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
432−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
433− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
434−#endif
435−
436−instance IsHList (a, b, c, d, e, f) where
437− type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
438− toHList (a, b, c, d, e, f) = hBuild a b c d e f
439− fromHList l = case l of
440− a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
441−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
442− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
443−#endif
src/Reflex/Dynamic/Nub.hsdeleted−60
@@ -1,60 +0,0 @@
1−{-# LANGUAGE MagicHash, FlexibleInstances, MultiParamTypeClasses #-}
2−module Reflex.Dynamic.Nub
3− ( NubDynamic
4− , nubDynamic
5− , fromNubDynamic
6− , alreadyNubbedDynamic
7− ) where
8−
9−import Reflex.Class
10−import Reflex.Dynamic
11−
12−import GHC.Exts
13−
14−newtype NubDynamic t a = NubDynamic { unNubDynamic :: Dynamic t a }
15−
16−nubDynamic :: Reflex t => Dynamic t a -> NubDynamic t a
17−nubDynamic d = NubDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
18− old <- sample $ current d --TODO: Is it better to sample ourselves here?
19− return $ unsafeJustChanged old new
20−
21−fromNubDynamic :: (Reflex t, Eq a) => NubDynamic t a -> Dynamic t a
22−fromNubDynamic (NubDynamic d) = nubDyn d
23−
24−-- | Create a NubDynamic without nubbing it on creation
25−-- This will be slightly faster than nubDynamic when used with a Dynamic whose values are always (or nearly always) different from its previous values; if used with a Dynamic whose values do not change frequently, it may be much slower than nubDynamic
26−alreadyNubbedDynamic :: Dynamic t a -> NubDynamic t a
27−alreadyNubbedDynamic = NubDynamic
28−
29−unsafeJustChanged :: a -> a -> Maybe a
30−unsafeJustChanged old new = case old `seq` new `seq` reallyUnsafePtrEquality# old new of
31− 0# -> Just new
32− _ -> Nothing
33−
34−instance Reflex t => Accumulator t (NubDynamic t) where
35− accumMaybeM f z e = do
36− let f' old change = do
37− mNew <- f old change
38− return $ unsafeJustChanged old =<< mNew
39− d <- accumMaybeM f' z e
40− return $ NubDynamic d
41− mapAccumMaybeM f z e = do
42− let f' old change = do
43− (mNew, output) <- f old change
44− return (unsafeJustChanged old =<< mNew, output)
45− (d, out) <- mapAccumMaybeM f' z e
46− return (NubDynamic d, out)
47−
48−instance Reflex t => Functor (NubDynamic t) where
49− fmap f (NubDynamic d) = nubDynamic $ fmap f d
50−
51−instance Reflex t => Applicative (NubDynamic t) where
52− pure = NubDynamic . constDyn
53− NubDynamic a <*> NubDynamic b = nubDynamic $ a <*> b
54− _ *> b = b
55− a <* _ = a
56−
57−instance Reflex t => Monad (NubDynamic t) where
58− NubDynamic x >>= f = nubDynamic $ x >>= unNubDynamic . f
59− _ >> b = b
60− return = pure
src/Reflex/Dynamic/TH.hsdeleted−67
@@ -1,67 +0,0 @@
1−{-# LANGUAGE TemplateHaskell, ScopedTypeVariables, TypeOperators, GADTs, EmptyDataDecls, PatternGuards #-}
2−module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
3−
4−import Reflex.Dynamic
5−
6−import Language.Haskell.TH
7−import qualified Language.Haskell.TH.Syntax as TH
8−import Language.Haskell.TH.Quote
9−import Data.Data
10−import Control.Monad.State
11−import qualified Language.Haskell.Exts as Hs
12−import qualified Language.Haskell.Meta.Syntax.Translate as Hs
13−import Data.Monoid
14−import Data.Generics
15−
16−-- | Quote a Dynamic expression. Within the quoted expression, you can use '$(unqDyn [| x |])' to refer to any expression 'x' of type 'Dynamic t a'; the unquoted result will be of type 'a'
17−qDyn :: Q Exp -> Q Exp
18−qDyn qe = do
19− e <- qe
20− let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
21− f d = case eqT of
22− Just (Refl :: d :~: Exp)
23− | AppE (VarE m) eInner <- d
24− , m == 'unqMarker
25− -> do n <- lift $ newName "dynamicQuotedExpressionVariable"
26− modify ((n, eInner):)
27− return $ VarE n
28− _ -> gmapM f d
29− (e', exprsReversed) <- runStateT (gmapM f e) []
30− let exprs = reverse exprsReversed
31− arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
32− param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
33− [| mapDyn $(return $ LamE [param] e') =<< distributeFHListOverDyn $(return arg) |]
34−
35−unqDyn :: Q Exp -> Q Exp
36−unqDyn e = [| unqMarker $e |]
37−
38−-- | This type represents an occurrence of unqDyn before it has been processed by qDyn. If you see it in a type error, it probably means that unqDyn has been used outside of a qDyn context.
39−data UnqDyn
40−
41−-- unqMarker must not be exported; it is used only as a way of smuggling data from unqDyn to qDyn
42−--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
43−unqMarker :: a -> UnqDyn
44−unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
45−
46−mkDyn :: QuasiQuoter
47−mkDyn = QuasiQuoter
48− { quoteExp = mkDynExp
49− , quotePat = error "mkDyn: pattern splices are not supported"
50− , quoteType = error "mkDyn: type splices are not supported"
51− , quoteDec = error "mkDyn: declaration splices are not supported"
52− }
53−
54−mkDynExp :: String -> Q Exp
55−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
56− Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
57− Hs.ParseOk e -> qDyn $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
58− where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
59− antiE x = case x of
60− Hs.SpliceExp se ->
61− Hs.App (Hs.Var $ Hs.Qual (Hs.ModuleName modName) (Hs.Ident occName)) $ case se of
62− Hs.IdSplice v -> Hs.Var $ Hs.UnQual $ Hs.Ident v
63− Hs.ParenSplice ps -> ps
64− _ -> x
65− reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
66− | modName == modName' && occName == occName' = 'unqMarker
67− reinstateUnqDyn x = x
src/Reflex/Host/Class.hsdeleted−227
@@ -1,227 +0,0 @@
1−{-# LANGUAGE ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, FunctionalDependencies, FlexibleContexts #-}
2−module Reflex.Host.Class where
3−
4−import Reflex.Class
5−
6−import Control.Applicative
7−import Control.Monad
8−import Control.Monad.Fix
9−import Control.Monad.Identity
10−import Control.Monad.Trans
11−import Control.Monad.Trans.Reader (ReaderT())
12−import Control.Monad.Trans.Writer (WriterT())
13−import Control.Monad.Trans.Cont (ContT())
14−import Control.Monad.Trans.Except (ExceptT())
15−import Control.Monad.Trans.RWS (RWST())
16−import Control.Monad.Trans.State (StateT())
17−import qualified Control.Monad.Trans.State.Strict as Strict
18−import Data.Dependent.Sum (DSum (..))
19−import Data.Monoid
20−import Data.GADT.Compare
21−import Control.Monad.Ref
22−
23−-- Note: this import must come last to silence warnings from AMP
24−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl)
25−
26−-- | Framework implementation support class for the reflex implementation represented by @t@.
27−class (Reflex t, MonadReflexCreateTrigger t (HostFrame t), MonadSample t (HostFrame t), MonadHold t (HostFrame t), MonadFix (HostFrame t), MonadSubscribeEvent t (HostFrame t)) => ReflexHost t where
28− type EventTrigger t :: * -> *
29− type EventHandle t :: * -> *
30− type HostFrame t :: * -> *
31−
32−-- | Monad in which Events can be 'subscribed'. This forces all underlying event sources to be initialized, so that the event will fire whenever it ought to. Events must be subscribed before they are read using readEvent
33−class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
34− -- | Subscribe to an event and set it up if needed.
35− --
36− -- This function will create a new 'EventHandle' from an 'Event'. This handle may then be used via
37− -- 'readEvent' in the read callback of 'fireEventsAndRead'.
38− --
39− -- If the event wasn't subscribed to before (either manually or through a dependent event or behavior)
40− -- then this function will cause the event and all dependencies of this event to be set up.
41− -- For example, if the event was created by 'newEventWithTrigger', then it's callback will be executed.
42− --
43− -- It's safe to call this function multiple times.
44− subscribeEvent :: Event t a -> m (EventHandle t a)
45−
46−-- | Monad that allows to read events' values.
47−class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
48− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling 'subscribeEvent').
49− --
50− -- After event propagation is done, all events can be in two states: either they are firing with some value or they are not firing.
51− -- In the former case, this function returns @Just act@, where @act@ in an action to read the current value of
52− -- the event. In the latter case, the function returns @Nothing@.
53− --
54− -- This function is normally used in the calllback for 'fireEventsAndRead'.
55− readEvent :: EventHandle t a -> m (Maybe (m a))
56−
57−-- | A monad where new events feed from external sources can be created.
58−class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
59− -- | Creates a root 'Event' (one that is not based on any other event).
60− --
61− -- When a subscriber first subscribes to an event (building another event
62− -- that depends on the subscription) the given callback function is run and
63− -- passed a trigger. The callback function can then set up the event source in IO.
64− -- After this is done, the callback function must return an accompanying teardown action.
65− --
66− -- Any time between setup and teardown the trigger can be used to fire
67− -- the event, by passing it to 'fireEventsAndRead'.
68− --
69− -- Note: An event may be set up multiple times. So after the teardown action is executed, the event may still be set up again in the future.
70− newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
71− newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
72−
73−class (ReflexHost t, MonadReflexCreateTrigger t m, MonadSubscribeEvent t m, MonadReadEvent t (ReadPhase m), MonadSample t (ReadPhase m), MonadHold t (ReadPhase m)) => MonadReflexHost t m | m -> t where
74− type ReadPhase m :: * -> *
75− -- | Propagate some events firings and read the values of events afterwards.
76− --
77− -- This function will create a new frame to fire the given events. It will then update all
78− -- dependent events and behaviors. After that is done, the given callback is executed which
79− -- allows to read the final values of events and check whether they have fired in this frame or
80− -- not.
81− --
82− -- All events that are given are fired at the same time.
83− --
84− -- This function is typically used in the main loop of a reflex framework implementation.
85− -- The main loop waits for external events to happen (such as keyboard input or a mouse click)
86− -- and then fires the corresponding events using this function. The read callback can be used
87− -- to read output events and perform a corresponding response action to the external event.
88− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m a
89−
90− -- | Run a frame without any events firing.
91− --
92− -- This function should be used when you want to use 'sample' and 'hold' when no events are currently firing.
93− -- Using this function in that case can improve performance, since the implementation can assume that no events
94− -- are firing when 'sample' or 'hold' are called.
95− --
96− -- This function is commonly used to set up the basic event network when the application starts up.
97− runHostFrame :: HostFrame t a -> m a
98−
99−-- | Like 'fireEventsAndRead', but without reading any events.
100−fireEvents :: MonadReflexHost t m => [DSum (EventTrigger t) Identity] -> m ()
101−fireEvents dm = fireEventsAndRead dm $ return ()
102−{-# INLINE fireEvents #-}
103−
104−-- | Create a new event and store its trigger in an 'IORef' while it's active.
105−--
106−-- An event is only active between the set up (when it's first subscribed to) and the teardown phases (when noboby is subscribing the event anymore).
107−-- This function returns an Event and an 'IORef'. As long as the event is active, the 'IORef' will contain 'Just' the event trigger to
108−-- trigger this event. When the event is not active, the 'IORef' will contain 'Nothing'. This allows event sources to be more efficient,
109−-- since they don't need to produce events when nobody is listening.
110−newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
111−newEventWithTriggerRef = do
112− rt <- newRef Nothing
113− e <- newEventWithTrigger $ \t -> do
114− writeRef rt $ Just t
115− return $ writeRef rt Nothing
116− return (e, rt)
117−{-# INLINE newEventWithTriggerRef #-}
118−
119−fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
120−fireEventRef mtRef input = do
121− mt <- readRef mtRef
122− case mt of
123− Nothing -> return ()
124− Just trigger -> fireEvents [trigger :=> Identity input]
125−
126−fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
127−fireEventRefAndRead mtRef input e = do
128− mt <- readRef mtRef
129− case mt of
130− Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
131− Just trigger -> fireEventsAndRead [trigger :=> Identity input] $ do
132− mGetValue <- readEvent e
133− case mGetValue of
134− Nothing -> return Nothing
135− Just getValue -> liftM Just getValue
136−
137−
138−--------------------------------------------------------------------------------
139−-- Instances
140−--------------------------------------------------------------------------------
141−
142−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ReaderT r m) where
143− newEventWithTrigger = lift . newEventWithTrigger
144− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
145−
146−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ReaderT r m) where
147− subscribeEvent = lift . subscribeEvent
148−
149−instance MonadReflexHost t m => MonadReflexHost t (ReaderT r m) where
150− type ReadPhase (ReaderT r m) = ReadPhase m
151− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
152− runHostFrame = lift . runHostFrame
153−
154−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (WriterT w m) where
155− newEventWithTrigger = lift . newEventWithTrigger
156− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
157−
158−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (WriterT w m) where
159− subscribeEvent = lift . subscribeEvent
160−
161−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (WriterT w m) where
162− type ReadPhase (WriterT w m) = ReadPhase m
163− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
164− runHostFrame = lift . runHostFrame
165−
166−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StateT s m) where
167− newEventWithTrigger = lift . newEventWithTrigger
168− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
169−
170−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (StateT r m) where
171− subscribeEvent = lift . subscribeEvent
172−
173−instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
174− type ReadPhase (StateT s m) = ReadPhase m
175− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
176− runHostFrame = lift . runHostFrame
177−
178−
179−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
180− newEventWithTrigger = lift . newEventWithTrigger
181− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
182−
183−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
184− subscribeEvent = lift . subscribeEvent
185−
186−instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
187− type ReadPhase (Strict.StateT s m) = ReadPhase m
188− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
189− runHostFrame = lift . runHostFrame
190−
191−
192−
193−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
194− newEventWithTrigger = lift . newEventWithTrigger
195− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
196−
197−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ContT r m) where
198− subscribeEvent = lift . subscribeEvent
199−
200−instance MonadReflexHost t m => MonadReflexHost t (ContT r m) where
201− type ReadPhase (ContT r m) = ReadPhase m
202− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
203− runHostFrame = lift . runHostFrame
204−
205−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ExceptT e m) where
206− newEventWithTrigger = lift . newEventWithTrigger
207− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
208−
209−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ExceptT r m) where
210− subscribeEvent = lift . subscribeEvent
211−
212−instance MonadReflexHost t m => MonadReflexHost t (ExceptT e m) where
213− type ReadPhase (ExceptT e m) = ReadPhase m
214− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
215− runHostFrame = lift . runHostFrame
216−
217−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (RWST r w s m) where
218− newEventWithTrigger = lift . newEventWithTrigger
219− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
220−
221−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (RWST r w s m) where
222− subscribeEvent = lift . subscribeEvent
223−
224−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (RWST r w s m) where
225− type ReadPhase (RWST r w s m) = ReadPhase m
226− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
227− runHostFrame = lift . runHostFrame
src/Reflex/Spider.hsdeleted−8
@@ -1,8 +0,0 @@
1−module Reflex.Spider ( Spider
2− , SpiderEnv
3− , Global
4− , SpiderHost
5− , runSpiderHost
6− ) where
7−
8−import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hsdeleted−2267
@@ -1,2267 +0,0 @@
1−{-# LANGUAGE CPP, ExistentialQuantification, GADTs, ScopedTypeVariables, TypeFamilies, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, BangPatterns, UndecidableInstances, EmptyDataDecls, RecursiveDo, RoleAnnotations, LambdaCase, TypeOperators, FlexibleContexts, ConstraintKinds, MagicHash, UnboxedTuples, FunctionalDependencies, MultiWayIf #-}
2−module Reflex.Spider.Internal (Spider, SpiderEnv (..), Global, SpiderHost, runSpiderHost, SpiderEventHandle (..), showSubscriberType, showEventType, ComputeM (..), MergeSubscribedParent (..), HasSpiderEnv (..), newSpiderEnv, SpiderPullM (..), SpiderPushM (..), ReadPhase (..)) where
3−
4−import qualified Reflex.Class as R
5−import qualified Reflex.Host.Class as R
6−import Data.WeakBag (WeakBag, WeakBagTicket)
7−import qualified Data.WeakBag as WeakBag
8−
9−import Data.List
10−import Data.IORef
11−import System.Mem.Weak
12−import Data.Foldable
13−import Data.Traversable
14−import Control.Monad hiding (mapM, mapM_, forM_, forM, sequence)
15−import Control.Monad.Identity hiding (mapM, mapM_, forM_, forM, sequence)
16−import Control.Monad.Reader hiding (mapM, mapM_, forM_, forM, sequence)
17−import Control.Monad.State hiding (mapM, mapM_, forM_, forM, sequence)
18−import Control.Applicative -- Unconditionally import, because otherwise it breaks on GHC 7.10.1RC2
19−import Data.Some (Some)
20−import qualified Data.Some as Some
21−import Data.Dependent.Map (DMap, DSum (..))
22−import qualified Data.Dependent.Map as DMap
23−import Data.GADT.Compare
24−import Data.Maybe
25−import Data.IntMap.Strict (IntMap)
26−import qualified Data.IntMap.Strict as IntMap
27−import Control.Monad.Ref
28−import Control.Monad.Exception
29−import Data.Monoid ((<>))
30−import Data.Functor.Compose
31−import Control.Exception
32−import Control.Concurrent
33−import Data.Function
34−import Data.These
35−import Data.Align
36−
37−import GHC.IORef (IORef (..))
38−import GHC.Base (IO (..))
39−import GHC.Stack
40−
41−import System.IO.Unsafe
42−import Unsafe.Coerce
43−import Data.Coerce
44−import Control.Monad.Primitive
45−
46−import qualified Data.List.NonEmpty as NonEmpty
47−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
48−import Data.Tree (Tree (..), Forest, drawForest)
49−
50−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
51−import Prelude hiding (mapM, mapM_, any, sequence, concat)
52−
53−type SubscriberList a = WeakBag (Subscriber a)
54−type SubscriberListTicket a = WeakBagTicket (Subscriber a)
55−
56−debugPropagate :: Bool
57−
58−debugInvalidateHeight :: Bool
59−
60−debugInvalidate :: Bool
61−
62−#ifdef DEBUG
63−
64−#define DEBUG_NODEIDS
65−
66−debugPropagate = True
67−
68−debugInvalidateHeight = False
69−
70−debugInvalidate = True
71−
72−class HasNodeId a where
73− getNodeId :: a -> Int
74−
75−instance HasNodeId (Hold p a) where
76− getNodeId = holdNodeId
77−
78−instance HasNodeId (PushSubscribed a b) where
79− getNodeId = pushSubscribedNodeId
80−
81−instance HasNodeId (SwitchSubscribed a) where
82− getNodeId = switchSubscribedNodeId
83−
84−instance HasNodeId (MergeSubscribed a) where
85− getNodeId = mergeSubscribedNodeId
86−
87−instance HasNodeId (FanSubscribed a) where
88− getNodeId = fanSubscribedNodeId
89−
90−instance HasNodeId (CoincidenceSubscribed a) where
91− getNodeId = coincidenceSubscribedNodeId
92−
93−instance HasNodeId (RootSubscribed a) where
94− getNodeId = rootSubscribedNodeId
95−
96−instance HasNodeId (Pull a) where
97− getNodeId = pullNodeId
98−
99−{-# INLINE showNodeId #-}
100−showNodeId :: HasNodeId a => a -> String
101−showNodeId = ("#"<>) . show . getNodeId
102−
103−#else
104−
105−debugPropagate = False
106−
107−debugInvalidateHeight = False
108−
109−debugInvalidate = False
110−
111−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
112−{-# INLINE showNodeId #-}
113−showNodeId :: a -> String
114−showNodeId _ = ""
115−
116−#endif
117−
118−#ifdef DEBUG_NODEIDS
119−{-# NOINLINE nextNodeIdRef #-}
120−nextNodeIdRef :: IORef Int
121−nextNodeIdRef = unsafePerformIO $ newIORef 1
122−
123−newNodeId :: IO Int
124−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
125−
126−{-# NOINLINE unsafeNodeId #-}
127−unsafeNodeId :: a -> Int
128−unsafeNodeId a = unsafePerformIO $ do
129− touch a
130− newNodeId
131−#endif
132−
133−--TODO: Figure out why certain things are not 'representational', then make them representational so we can use coerce
134−--type role Hold representational
135−data Hold p a
136− = Hold { holdValue :: !(IORef a)
137− , holdInvalidators :: !(IORef [Weak Invalidator])
138− , holdParent :: !(IORef (Either (Event (p a)) (EventSubscription (p a)))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
139−#ifdef DEBUG_NODEIDS
140− , holdNodeId :: Int
141−#endif
142− }
143−
144−data EventEnv x
145− = EventEnv { eventEnvSpiderEnv :: !(SpiderEnv x)
146− , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
147− , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
148− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
149− , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
150− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
151− , eventEnvRootClears :: !(IORef [SomeRootClear])
152− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
153− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
154− , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
155− }
156−
157−{-# INLINE runEventM #-}
158−runEventM :: EventM x a -> EventEnv x -> IO a
159−runEventM = runReaderT . unEventM
160−
161−class MonadIO m => Defer a m where
162− getDeferralQueue :: m (IORef [a])
163−
164−{-# INLINE defer #-}
165−defer :: Defer a m => a -> m ()
166−defer a = do
167− q <- getDeferralQueue
168− liftIO $ modifyIORef' q (a:)
169−
170−instance Defer SomeAssignment (EventM x) where
171− {-# INLINE getDeferralQueue #-}
172− getDeferralQueue = EventM $ asks eventEnvAssignments
173−
174−instance Defer SomeHoldInit (EventM x) where
175− {-# INLINE getDeferralQueue #-}
176− getDeferralQueue = EventM $ asks eventEnvHoldInits
177−
178−instance Defer SomeHoldInit BehaviorM where
179− {-# INLINE getDeferralQueue #-}
180− getDeferralQueue = BehaviorM $ asks snd
181−
182−instance Defer SomeMergeUpdate (EventM x) where
183− {-# INLINE getDeferralQueue #-}
184− getDeferralQueue = EventM $ asks eventEnvMergeUpdates
185−
186−instance Defer SomeMergeInit (EventM x) where
187− {-# INLINE getDeferralQueue #-}
188− getDeferralQueue = EventM $ asks eventEnvMergeInits
189−
190−class HasCurrentHeight m where
191− getCurrentHeight :: m Height
192− scheduleMerge :: Height -> MergeSubscribed a -> m ()
193−
194−instance HasCurrentHeight (EventM x) where
195− {-# INLINE getCurrentHeight #-}
196− getCurrentHeight = EventM $ do
197− heightRef <- asks eventEnvCurrentHeight
198− liftIO $ readIORef heightRef
199− {-# INLINE scheduleMerge #-}
200− scheduleMerge height subscribed = EventM $ do
201− delayedRef <- asks eventEnvDelayedMerges
202− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [DelayedMerge subscribed]
203−
204−class HasSpiderEnv x m | m -> x where
205− askSpiderEnv :: m (SpiderEnv x)
206−
207−instance HasSpiderEnv x (EventM x) where
208− {-# INLINE askSpiderEnv #-}
209− askSpiderEnv = EventM $ asks eventEnvSpiderEnv
210−
211−instance HasSpiderEnv x (SpiderHost x) where
212− {-# INLINE askSpiderEnv #-}
213− askSpiderEnv = SpiderHost ask
214−
215−putCurrentHeight :: Height -> EventM x ()
216−putCurrentHeight h = EventM $ do
217− heightRef <- asks eventEnvCurrentHeight
218− liftIO $ writeIORef heightRef $! h
219−
220−instance Defer SomeClear (EventM x) where
221− {-# INLINE getDeferralQueue #-}
222− getDeferralQueue = EventM $ asks eventEnvClears
223−
224−{-# INLINE scheduleClear #-}
225−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
226−scheduleClear r = defer $ SomeClear r
227−
228−instance Defer SomeRootClear (EventM x) where
229− {-# INLINE getDeferralQueue #-}
230− getDeferralQueue = EventM $ asks eventEnvRootClears
231−
232−{-# INLINE scheduleRootClear #-}
233−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
234−scheduleRootClear r = defer $ SomeRootClear r
235−
236−instance Defer SomeResetCoincidence (EventM x) where
237− {-# INLINE getDeferralQueue #-}
238− getDeferralQueue = EventM $ asks eventEnvResetCoincidences
239−
240−-- Note: hold cannot examine its event until after the phase is over
241−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> EventM x (Hold p a) #-}
242−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> ComputeM (Hold p a) #-}
243−hold :: (R.Patch p, Defer SomeHoldInit m) => a -> Event (p a) -> m (Hold p a)
244−hold v0 e = do
245− valRef <- liftIO $ newIORef v0
246− invsRef <- liftIO $ newIORef []
247− parentRef <- liftIO $ newIORef $ Left e
248−#ifdef DEBUG_NODEIDS
249− nodeId <- liftIO newNodeId
250−#endif
251− let h = Hold
252− { holdValue = valRef
253− , holdInvalidators = invsRef
254− , holdParent = parentRef
255−#ifdef DEBUG_NODEIDS
256− , holdNodeId = nodeId
257−#endif
258− }
259− defer $ SomeHoldInit h
260− return h
261−
262−{-
263−mkWeakIORefKey :: IORef a -> b -> IO () -> IO (Weak b)
264−mkWeakIORefKey r@(IORef (STRef r#)) b (IO f) = IO $ \s ->
265− case mkWeak# r# b f s of (# s1, w #) -> (# s1, Weak w #)
266−-}
267−
268−{-# INLINE getHoldEventSubscription #-}
269−getHoldEventSubscription :: forall m p a x. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeClear m, R.Patch p, HasCurrentHeight m, Defer SomeMergeInit m, Defer SomeResetCoincidence m, HasSpiderEnv x m) => Hold p a -> m (EventSubscription (p a))
270−getHoldEventSubscription h = do
271− ep <- liftIO $ readIORef $ holdParent h
272− case ep of
273− Right subd -> return subd
274− Left e -> do
275− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
276− {-
277− spiderEnv <- askSpiderEnv
278− weakSubscriptionRef <- liftIO $ mkWeakIORef subscriptionRef $ return ()
279− wh <- liftIO $ mkWeakIORefKey (holdValue h) h $ do
280− msr <- deRefWeak weakSubscriptionRef
281− forM_ msr $ \sr -> do
282− subscription <- readIORef sr
283− atomicModifyIORef (_spiderEnv_toUnsubscribe spiderEnv) $ \x -> (SomeEventSubscription subscription : x, ())
284−-- return ()
285− {-
286− -- What can we do? 1) Detect GCs at the SpiderEnv level, then go over the tree
287− 2) Have a finalizer on every SubscriberListNode that only runs if its parent is still alive, and does a normal unsubscribe if so; this would seem to double the number of weakRefs we use, but perhaps not the weakDepth, so maybe it's OK; we will probably need to move the regular call to unsubscribe into the finalizer and then use finalize instead of that call; we can have a strong reference to the SubscriberListNode, which means we just need a single weak reference to each EventSubscribed, which we can reuse in all of the finalizers that need to (potentially) unsubscribe from it
288−-}
289− -}
290− subscription@(EventSubscription _ subd) <- subscribe e =<< liftIO (newSubscriberHold h)
291− liftIO $ writeIORef subscriptionRef $! subscription
292− occ <- liftIO $ readEventSubscribed subd
293− case occ of
294− Nothing -> return ()
295− Just o -> do
296− old <- liftIO $ readIORef $ holdValue h
297− case R.apply o old of
298− Nothing -> return ()
299− Just new -> do
300− -- Need to evaluate these so that we don't retain the Hold itself
301− v <- liftIO $ evaluate $ holdValue h
302− i <- liftIO $ evaluate $ holdInvalidators h
303− defer $ SomeAssignment v i new
304− liftIO $ writeIORef (holdParent h) $! Right subscription
305− return subscription
306−
307−type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
308−
309−--type role BehaviorM representational
310−-- BehaviorM can sample behaviors
311−newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT BehaviorEnv IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
312−
313−instance Monad BehaviorM where
314− {-# INLINE (>>=) #-}
315− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
316− {-# INLINE (>>) #-}
317− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
318− {-# INLINE return #-}
319− return x = BehaviorM $ return x
320− {-# INLINE fail #-}
321− fail s = BehaviorM $ fail s
322−
323−data BehaviorSubscribed a
324− = forall p. BehaviorSubscribedHold (Hold p a)
325− | BehaviorSubscribedPull (PullSubscribed a)
326−
327−data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
328−
329−--type role PullSubscribed representational
330−data PullSubscribed a
331− = PullSubscribed { pullSubscribedValue :: !a
332− , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
333− , pullSubscribedOwnInvalidator :: !Invalidator
334− , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
335− }
336−
337−--type role Pull representational
338−data Pull a
339− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
340− , pullCompute :: !(BehaviorM a)
341−#ifdef DEBUG_NODEIDS
342− , pullNodeId :: Int
343−#endif
344− }
345−
346−data Invalidator
347− = forall a. InvalidatorPull (Pull a)
348− | forall a. InvalidatorSwitch (SwitchSubscribed a)
349−
350−data RootSubscribed a = forall k. GCompare k => RootSubscribed
351− { rootSubscribedKey :: !(k a)
352− , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
353− , rootSubscribedSubscribers :: !(SubscriberList a)
354− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
355− , rootSubscribedUninit :: IO ()
356− , rootSubscribedWeakSelf :: !(IORef (Weak (RootSubscribed a))) --TODO: Can we make this a lazy non-IORef and then force it manually to avoid an indirection each time we use it?
357−#ifdef DEBUG_NODEIDS
358− , rootSubscribedNodeId :: Int
359−#endif
360− }
361−
362−data Root (k :: * -> *)
363− = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
364− , rootSubscribed :: !(IORef (DMap k RootSubscribed))
365− , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
366− }
367−
368−data SomeHoldInit = forall p a. R.Patch p => SomeHoldInit !(Hold p a)
369−
370−data SomeMergeUpdate = forall k. GCompare k => SomeMergeUpdate (R.PatchDMap (DMap k Event)) (MergeSubscribed k)
371−
372−data SomeMergeInit = forall k. GCompare k => SomeMergeInit !(MergeSubscribed k) !(Event (R.PatchDMap (DMap k Event)))
373−
374−data SomeEventSubscription = forall a. SomeEventSubscription (EventSubscription a)
375−
376−-- EventM can do everything BehaviorM can, plus create holds
377−newtype EventM x a = EventM { unEventM :: ReaderT (EventEnv x) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException) -- The environment should be Nothing if we are not in a frame, and Just if we are - in which case it is a list of assignments to be done after the frame is over
378−
379−data PushSubscribed a b
380− = PushSubscribed { pushSubscribedCachedSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
381− , pushSubscribedOccurrence :: !(IORef (Maybe b)) -- If the current height is less than our height, this should always be Nothing; during our height, this will get filled in at some point, always before our children are notified; after our height, this will be filled in with the correct value (Nothing if we are not firing, Just if we are)
382− , pushSubscribedHeight :: !(IORef Height)
383− , pushSubscribedSubscribers :: !(SubscriberList b)
384− , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die --TODO: Probably unneeded now
385− , pushSubscribedWeakSelf :: !(IORef (Weak (PushSubscribed a b)))
386− , pushSubscribedParent :: !(EventSubscription a)
387−#ifdef DEBUG_NODEIDS
388− , pushSubscribedNodeId :: Int
389−#endif
390− }
391−
392−newtype ComputeM a = ComputeM { unComputeM :: ReaderT (IORef [SomeHoldInit]) IO a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
393−
394−instance Defer SomeHoldInit ComputeM where
395− getDeferralQueue = ComputeM ask
396−
397−{-# SPECIALIZE runComputeM :: ComputeM a -> EventM x a #-}
398−runComputeM :: Defer SomeHoldInit m => ComputeM a -> m a
399−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
400−
401−data Push a b
402− = Push { pushCompute :: !(a -> ComputeM (Maybe b)) -- Compute the current firing value; assumes that its parent has been computed --TODO: This only uses Defer SomeHoldInit EventM; perhaps only the hold init queue should be passed in (also, perhaps those hold inits can be accelerated - can there be a loop we would need to worry about?)
403− , pushParent :: !(Event a)
404− , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
405− }
406−
407−data MergeSubscribedParent k a
408− = MergeSubscribedParent { mergeSubscribedParentSubscription :: !(EventSubscription a)
409− , mergeSubscribedParentSubscriber :: !(Subscriber a)
410− }
411−
412−data HeightBag = HeightBag
413− { _heightBag_size :: {-# UNPACK #-} !Int
414− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
415− }
416− deriving (Show, Read, Eq, Ord)
417−
418−heightBagEmpty :: HeightBag
419−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
420−
421−heightBagSize :: HeightBag -> Int
422−heightBagSize = _heightBag_size
423−
424−heightBagFromList :: [Height] -> HeightBag
425−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
426−
427−heightBagAdd :: Height -> HeightBag -> HeightBag
428−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
429−
430−heightBagRemove :: Height -> HeightBag -> HeightBag
431−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
432− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
433− Just old -> HeightBag (pred s) $ case old of
434− 0 -> IntMap.delete h c
435− _ -> IntMap.insert h (pred old) c
436−
437−heightBagMax :: HeightBag -> Height
438−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
439− Just ((h, _), _) -> Height h
440− Nothing -> zeroHeight
441−
442−heightBagVerify :: HeightBag -> HeightBag
443−#ifdef DEBUG
444−heightBagVerify b@(HeightBag s c) = if
445− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
446− -> error $ "heightBagVerify: size doesn't match: " <> show b
447− | unHeight invalidHeight `IntMap.member` c
448− -> error $ "heightBagVerify: contains invalid height: " <> show b
449− | otherwise -> b
450−#else
451−heightBagVerify = id
452−#endif
453−
454−data MergeSubscribed k
455− = MergeSubscribed { mergeSubscribedCachedSubscribed :: !(IORef (Maybe (MergeSubscribed k))) -- From the original merge
456− , mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
457− , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
458− , mergeSubscribedHeight :: !(IORef Height)
459− , mergeSubscribedHeightBag :: !(IORef HeightBag)
460− , mergeSubscribedSubscribers :: !(SubscriberList (DMap k Identity))
461− , mergeSubscribedChange :: !(IORef (Subscriber (R.PatchDMap (DMap k Event)), EventSubscribed (R.PatchDMap (DMap k Event))))
462− , mergeSubscribedParents :: !(IORef (DMap k (MergeSubscribedParent k)))
463− , mergeSubscribedWeakSelf :: !(IORef (Weak (MergeSubscribed k)))
464−#ifdef DEBUG_NODEIDS
465− , mergeSubscribedNodeId :: Int
466−#endif
467− }
468−
469−--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
470−data Merge k
471− = Merge { mergeParents :: !(Dynamic R.PatchDMap (DMap k Event))
472− , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
473− }
474−
475−data FanSubscribedChildren k a = FanSubscribedChildren
476− { _fanSubscribedChildren_list :: !(SubscriberList a)
477− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
478− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
479− }
480−
481−data FanSubscribed k
482− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
483− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
484− , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
485−#ifdef DEBUG_NODEIDS
486− , fanSubscribedNodeId :: Int
487−#endif
488− }
489−
490−data Fan k
491− = Fan { fanParent :: !(Event (DMap k Identity))
492− , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
493− }
494−
495−data SwitchSubscribed a
496− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
497− , switchSubscribedOccurrence :: !(IORef (Maybe a))
498− , switchSubscribedHeight :: !(IORef Height)
499− , switchSubscribedSubscribers :: !(SubscriberList a)
500− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
501− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
502− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
503− , switchSubscribedParent :: !(Behavior (Event a))
504− , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
505− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
506−#ifdef DEBUG_NODEIDS
507− , switchSubscribedNodeId :: Int
508−#endif
509− }
510−
511−data Switch a
512− = Switch { switchParent :: !(Behavior (Event a))
513− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
514− }
515−
516−data CoincidenceSubscribed a
517− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
518− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
519− , coincidenceSubscribedSubscribers :: !(SubscriberList a)
520− , coincidenceSubscribedHeight :: !(IORef Height)
521− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
522− , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
523− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
524− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
525−#ifdef DEBUG_NODEIDS
526− , coincidenceSubscribedNodeId :: Int
527−#endif
528− }
529−
530−data Coincidence a
531− = Coincidence { coincidenceParent :: !(Event (Event a))
532− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
533− }
534−
535−data Subscriber a
536− = forall b. SubscriberPush (a -> ComputeM (Maybe b)) (PushSubscribed a b) --TODO: Why does this have the pushCompute in it?
537− | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
538− | forall k. (a ~ R.PatchDMap (DMap k Event), GCompare k) => SubscriberMergeChange (MergeSubscribed k)
539− | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
540− | forall p b. (a ~ p b, R.Patch p) => SubscriberHold !(Hold p b)
541− | forall b. (a ~ Identity b) => SubscriberHoldIdentity !(Hold Identity b)
542− | SubscriberSwitch (SwitchSubscribed a)
543− | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
544− | SubscriberCoincidenceInner (CoincidenceSubscribed a)
545− | SubscriberHandle
546−
547−showSubscriberType :: Subscriber a -> String
548−showSubscriberType = \case
549− SubscriberPush _ _ -> "SubscriberPush"
550− SubscriberMerge _ _ -> "SubscriberMerge"
551− SubscriberFan _ -> "SubscriberFan"
552− SubscriberMergeChange _ -> "SubscriberMergeChange"
553− SubscriberHold _ -> "SubscriberHold"
554− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
555− SubscriberSwitch _ -> "SubscriberSwitch"
556− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
557− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
558− SubscriberHandle -> "SubscriberHandle"
559−
560−data Event a
561− = forall k. GCompare k => EventRoot !(k a) !(Root k)
562− | EventNever
563− | forall b. EventPush !(Push b a)
564− | forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
565− | forall k. GCompare k => EventFan !(k a) !(Fan k)
566− | EventSwitch !(Switch a)
567− | EventCoincidence !(Coincidence a)
568− | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
569− | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
570− | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
571− | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
572−
573−showEventType :: Event a -> String
574−showEventType = \case
575− EventRoot _ _ -> "EventRoot"
576− EventNever -> "EventNever"
577− EventPush _ -> "EventPush"
578− EventMerge _ -> "EventMerge"
579− EventFan _ _ -> "EventFan"
580− EventSwitch _ -> "EventSwitch"
581− EventCoincidence _ -> "EventCoincidence"
582− EventHold _ -> "EventHold"
583− EventDyn _ -> "EventDyn"
584− EventHoldIdentity _ -> "EventHoldIdentity"
585− EventDynIdentity _ -> "EventDynIdentity"
586−
587−data EventSubscription a = EventSubscription
588− { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
589− , _eventSubscription_subscribed :: !(EventSubscribed a)
590− }
591−
592−data EventSubscribed a
593− = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
594− | EventSubscribedNever
595− | forall b. EventSubscribedPush !(PushSubscribed b a)
596− | forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
597− | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
598− | EventSubscribedSwitch !(SwitchSubscribed a)
599− | EventSubscribedCoincidence !(CoincidenceSubscribed a)
600−
601−unsubscribe :: EventSubscription a -> IO ()
602−unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
603−
604−-- These function are constructor functions that are marked NOINLINE so they are
605−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
606−-- so any weak references that are attached to the constructors will have their
607−-- finalizer run. Using the opaque constructor, does not see the
608−-- constructor application, so it behaves like an IORef and cannot be fused away.
609−--
610−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
611−-- the weak pointer to it in some cases.
612−
613−--TODO: I think all of these noinlines are now unnecessary
614−{-# NOINLINE newSubscriberPush #-}
615−newSubscriberPush :: (a -> ComputeM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
616−newSubscriberPush compute subd = return $! SubscriberPush compute subd
617−
618−{-# RULES
619− "newSubscriberHold/Identity" forall h. newSubscriberHold h = newSubscriberHoldIdentity h
620− #-}
621−{-# NOINLINE newSubscriberHold #-}
622−newSubscriberHold :: R.Patch p => Hold p a -> IO (Subscriber (p a))
623−newSubscriberHold h = return $! SubscriberHold h
624−
625−{-# NOINLINE newSubscriberHoldIdentity #-}
626−newSubscriberHoldIdentity :: Hold Identity a -> IO (Subscriber (Identity a))
627−newSubscriberHoldIdentity h = return $! SubscriberHoldIdentity h
628−
629−{-# NOINLINE newSubscriberMerge #-}
630−newSubscriberMerge :: GCompare k => k a -> MergeSubscribed k -> IO (Subscriber a)
631−newSubscriberMerge k subd = return $! SubscriberMerge k subd
632−
633−{-# NOINLINE newSubscriberMergeChange #-}
634−newSubscriberMergeChange :: GCompare k => MergeSubscribed k -> IO (Subscriber (R.PatchDMap (DMap k Event)))
635−newSubscriberMergeChange subd = return $! SubscriberMergeChange subd
636−
637−{-# NOINLINE newSubscriberFan #-}
638−newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
639−newSubscriberFan subd = return $! SubscriberFan subd
640−
641−{-# NOINLINE newSubscriberSwitch #-}
642−newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
643−newSubscriberSwitch subd = return $! SubscriberSwitch subd
644−
645−{-# NOINLINE newSubscriberCoincidenceOuter #-}
646−newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
647−newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
648−
649−{-# NOINLINE newSubscriberCoincidenceInner #-}
650−newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
651−newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
652−
653−{-# NOINLINE newInvalidatorSwitch #-}
654−newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
655−newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
656−
657−{-# NOINLINE newInvalidatorPull #-}
658−newInvalidatorPull :: Pull a -> IO Invalidator
659−newInvalidatorPull p = return $! InvalidatorPull p
660−
661−data Dynamic p a
662− = DynamicHold !(Hold p a)
663− | p ~ Identity => DynamicHoldIdentity !(Hold Identity a)
664− | DynamicConst !a
665− | DynamicDyn !(Dyn p a)
666− | p ~ Identity => DynamicDynIdentity !(Dyn Identity a)
667−
668−{-# INLINE current #-}
669−current :: R.Patch p => Dynamic p a -> Behavior a
670−current = \case
671− DynamicHold h -> BehaviorHold h
672− DynamicHoldIdentity h -> BehaviorHoldIdentity h
673− DynamicConst a -> BehaviorConst a
674− DynamicDyn d -> BehaviorDyn d
675− DynamicDynIdentity d -> BehaviorDynIdentity d
676−
677−--TODO: If you only need updated, not current, can we avoid actually constructing the Hold?
678−{-# INLINE updated #-}
679−updated :: R.Patch p => Dynamic p a -> Event (p a)
680−updated = \case
681− DynamicHold h -> EventHold h
682− DynamicHoldIdentity h -> EventHoldIdentity h
683− DynamicConst _ -> EventNever
684− DynamicDyn d -> EventDyn d
685− DynamicDynIdentity d -> EventDynIdentity d
686−
687−newMapDyn :: (a -> b) -> Dynamic Identity a -> Dynamic Identity b
688−newMapDyn f d = DynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ current d) (fmap (Identity . f . runIdentity) $ updated d)
689−
690−instance Functor (Dynamic Identity) where
691− fmap f d = newMapDyn f d
692−
693−instance Applicative (Dynamic Identity) where
694− pure = DynamicConst
695− (<*>) = zipDynWith ($)
696− _ *> b = b
697− a <* _ = a
698−
699−instance R.FunctorMaybe Event where
700− fmapMaybe f = push $ return . f
701−
702−instance Align Event where
703− nil = EventNever
704− align ea eb = R.fmapMaybe R.dmapToThese $ merge $ DynamicConst $ DMap.fromList [R.LeftTag :=> ea, R.RightTag :=> eb]
705−
706−--TODO: Avoid the duplication between this and R.zipDynWith
707−zipDynWith :: (a -> b -> c) -> Dynamic Identity a -> Dynamic Identity b -> Dynamic Identity c
708−zipDynWith f da db =
709− let eab = align (updated da) (updated db)
710− ec = flip push eab $ \o -> do
711− (a, b) <- case o of
712− This (Identity a) -> do
713− b <- readBehaviorUntracked $ current db
714− return (a, b)
715− That (Identity b) -> do
716− a <- readBehaviorUntracked $ current da
717− return (a, b)
718− These (Identity a) (Identity b) -> return (a, b)
719− return $ Just $ Identity $ f a b
720− in DynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (current da) <*> readBehaviorUntracked (current db)) ec
721−
722−instance Monad (Dynamic Identity) where
723− {-# INLINE return #-}
724− return = pure
725− {-# INLINE (>>=) #-}
726− x >>= f = DynamicDynIdentity $ newJoinDyn $ newMapDyn f x --TODO: (>>), fail
727− {-# INLINE (>>) #-}
728− _ >> y = y
729− {-# INLINE fail #-}
730− fail _ = error "Dynamic does not support 'fail'"
731−
732−newtype Dyn p a = Dyn { unDyn :: IORef (Either (BehaviorM a, Event (p a)) (Hold p a)) }
733−
734−unsafeDyn :: BehaviorM a -> Event (p a) -> Dyn p a
735−unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
736− where x = (readV0, v')
737−
738−newJoinDyn :: Dynamic Identity (Dynamic Identity a) -> Dyn Identity a
739−newJoinDyn d =
740− let readV0 = readBehaviorTracked . current =<< readBehaviorTracked (current d)
741− eOuter = push (fmap (Just . Identity) . readBehaviorUntracked . current . runIdentity) $ updated d
742− eInner = switch $ fmap updated $ current d
743− eBoth = coincidence $ fmap (updated . runIdentity) $ updated d
744− v' = unSpiderEvent $ R.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
745− in unsafeDyn readV0 v'
746−
747−--type role Behavior representational
748−data Behavior a
749− = forall p. BehaviorHold !(Hold p a)
750− | BehaviorHoldIdentity !(Hold Identity a)
751− | BehaviorConst !a
752− | BehaviorPull !(Pull a)
753− | BehaviorDynIdentity !(Dyn Identity a)
754− | forall p. R.Patch p => BehaviorDyn !(Dyn p a)
755−
756−-- ResultM can read behaviors and events
757−type ResultM = EventM
758−
759−{-# NOINLINE unsafeNewIORef #-}
760−unsafeNewIORef :: a -> b -> IORef b
761−unsafeNewIORef _ b = unsafePerformIO $ newIORef b
762−
763−instance Functor Event where
764− fmap f = push $ return . Just . f
765−
766−instance Functor Behavior where
767− fmap f = pull . liftM f . readBehaviorTracked
768−
769−{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
770−push :: forall a b. (a -> ComputeM (Maybe b)) -> Event a -> Event b
771−push f e = EventPush $ Push
772− { pushCompute = f
773− , pushParent = e
774− , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
775− }
776−-- DISABLED: {- RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
777−
778−{-# NOINLINE pull #-}
779−pull :: BehaviorM a -> Behavior a
780−pull a = BehaviorPull $ Pull
781− { pullCompute = a
782− , pullValue = unsafeNewIORef a Nothing
783−#ifdef DEBUG_NODEIDS
784− , pullNodeId = unsafeNodeId a
785−#endif
786− }
787−
788−{-# NOINLINE switch #-}
789−switch :: Behavior (Event a) -> Event a
790−switch a = EventSwitch $ Switch
791− { switchParent = a
792− , switchSubscribed = unsafeNewIORef a Nothing
793− }
794−
795−coincidence :: Event (Event a) -> Event a
796−coincidence a = EventCoincidence $ Coincidence
797− { coincidenceParent = a
798− , coincidenceSubscribed = unsafeNewIORef a Nothing
799− }
800−
801−propagateAndUpdateSubscribersRef :: SubscriberList a -> a -> EventM x ()
802−propagateAndUpdateSubscribersRef subscribersRef a = do
803− propagate a subscribersRef
804−
805−-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
806−run :: [DSum RootTrigger Identity] -> ResultM x b -> SpiderHost x b
807−run roots after = do
808− tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
809− spiderEnv <- SpiderHost ask
810− result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
811− rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
812− occBefore <- liftIO $ do
813− occBefore <- readIORef occRef
814− writeIORef occRef $! DMap.insert k a occBefore
815− return occBefore
816− if DMap.null occBefore
817− then do scheduleRootClear occRef
818− return $ Just r
819− else return Nothing
820− forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
821− propagateAndUpdateSubscribersRef subscribersRef a
822− delayedRef <- EventM $ asks eventEnvDelayedMerges
823− let go = do
824− delayed <- liftIO $ readIORef delayedRef
825− case IntMap.minViewWithKey delayed of
826− Nothing -> return ()
827− Just ((currentHeight, cur), future) -> do
828− tracePropagate $ "Running height " ++ show currentHeight
829− putCurrentHeight $ Height currentHeight
830− liftIO $ writeIORef delayedRef $! future
831− forM_ cur $ \d -> case d of
832− DelayedMerge subscribed -> do
833− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
834− case height `compare` Height currentHeight of
835− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
836− GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
837− EQ -> do
838− m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
839− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! DMap.empty
840− --TODO: Assert that m is not empty
841− liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $! Just m
842− scheduleClear $ mergeSubscribedOccurrence subscribed
843− propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
844− go
845− go
846− putCurrentHeight maxBound
847− after
848− tracePropagate "Done running an event frame"
849− return result
850−
851−data SomeClear = forall a. SomeClear (IORef (Maybe a))
852−
853−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
854−
855−data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
856−
857−data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
858−
859−debugFinalize :: Bool
860−debugFinalize = False
861−
862−mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
863−mkWeakPtrWithDebug x debugNote = do
864− x' <- evaluate x
865− mkWeakPtr x' $
866− if debugFinalize
867− then Just $ putStrLn $ "finalizing: " ++ debugNote
868− else Nothing
869−
870−type WeakList a = [Weak a]
871−
872−{-# INLINE withIncreasedDepth #-}
873−#ifdef DEBUG
874−withIncreasedDepth :: CanTrace x m => m a -> m a
875−withIncreasedDepth a = do
876− spiderEnv <- askSpiderEnv
877− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
878− result <- a
879− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
880− return result
881−#else
882−withIncreasedDepth :: m a -> m a
883−withIncreasedDepth = id
884−#endif
885−
886−type CanTrace x m = (HasSpiderEnv x m, MonadIO m)
887−
888−{-# INLINE tracePropagate #-}
889−tracePropagate :: (CanTrace x m) => String -> m ()
890−tracePropagate = traceWhen debugPropagate
891−
892−{-# INLINE traceSubscribe #-}
893−traceSubscribe :: String -> IO ()
894−traceSubscribe = when debugSubscribe . liftIO . putStrLn
895−
896−{-# INLINE traceInvalidate #-}
897−traceInvalidate :: String -> IO ()
898−traceInvalidate = when debugInvalidate . liftIO . putStrLn
899−
900−{-# INLINE traceWhen #-}
901−traceWhen :: (CanTrace x m) => Bool -> String -> m ()
902−traceWhen b message = traceMWhen b $ return message
903−
904−{-# INLINE traceMWhen #-}
905−traceMWhen :: (CanTrace x m) => Bool -> m String -> m ()
906−traceMWhen b getMessage = when b $ do
907− message <- getMessage
908−#ifdef DEBUG
909− spiderEnv <- askSpiderEnv
910− d <- liftIO $ readIORef $ _spiderEnv_depth spiderEnv
911−#else
912− let d = 0
913−#endif
914− liftIO $ putStrLn $ replicate d ' ' <> message
915−
916−whoCreatedIORef :: IORef a -> IO [String]
917−whoCreatedIORef (IORef a) = whoCreated $! a
918−
919−whoCreatedEventSubscribed :: EventSubscribed a -> IO [String]
920−whoCreatedEventSubscribed = \case
921− EventSubscribedRoot _ -> return ["root"]
922− EventSubscribedNever -> return ["never"]
923− EventSubscribedPush subscribed -> whoCreatedIORef $ pushSubscribedCachedSubscribed subscribed
924− EventSubscribedFan _ subscribed -> whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
925− EventSubscribedMerge subscribed -> whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
926− EventSubscribedSwitch subscribed -> whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
927− EventSubscribedCoincidence subscribed -> whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
928−
929−walkInvalidHeightParents :: EventSubscribed a -> IO [Some EventSubscribed]
930−walkInvalidHeightParents s0 = do
931− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
932− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
933− when (h == invalidHeight) $ do
934− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
935− modify (Some.This s :)
936− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
937− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
938− return subscribers
939−
940−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
941−groupByHead = \case
942− [] -> []
943− (x :| xs) : t -> case groupByHead t of
944− [] -> [(x, xs :| [])]
945− l@((y, yss) : t')
946− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
947− | otherwise -> (x, xs :| []) : l
948−
949−listsToForest :: Eq a => [[a]] -> Forest a
950−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
951−
952−-- | Propagate everything at the current height
953−propagate :: a -> SubscriberList a -> EventM x ()
954−propagate a subscribers = withIncreasedDepth $ do
955− -- 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
956− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
957− WeakBag.traverse subscribers $ \s -> case s of
958− SubscriberPush compute subscribed -> {-# SCC "traversePush" #-} do
959− tracePropagate $ "SubscriberPush" <> showNodeId subscribed
960− occ <- runComputeM $ compute a
961− case occ of
962− Nothing -> return () -- No need to write a Nothing back into the Ref
963− Just o -> do
964− liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
965− scheduleClear $ pushSubscribedOccurrence subscribed
966− propagate o $ pushSubscribedSubscribers subscribed
967− SubscriberMerge k subscribed -> {-# SCC "traverseMerge" #-} do
968− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
969− oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
970− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge" <> showNodeId subscribed) k (Identity a) oldM
971− tracePropagate $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
972− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! newM
973− when (DMap.null oldM) $ do -- Only schedule the firing once
974− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
975− --TODO: assertions about height
976− currentHeight <- getCurrentHeight
977− when (height <= currentHeight) $ do
978− myStack <- liftIO $ whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
979− if height /= invalidHeight
980− then error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
981− else liftIO $ do
982− nodesInvolvedInCycle <- walkInvalidHeightParents $ EventSubscribedMerge subscribed
983− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
984− error $ "Causality loop found:\n" <> drawForest (listsToForest stacks)
985− scheduleMerge height subscribed
986− SubscriberMergeChange subscribed -> {-# SCC "traverseMergeChange" #-} do
987− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
988− defer $ SomeMergeUpdate a subscribed
989− SubscriberFan subscribed -> {-# SCC "traverseFan" #-} do
990− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
991− tracePropagate $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
992− --TODO: We need a better DMap intersection; here, we are assuming that the number of firing keys is small and the number of subscribers is large
993− forM_ (DMap.toList a) $ \(k :=> Identity v) -> {-# SCC "SubscriberFan" #-} case DMap.lookup k subs of
994− Nothing -> do
995− tracePropagate "No subscriber for key"
996− return ()
997− Just subsubs -> do
998− propagate v $ _fanSubscribedChildren_list subsubs
999− return ()
1000− SubscriberHold h -> {-# SCC "traverseHold" #-} propagateSubscriberHold h a
1001− SubscriberHoldIdentity h -> {-# SCC "traverseHoldIdentity" #-} propagateSubscriberHold h a
1002− SubscriberSwitch subscribed -> {-# SCC "traverseSwitch" #-} do
1003− tracePropagate $ "SubscriberSwitch" <> showNodeId subscribed
1004− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $! Just a
1005− scheduleClear $ switchSubscribedOccurrence subscribed
1006− propagate a $ switchSubscribedSubscribers subscribed
1007− SubscriberCoincidenceOuter subscribed -> {-# SCC "traverseCoincidenceOuter" #-} do
1008− tracePropagate $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
1009− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
1010− tracePropagate $ " outerHeight = " <> show outerHeight
1011− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
1012− tracePropagate $ " isJust occ = " <> show (isJust occ)
1013− tracePropagate $ " innerHeight = " <> show innerHeight
1014− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $! Just innerSubd
1015− scheduleClear $ coincidenceSubscribedInnerParent subscribed
1016− case occ of
1017− Nothing -> do
1018− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
1019− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
1020− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
1021− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
1022− Just o -> do -- Since it's already firing, no need to adjust height
1023− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
1024− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1025− propagate o $ coincidenceSubscribedSubscribers subscribed
1026− SubscriberCoincidenceInner subscribed -> {-# SCC "traverseCoincidenceInner" #-} do
1027− tracePropagate $ "SubscriberCoincidenceInner" <> showNodeId subscribed
1028− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
1029− case occ of
1030− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
1031− Nothing -> do
1032− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $! Just a
1033− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1034− propagate a $ coincidenceSubscribedSubscribers subscribed
1035− SubscriberHandle -> {-# SCC "traverseHandle" #-} tracePropagate "SubscriberHandle"
1036−
1037−{-# INLINE propagateSubscriberHold #-}
1038−propagateSubscriberHold :: R.Patch p => Hold p a -> p a -> EventM x ()
1039−propagateSubscriberHold h a = do
1040− {-# SCC "trace" #-} traceMWhen debugPropagate $ liftIO $ do
1041− invalidators <- liftIO $ readIORef $ holdInvalidators h
1042− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1043− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1044− case {-# SCC "apply" #-} R.apply a v of
1045− Nothing -> return ()
1046− Just v' -> do
1047− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1048− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1049− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1050− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1051−
1052−data SomeResetCoincidence = forall a. SomeResetCoincidence !(EventSubscription a) !(Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1053−
1054−{-# INLINE subscribeCoincidenceInner #-}
1055−subscribeCoincidenceInner :: CanSubscribe x m => Event a -> Height -> CoincidenceSubscribed a -> m (Maybe a, Height, EventSubscribed a)
1056−subscribeCoincidenceInner o outerHeight subscribedUnsafe = do
1057− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
1058− subscription@(EventSubscription _ innerSubd) <- subscribe o subInner
1059− innerOcc <- liftIO $ readEventSubscribed innerSubd
1060− innerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef innerSubd
1061− let height = max innerHeight outerHeight
1062− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
1063− return (innerOcc, height, innerSubd)
1064−
1065−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> BehaviorM a #-}
1066−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> EventM x a #-}
1067−readBehaviorUntracked :: Defer SomeHoldInit m => Behavior a -> m a
1068−readBehaviorUntracked b = do
1069− holdInits <- getDeferralQueue
1070− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1071−
1072−runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IORef [SomeHoldInit] -> IO a
1073−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1074−
1075−askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1076−askInvalidator = liftM (fmap fst . fst) $ BehaviorM ask
1077−
1078−askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1079−askParentsRef = liftM (fmap snd . fst) $ BehaviorM ask
1080−
1081−askBehaviorHoldInits :: BehaviorM (IORef [SomeHoldInit])
1082−askBehaviorHoldInits = liftM snd $ BehaviorM ask
1083−
1084−readBehaviorTracked :: Behavior a -> BehaviorM a
1085−readBehaviorTracked b = case b of
1086− BehaviorHold h -> readHoldTracked h
1087− BehaviorHoldIdentity h -> readHoldTracked h
1088− BehaviorConst a -> return a
1089− BehaviorPull p -> do
1090− val <- liftIO $ readIORef $ pullValue p
1091− case val of
1092− Just subscribed -> do
1093− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1094− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
1095− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
1096− return $ pullSubscribedValue subscribed
1097− Nothing -> do
1098− i <- liftIO $ newInvalidatorPull p
1099− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
1100− parentsRef <- liftIO $ newIORef []
1101− holdInits <- askBehaviorHoldInits
1102− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
1103− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
1104− parents <- liftIO $ readIORef parentsRef
1105− let subscribed = PullSubscribed
1106− { pullSubscribedValue = a
1107− , pullSubscribedInvalidators = invsRef
1108− , pullSubscribedOwnInvalidator = i
1109− , pullSubscribedParents = parents
1110− }
1111− liftIO $ writeIORef (pullValue p) $! Just subscribed
1112− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1113− return a
1114− BehaviorDyn d -> readHoldTracked =<< getDynHold d
1115− BehaviorDynIdentity d -> readHoldTracked =<< getDynHold d
1116−
1117−{-# INLINE readHoldTracked #-}
1118−readHoldTracked :: Hold p a -> BehaviorM a
1119−readHoldTracked h = do
1120− result <- liftIO $ readIORef $ holdValue h
1121− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
1122− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
1123− return result
1124−
1125−{-# INLINE getDynHold #-}
1126−getDynHold :: (Defer SomeHoldInit m, R.Patch p) => Dyn p a -> m (Hold p a)
1127−getDynHold d = do
1128− mh <- liftIO $ readIORef $ unDyn d
1129− case mh of
1130− Right h -> return h
1131− Left (readV0, v') -> do
1132− holdInits <- getDeferralQueue
1133− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1134− h <- hold v0 v'
1135− liftIO $ writeIORef (unDyn d) $! Right h
1136− return h
1137−
1138−{-
1139−readEvent :: Event a -> ResultM (Maybe a)
1140−readEvent e = case e of
1141− EventRoot k r -> liftIO . liftM (coerce . DMap.lookup k) . readIORef $ rootOccurrence r
1142− EventNever -> return Nothing
1143− EventPush p -> do
1144− subscribed <- getPushSubscribed p
1145− liftIO $ readPushSubscribed subscribed
1146− EventMerge m -> do
1147− subscribed <- getMergeSubscribed m
1148− liftIO $ readMergeSubscribed subscribed
1149− EventFan k f -> do
1150− subscribed <- getFanSubscribed f
1151− liftIO $ readFanSubscribed k subscribed
1152− EventSwitch s -> do
1153− subscribed <- getSwitchSubscribed s
1154− liftIO $ readSwitchSubscribed subscribed
1155− EventCoincidence c -> do
1156− subscribed <- getCoincidenceSubscribed c
1157− liftIO $ readCoincidenceSubscribed subscribed
1158− EventHold h -> liftIO . readEventSubscribed =<< getHoldEventSubscription h
1159− EventDyn d -> liftIO . readEventSubscribed =<< getHoldEventSubscription =<< getDynHold d
1160− EventHoldIdentity h -> liftIO . readEventSubscribed =<< getHoldEventSubscription h
1161− EventDynIdentity d -> liftIO . readEventSubscribed =<< getHoldEventSubscription =<< getDynHold d
1162−-}
1163−
1164−readEventSubscribed :: EventSubscribed a -> IO (Maybe a)
1165−readEventSubscribed = \case
1166− EventSubscribedRoot rs -> readRootSubscribed rs
1167− EventSubscribedNever -> return Nothing
1168− EventSubscribedPush ps -> readPushSubscribed ps
1169− EventSubscribedMerge ms -> readMergeSubscribed ms
1170− EventSubscribedFan k fs -> readFanSubscribed k fs
1171− EventSubscribedSwitch ss -> readSwitchSubscribed ss
1172− EventSubscribedCoincidence cs -> readCoincidenceSubscribed cs
1173−
1174−readRootSubscribed :: RootSubscribed a -> IO (Maybe a)
1175−readRootSubscribed = rootSubscribedOccurrence
1176−
1177−readPushSubscribed :: PushSubscribed b a -> IO (Maybe a)
1178−readPushSubscribed subscribed = do
1179− result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
1180− touch $ pushSubscribedSelf subscribed
1181− return result
1182−
1183−readMergeSubscribed :: MergeSubscribed k -> IO (Maybe (DMap k Identity))
1184−readMergeSubscribed subscribed = do
1185− result <- readIORef $ mergeSubscribedOccurrence subscribed
1186− touch $ mergeSubscribedParents subscribed
1187− return result
1188−
1189−readFanSubscribed :: GCompare k => k a -> FanSubscribed k -> IO (Maybe a)
1190−readFanSubscribed k fs = do
1191− parentOcc <- readEventSubscribed $ _eventSubscription_subscribed $ fanSubscribedParent fs
1192− return $ coerce $ DMap.lookup k =<< parentOcc
1193−
1194−readSwitchSubscribed :: SwitchSubscribed a -> IO (Maybe a)
1195−readSwitchSubscribed subscribed = do
1196− result <- readIORef $ switchSubscribedOccurrence subscribed
1197− touch $ switchSubscribedOwnInvalidator subscribed
1198− return result
1199−
1200−readCoincidenceSubscribed :: CoincidenceSubscribed a -> IO (Maybe a)
1201−readCoincidenceSubscribed subscribed = do
1202− result <- readIORef $ coincidenceSubscribedOccurrence subscribed
1203− touch $ coincidenceSubscribedOuter subscribed
1204− --TODO: do we need to touch the inner subscriber?
1205− return result
1206−
1207−-- Always refers to 0
1208−{-# NOINLINE zeroRef #-}
1209−zeroRef :: IORef Height
1210−zeroRef = unsafePerformIO $ newIORef zeroHeight
1211−
1212−{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
1213−subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
1214−subscribe e sub = case e of
1215− EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
1216− EventNever -> return $ EventSubscription Nothing EventSubscribedNever
1217− EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
1218− EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
1219− EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
1220− EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
1221− EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
1222− EventHold h -> subscribeHoldEvent h sub
1223− EventDyn j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
1224− EventHoldIdentity h -> subscribeHoldEvent h sub
1225− EventDynIdentity j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
1226− where
1227− wrap :: (t -> EventSubscribed a) -> (Subscriber a -> m (SubscriberListTicket a, t)) -> m (EventSubscription a)
1228− wrap tag getSpecificSubscribed = do
1229− (sln, subd) <- getSpecificSubscribed sub
1230− return $ EventSubscription (Just sln) $ tag subd
1231−
1232−{-# INLINE subscribeHoldEvent #-}
1233−subscribeHoldEvent :: (CanSubscribe x m, R.Patch p) => Hold p a -> Subscriber (p a) -> m (EventSubscription (p a))
1234−subscribeHoldEvent h sub = do
1235− EventSubscription _ subd <- getHoldEventSubscription h
1236− sln <- liftIO $ subscribeEventSubscribed subd sub
1237− return $ EventSubscription sln subd
1238−
1239−{-# INLINE debugSubscribe #-}
1240−debugSubscribe :: Bool
1241−debugSubscribe = False
1242−
1243−{-# INLINE subscribeEventSubscribed #-}
1244−subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
1245−subscribeEventSubscribed es sub = case es of
1246− EventSubscribedRoot r -> do
1247− traceSubscribe $ "subscribeEventSubscribed Root"
1248− fmap Just $ subscribeRootSubscribed r sub
1249− EventSubscribedNever -> do
1250− traceSubscribe $ "subscribeEventSubscribed Never"
1251− return Nothing
1252− EventSubscribedPush subscribed -> do
1253− traceSubscribe $ "subscribeEventSubscribed Push"
1254− fmap Just $ subscribePushSubscribed subscribed sub
1255− EventSubscribedFan k subscribed -> do
1256− traceSubscribe $ "subscribeEventSubscribed Fan"
1257− fmap Just $ subscribeFanSubscribed k subscribed sub
1258− EventSubscribedMerge subscribed -> do
1259− traceSubscribe $ "subscribeEventSubscribed Merge"
1260− fmap Just $ subscribeMergeSubscribed subscribed sub
1261− EventSubscribedSwitch subscribed -> do
1262− traceSubscribe $ "subscribeEventSubscribed Switch"
1263− fmap Just $ subscribeSwitchSubscribed subscribed sub
1264− EventSubscribedCoincidence subscribed -> do
1265− traceSubscribe $ "subscribeEventSubscribed Coincidence"
1266− fmap Just $ subscribeCoincidenceSubscribed subscribed sub
1267−
1268−{-# INLINE eventSubscribedHeightRef #-}
1269−eventSubscribedHeightRef :: EventSubscribed a -> IORef Height
1270−eventSubscribedHeightRef = \case
1271− EventSubscribedRoot _ -> zeroRef
1272− EventSubscribedNever -> zeroRef
1273− EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
1274− EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
1275− EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
1276− EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
1277− EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
1278−
1279−{-# INLINE eventSubscribedHasOwnHeightRef #-}
1280−eventSubscribedHasOwnHeightRef :: EventSubscribed a -> Bool
1281−eventSubscribedHasOwnHeightRef = \case
1282− EventSubscribedRoot _ -> False
1283− EventSubscribedNever -> False
1284− EventSubscribedPush _ -> False
1285− EventSubscribedFan _ _ -> False
1286− EventSubscribedMerge _ -> True
1287− EventSubscribedSwitch _ -> True
1288− EventSubscribedCoincidence _ -> True
1289−
1290−{-# INLINE eventSubscribedGetParents #-}
1291−eventSubscribedGetParents :: EventSubscribed a -> IO [Some EventSubscribed]
1292−eventSubscribedGetParents = \case
1293− EventSubscribedRoot _ -> return []
1294− EventSubscribedNever -> return []
1295− EventSubscribedPush subscribed -> return [Some.This $ _eventSubscription_subscribed $ pushSubscribedParent subscribed]
1296− EventSubscribedFan _ subscribed -> return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
1297− EventSubscribedMerge subscribed -> do
1298− ps <- readIORef $ mergeSubscribedParents subscribed
1299− return $ fmap (\(_ :=> v) -> Some.This $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription v) $ DMap.toList ps
1300− EventSubscribedSwitch subscribed -> do
1301− s <- readIORef $ switchSubscribedCurrentParent subscribed
1302− return [Some.This $ _eventSubscription_subscribed s]
1303− EventSubscribedCoincidence subscribed -> do
1304− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
1305− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1306− innerParents = maybeToList $ fmap Some.This innerSubscription
1307− return $ outerParent : innerParents
1308−
1309−type CanSubscribe x m =
1310− ( HasSpiderEnv x m
1311− , HasCurrentHeight m
1312− , Defer SomeHoldInit m, Defer SomeMergeInit m -- The things we need to lazily initialize state
1313− , Defer SomeClear m -- Wiping out event firings
1314− , Defer SomeAssignment m -- Updating state
1315− , Defer SomeResetCoincidence m
1316− )
1317−
1318−getRootSubscribed :: GCompare k => k a -> Root k -> Subscriber a -> IO (SubscriberListTicket a, RootSubscribed a)
1319−getRootSubscribed k r sub = do
1320− mSubscribed <- readIORef $ rootSubscribed r
1321− case DMap.lookup k mSubscribed of
1322− Just subscribed -> {-# SCC "hitRoot" #-} do
1323− sln <- subscribeRootSubscribed subscribed sub
1324− return (sln, subscribed)
1325− Nothing -> {-# SCC "missRoot" #-} do
1326− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1327− let !cached = rootSubscribed r
1328− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1329− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1330− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1331− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1332− writeIORef uninitRef $! uninit
1333− let subscribed = RootSubscribed
1334− { rootSubscribedKey = k
1335− , rootSubscribedCachedSubscribed = cached
1336− , rootSubscribedOccurrence = liftM (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1337− , rootSubscribedSubscribers = subs
1338− , rootSubscribedUninit = uninit
1339− , rootSubscribedWeakSelf = weakSelf
1340−#ifdef DEBUG_NODEIDS
1341− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1342−#endif
1343− }
1344− writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1345− modifyIORef' (rootSubscribed r) $ DMap.insertWith (error $ "getRootSubscribed: duplicate key inserted into Root") k subscribed --TODO: I think we can just write back mSubscribed rather than re-reading it
1346− return (sln, subscribed)
1347−
1348−cleanupRootSubscribed :: RootSubscribed a -> IO ()
1349−cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1350− rootSubscribedUninit self
1351− modifyIORef' cached $ DMap.delete k
1352−
1353−{-# INLINE subscribeRootSubscribed #-}
1354−subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1355−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1356−
1357−-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
1358−{-# SPECIALIZE getPushSubscribed :: Push a b -> Subscriber b -> EventM x (SubscriberListTicket b, PushSubscribed a b) #-}
1359−getPushSubscribed :: CanSubscribe x m => Push a b -> Subscriber b -> m (SubscriberListTicket b, PushSubscribed a b)
1360−getPushSubscribed p sub = do
1361− mSubscribed <- liftIO $ readIORef $ pushSubscribed p
1362− case mSubscribed of
1363− Just subscribed -> {-# SCC "hitPush" #-} do
1364− sln <- liftIO $ subscribePushSubscribed subscribed sub
1365− return (sln, subscribed)
1366− Nothing -> {-# SCC "missPush" #-} do -- Not yet subscribed
1367− subscribedRef <- liftIO $ newIORef $ error "getPushSubscribed: subscribed not yet intialized"
1368− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1369− s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
1370− subscription@(EventSubscription _ subd) <- subscribe (pushParent p) s
1371− parentOcc <- liftIO $ readEventSubscribed subd
1372− occ <- runComputeM $ liftM join $ mapM (pushCompute p) parentOcc
1373− occRef <- liftIO $ newIORef occ
1374− when (isJust occ) $ scheduleClear occRef
1375− weakSelf <- liftIO $ newIORef $ error "getPushSubscribed: weakSelf not initialized"
1376− let !cached = pushSubscribed p
1377− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupPushSubscribed
1378− let subscribed = PushSubscribed
1379− { pushSubscribedCachedSubscribed = cached
1380− , pushSubscribedOccurrence = occRef
1381− , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
1382− , pushSubscribedSubscribers = subs
1383− , pushSubscribedSelf = s
1384− , pushSubscribedWeakSelf = weakSelf
1385− , pushSubscribedParent = subscription
1386−#ifdef DEBUG_NODEIDS
1387− , pushSubscribedNodeId = unsafeNodeId p
1388−#endif
1389− }
1390− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
1391− liftIO $ writeIORef subscribedRef $! subscribed
1392− liftIO $ writeIORef (pushSubscribed p) $! Just subscribed
1393− return (sln, subscribed)
1394−
1395−cleanupPushSubscribed :: PushSubscribed a b -> IO ()
1396−cleanupPushSubscribed self = do
1397− unsubscribe $ pushSubscribedParent self
1398− writeIORef (pushSubscribedCachedSubscribed self) $! Nothing
1399−
1400−{-# INLINE subscribePushSubscribed #-}
1401−subscribePushSubscribed :: PushSubscribed a b -> Subscriber b -> IO (SubscriberListTicket b)
1402−subscribePushSubscribed subscribed sub = WeakBag.insert sub (pushSubscribedSubscribers subscribed) (pushSubscribedWeakSelf subscribed) cleanupPushSubscribed
1403−
1404−{-# SPECIALIZE getMergeSubscribed :: GCompare k => Merge k -> Subscriber (DMap k Identity) -> EventM x (SubscriberListTicket (DMap k Identity), MergeSubscribed k) #-}
1405−getMergeSubscribed :: forall x k m. (CanSubscribe x m, GCompare k) => Merge k -> Subscriber (DMap k Identity) -> m (SubscriberListTicket (DMap k Identity), MergeSubscribed k)
1406−getMergeSubscribed m sub = do
1407− mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
1408− case mSubscribed of
1409− Just subscribed -> {-# SCC "hitMerge" #-} do
1410− sln <- liftIO $ subscribeMergeSubscribed subscribed sub
1411− return (sln, subscribed)
1412− Nothing -> {-# SCC "missMerge" #-} do
1413− subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
1414− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1415− initialParents <- readBehaviorUntracked $ current $ mergeParents m
1416− subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent k))] <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1417− s <- liftIO $ newSubscriberMerge k subscribedUnsafe
1418− subscription@(EventSubscription _ parentSubd) <- subscribe e s
1419− parentOcc <- liftIO $ readEventSubscribed parentSubd
1420− height <- liftIO $ readIORef $ eventSubscribedHeightRef parentSubd
1421− return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> (MergeSubscribedParent subscription s))
1422− let dm = DMap.fromDistinctAscList $ catMaybes $ map (\(x, _, _) -> x) subscribers
1423− heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1424− myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1425− myHeight = if any (== invalidHeight) heights
1426− then invalidHeight
1427− else succHeight $ heightBagMax myHeightBag
1428− currentHeight <- getCurrentHeight
1429− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1430− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1431− else (Nothing, dm)
1432− when (not $ DMap.null accum) $ do
1433− scheduleMerge myHeight subscribedUnsafe
1434− occRef <- liftIO $ newIORef occ
1435− when (isJust occ) $ scheduleClear occRef
1436− accumRef <- liftIO $ newIORef accum
1437− heightRef <- liftIO $ newIORef myHeight
1438− heightMapRef <- liftIO $ newIORef myHeightBag
1439− weakSelf <- liftIO $ newIORef $ error "getMergeSubscribed: weakSelf not yet initialized"
1440− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupMergeSubscribed
1441− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1442− parentsRef <- liftIO $ newIORef $ DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1443− let !subscribed = MergeSubscribed
1444− { mergeSubscribedCachedSubscribed = mergeSubscribed m
1445− , mergeSubscribedOccurrence = occRef
1446− , mergeSubscribedAccum = accumRef
1447− , mergeSubscribedHeight = heightRef
1448− , mergeSubscribedHeightBag = heightMapRef
1449− , mergeSubscribedSubscribers = subs
1450− , mergeSubscribedChange = changeSubdRef
1451− , mergeSubscribedParents = parentsRef
1452− , mergeSubscribedWeakSelf = weakSelf
1453−#ifdef DEBUG_NODEIDS
1454− , mergeSubscribedNodeId = unsafeNodeId m
1455−#endif
1456− }
1457− defer $ SomeMergeInit subscribed $ updated $ mergeParents m
1458− liftIO $ writeIORef subscribedRef $! subscribed
1459− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "MergeSubscribed"
1460− liftIO $ writeIORef (mergeSubscribed m) $! Just subscribed
1461− return (sln, subscribed)
1462−
1463−cleanupMergeSubscribed :: MergeSubscribed k -> IO ()
1464−cleanupMergeSubscribed subscribed = do
1465− parents <- readIORef $ mergeSubscribedParents subscribed
1466− forM_ (DMap.toList parents) $ \(_ :=> msp) -> unsubscribe $ mergeSubscribedParentSubscription msp
1467− -- Not necessary, because this whole MergeSubscribed is dead: writeIORef (mergeSubscribedParents subscribed) DMap.empty
1468− writeIORef (mergeSubscribedCachedSubscribed subscribed) $! Nothing -- Get rid of the cached subscribed
1469−
1470−{-# INLINE subscribeMergeSubscribed #-}
1471−subscribeMergeSubscribed :: MergeSubscribed k -> Subscriber (DMap k Identity) -> IO (SubscriberListTicket (DMap k Identity))
1472−subscribeMergeSubscribed subscribed sub = WeakBag.insert sub (mergeSubscribedSubscribers subscribed) (mergeSubscribedWeakSelf subscribed) cleanupMergeSubscribed
1473−
1474−{-# SPECIALIZE getFanSubscribed :: GCompare k => k a -> Fan k -> Subscriber a -> EventM x (SubscriberListTicket a, FanSubscribed k) #-}
1475−getFanSubscribed :: (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderEnv x m, GCompare k) => k a -> Fan k -> Subscriber a -> m (SubscriberListTicket a, FanSubscribed k)
1476−getFanSubscribed k f sub = do
1477− mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1478− case mSubscribed of
1479− Just subscribed -> {-# SCC "hitFan" #-} do
1480− sln <- liftIO $ subscribeFanSubscribed k subscribed sub
1481− return (sln, subscribed)
1482− Nothing -> {-# SCC "missFan" #-} do
1483− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1484− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1485− s <- liftIO $ newSubscriberFan subscribedUnsafe
1486− subscription <- subscribe (fanParent f) s
1487− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1488− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1489− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1490− let subscribed = FanSubscribed
1491− { fanSubscribedCachedSubscribed = fanSubscribed f
1492− , fanSubscribedParent = subscription
1493− , fanSubscribedSubscribers = subscribersRef
1494−#ifdef DEBUG_NODEIDS
1495− , fanSubscribedNodeId = unsafeNodeId f
1496−#endif
1497− }
1498− let !self = (k, subscribed)
1499− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1500− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1501− liftIO $ writeIORef subscribedRef $! subscribed
1502− liftIO $ writeIORef (fanSubscribed f) $! Just subscribed
1503− return (slnForSub, subscribed)
1504−
1505−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed k) -> IO ()
1506−cleanupFanSubscribed (k, subscribed) = do
1507− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1508− let reducedSubscribers = DMap.delete k subscribers
1509− if DMap.null reducedSubscribers
1510− then do
1511− unsubscribe $ fanSubscribedParent subscribed
1512− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1513− writeIORef (fanSubscribedCachedSubscribed subscribed) $! Nothing
1514− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1515−
1516−{-# INLINE subscribeFanSubscribed #-}
1517−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed k -> Subscriber a -> IO (SubscriberListTicket a)
1518−subscribeFanSubscribed k subscribed sub = do
1519− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1520− case DMap.lookup k subscribers of
1521− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1522− let !self = (k, subscribed)
1523− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1524− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1525− writeIORef (fanSubscribedSubscribers subscribed) $! DMap.insertWith (error "subscribeEventSubscribed: key that we just failed to find is present - should be impossible") k (FanSubscribedChildren list self weakSelf) subscribers
1526− return sln
1527− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1528−
1529−{-# SPECIALIZE getSwitchSubscribed :: Switch a -> Subscriber a -> EventM x (SubscriberListTicket a, SwitchSubscribed a) #-}
1530−getSwitchSubscribed :: (Defer SomeHoldInit m, Defer SomeAssignment m, Defer SomeMergeInit m, Defer SomeClear m, Defer SomeResetCoincidence m, HasCurrentHeight m, HasSpiderEnv x m) => Switch a -> Subscriber a -> m (SubscriberListTicket a, SwitchSubscribed a)
1531−getSwitchSubscribed s sub = do
1532− mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1533− case mSubscribed of
1534− Just subscribed -> {-# SCC "hitSwitch" #-} do
1535− sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
1536− return (sln, subscribed)
1537− Nothing -> {-# SCC "missSwitch" #-} do
1538− subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1539− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1540− i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1541− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1542− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1543− wiRef <- liftIO $ newIORef wi
1544− parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1545− holdInits <- getDeferralQueue
1546− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1547− subscription@(EventSubscription _ subd) <- subscribe e mySub
1548− subscriptionRef <- liftIO $ newIORef subscription
1549− parentOcc <- liftIO $ readEventSubscribed subd
1550− occRef <- liftIO $ newIORef parentOcc
1551− when (isJust parentOcc) $ scheduleClear occRef
1552− heightRef <- liftIO $ newIORef =<< readIORef (eventSubscribedHeightRef subd)
1553− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1554− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1555− let !subscribed = SwitchSubscribed
1556− { switchSubscribedCachedSubscribed = switchSubscribed s
1557− , switchSubscribedOccurrence = occRef
1558− , switchSubscribedHeight = heightRef
1559− , switchSubscribedSubscribers = subs
1560− , switchSubscribedOwnInvalidator = i
1561− , switchSubscribedOwnWeakInvalidator = wiRef
1562− , switchSubscribedBehaviorParents = parentsRef
1563− , switchSubscribedParent = switchParent s
1564− , switchSubscribedCurrentParent = subscriptionRef
1565− , switchSubscribedWeakSelf = weakSelf
1566−#ifdef DEBUG_NODEIDS
1567− , switchSubscribedNodeId = unsafeNodeId s
1568−#endif
1569− }
1570− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1571− liftIO $ writeIORef subscribedRef $! subscribed
1572− liftIO $ writeIORef (switchSubscribed s) $! Just subscribed
1573− return (slnForSub, subscribed)
1574−
1575−cleanupSwitchSubscribed :: SwitchSubscribed a -> IO ()
1576−cleanupSwitchSubscribed subscribed = do
1577− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1578− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1579− writeIORef (switchSubscribedCachedSubscribed subscribed) $! Nothing
1580−
1581−{-# INLINE subscribeSwitchSubscribed #-}
1582−subscribeSwitchSubscribed :: SwitchSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1583−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1584−
1585−{-# SPECIALIZE getCoincidenceSubscribed :: Coincidence a -> Subscriber a -> EventM x (SubscriberListTicket a, CoincidenceSubscribed a) #-}
1586−getCoincidenceSubscribed :: forall x a m. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderEnv x m) => Coincidence a -> Subscriber a -> m (SubscriberListTicket a, CoincidenceSubscribed a)
1587−getCoincidenceSubscribed c sub = do
1588− mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1589− case mSubscribed of
1590− Just subscribed -> {-# SCC "hitCoincidence" #-} do
1591− sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1592− return (sln, subscribed)
1593− Nothing -> {-# SCC "missCoincidence" #-} do
1594− subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1595− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1596− subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1597− outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1598− outerOcc <- liftIO $ readEventSubscribed outerSubd
1599− outerHeight <- liftIO $ readIORef $ eventSubscribedHeightRef outerSubd
1600− (occ, height, mInnerSubd) <- case outerOcc of
1601− Nothing -> return (Nothing, outerHeight, Nothing)
1602− Just o -> do
1603− (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
1604− return (occ, height, Just innerSubd)
1605− occRef <- liftIO $ newIORef occ
1606− when (isJust occ) $ scheduleClear occRef
1607− heightRef <- liftIO $ newIORef height
1608− innerSubdRef <- liftIO $ newIORef mInnerSubd
1609− scheduleClear innerSubdRef
1610− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1611− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1612− let subscribed = CoincidenceSubscribed
1613− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1614− , coincidenceSubscribedOccurrence = occRef
1615− , coincidenceSubscribedHeight = heightRef
1616− , coincidenceSubscribedSubscribers = subs
1617− , coincidenceSubscribedOuter = subOuter
1618− , coincidenceSubscribedOuterParent = outerSubscription
1619− , coincidenceSubscribedInnerParent = innerSubdRef
1620− , coincidenceSubscribedWeakSelf = weakSelf
1621−#ifdef DEBUG_NODEIDS
1622− , coincidenceSubscribedNodeId = unsafeNodeId c
1623−#endif
1624− }
1625− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1626− liftIO $ writeIORef subscribedRef $! subscribed
1627− liftIO $ writeIORef (coincidenceSubscribed c) $! Just subscribed
1628− return (slnForSub, subscribed)
1629−
1630−cleanupCoincidenceSubscribed :: CoincidenceSubscribed a -> IO ()
1631−cleanupCoincidenceSubscribed subscribed = do
1632− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1633− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) $! Nothing
1634−
1635−{-# INLINE subscribeCoincidenceSubscribed #-}
1636−subscribeCoincidenceSubscribed :: CoincidenceSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1637−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1638−
1639−{-# INLINE merge #-}
1640−merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
1641−merge m = EventMerge $ Merge
1642− { mergeParents = m
1643− , mergeSubscribed = unsafeNewIORef m Nothing
1644− }
1645−
1646−newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1647−
1648−fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
1649−fan e =
1650− let f = Fan
1651− { fanParent = e
1652− , fanSubscribed = unsafeNewIORef e Nothing
1653− }
1654− in EventSelector $ \k -> EventFan k f
1655−
1656−{- SPECIALIZE runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x () #-}
1657−--runHoldInits :: (CanSubscribe x m, Defer SomeMergeUpdate m) => IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> m ()
1658−runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x ()
1659−runHoldInits holdInitRef mergeInitRef = do
1660− holdInits <- liftIO $ readIORef holdInitRef
1661− mergeInits <- liftIO $ readIORef mergeInitRef
1662− if null holdInits && null mergeInits then return () else do
1663− liftIO $ writeIORef holdInitRef $! []
1664− liftIO $ writeIORef mergeInitRef $! []
1665− mapM_ initHold holdInits
1666− mapM_ initMerge mergeInits
1667− runHoldInits holdInitRef mergeInitRef
1668−
1669−{- SPECIALIZE initHold :: SomeHoldInit -> EventM x () #-}
1670−--initHold :: CanSubscribe x m => SomeHoldInit -> m ()
1671−initHold :: SomeHoldInit -> EventM x ()
1672−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1673−
1674−{- SPECIALIZE initMerge :: SomeMergeInit -> EventM x () #-}
1675−--initMerge :: (CanSubscribe x m, Defer SomeMergeUpdate m) => SomeMergeInit -> m ()
1676−initMerge :: SomeMergeInit -> EventM x ()
1677−initMerge (SomeMergeInit subscribed changed) = do
1678− changeSub <- liftIO $ newSubscriberMergeChange subscribed
1679− EventSubscription _ changeSubd <- subscribe changed changeSub
1680− change <- liftIO $ readEventSubscribed changeSubd
1681− forM_ change $ \c -> defer $ SomeMergeUpdate c subscribed
1682− liftIO $ writeIORef (mergeSubscribedChange subscribed) $! (changeSub, changeSubd)
1683−
1684−-- | Run an event action outside of a frame
1685−runFrame :: EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1686−runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1687− -- Clear out pending unsubscriptions; these will need to happen eventually anyway, and might cause us a lot of extra work during the frame, so get rid of them now
1688− pendingCleanups <- atomicModifyIORef (_spiderEnv_cleanups spiderEnv) $ \x -> ([], x)
1689− {-# SCC "cleanups" #-} sequence_ pendingCleanups
1690− toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1691− holdInitRef <- newIORef []
1692− mergeUpdateRef <- newIORef []
1693− mergeInitRef <- newIORef []
1694− heightRef <- newIORef zeroHeight
1695− toClearRef <- newIORef []
1696− toClearRootRef <- newIORef []
1697− coincidenceInfosRef <- newIORef []
1698− delayedRef <- liftIO $ newIORef IntMap.empty
1699− let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1700− let go = do
1701− result <- a
1702− runHoldInits holdInitRef mergeInitRef -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1703− return result
1704− result <- runEventM go env
1705− toClear <- readIORef toClearRef
1706− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref $! Nothing
1707− toClearRoot <- readIORef toClearRootRef
1708− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1709− toAssign <- readIORef toAssignRef
1710− toReconnectRef <- newIORef []
1711− coincidenceInfos <- readIORef coincidenceInfosRef
1712− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1713− writeIORef vRef v
1714− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1715− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1716− mergeUpdates <- readIORef mergeUpdateRef
1717− writeIORef mergeUpdateRef $! []
1718− let updateMerge :: SomeMergeUpdate -> IO [SomeEventSubscription]
1719− updateMerge (SomeMergeUpdate (R.PatchDMap p :: R.PatchDMap (DMap k Event)) m) = do
1720− oldParents <- readIORef $ mergeSubscribedParents m
1721− let f (subscriptionsToKill, ps) (k :=> Compose me) = do
1722− (mOldSubd, newPs) <- case me of
1723− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1724− Just e -> do
1725− s <- liftIO $ newSubscriberMerge k m
1726− subscription@(EventSubscription _ subd) <- subscribe e s
1727− let newParent = MergeSubscribedParent subscription s
1728− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1729− liftIO $ addMergeHeight newParentHeight m
1730− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1731− forM_ mOldSubd $ \oldSubd -> do
1732− oldHeight <- liftIO $ readIORef $ eventSubscribedHeightRef $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription oldSubd
1733− liftIO $ removeMergeHeight oldHeight m
1734− return (maybeToList (SomeEventSubscription . mergeSubscribedParentSubscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1735− (subscriptionsToKill, newParents) <- flip runEventM env $ foldM f ([], oldParents) $ DMap.toList p --TODO: I think this runEventM is OK, since no events are firing at this time, but it might not be
1736− writeIORef (mergeSubscribedParents m) $! newParents
1737− return subscriptionsToKill
1738− when debugPropagate $ putStrLn "Updating merges"
1739− mergeSubscriptionsToKill <- concat <$> mapM updateMerge mergeUpdates
1740− when debugPropagate $ putStrLn "Updating merges done"
1741− toReconnect <- readIORef toReconnectRef
1742− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1743− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1744− wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1745− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1746− finalize wi
1747− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1748− wi' <- mkWeakPtrWithDebug i "wi'"
1749− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1750− writeIORef (switchSubscribedBehaviorParents subscribed) $! []
1751− writeIORef holdInitRef $! [] --TODO: Should we reuse this?
1752− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) holdInitRef
1753− runEventM (runHoldInits holdInitRef mergeInitRef) env --TODO: Is this actually OK? It seems like it should be, since we know that no events are firing at this point, but it still seems inelegant
1754− --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1755− sub <- newSubscriberSwitch subscribed
1756− subscription <- runReaderT (unSpiderHost (runFrame ({-# SCC "subscribeSwitch" #-} subscribe e sub))) spiderEnv --TODO: Assert that the event isn't firing --TODO: This should not loop because none of the events should be firing, but still, it is inefficient
1757− {-
1758− stackTrace <- liftIO $ liftM renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1759− liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1760− EventRoot _ -> "EventRoot"
1761− EventNever -> "EventNever"
1762− _ -> "something else"
1763− -}
1764− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1765− return $ SomeEventSubscription oldSubscription
1766− forM_ mergeSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1767− forM_ switchSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1768− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1769− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1770− parentHeight <- readIORef $ eventSubscribedHeightRef subd'
1771− myHeight <- readIORef $ switchSubscribedHeight subscribed
1772− when (parentHeight /= myHeight) $ do
1773− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1774− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1775− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> do
1776− invalidateMergeHeight m --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
1777− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1778− unsubscribe subscription
1779− mapM_ invalidateCoincidenceHeight mcs
1780− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1781− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
1782− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1783− height <- calculateSwitchHeight subscribed
1784− updateSwitchHeight height subscribed
1785− return result
1786−
1787−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1788−
1789−{-# INLINE zeroHeight #-}
1790−zeroHeight :: Height
1791−zeroHeight = Height 0
1792−
1793−{-# INLINE invalidHeight #-}
1794−invalidHeight :: Height
1795−invalidHeight = Height (-1000)
1796−
1797−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1798−{-# INLINE invalidHeightBeingTraversed #-}
1799−invalidHeightBeingTraversed :: Height
1800−invalidHeightBeingTraversed = Height (-1001)
1801−
1802−{-# INLINE succHeight #-}
1803−succHeight :: Height -> Height
1804−succHeight h@(Height a) =
1805− if h == invalidHeight
1806− then invalidHeight
1807− else Height $ succ a
1808−
1809−invalidateSubscriberHeight :: Height -> Subscriber a -> IO ()
1810−invalidateSubscriberHeight old s = case s of
1811− SubscriberPush _ subscribed -> do
1812− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1813− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ invalidateSubscriberHeight old
1814− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1815− SubscriberMerge _ subscribed -> do
1816− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1817− removeMergeHeight old subscribed
1818− invalidateMergeHeight subscribed
1819− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1820− SubscriberMergeChange _ -> return () -- Doesn't affect the height of the merge, because its changes are delayed
1821− SubscriberFan subscribed -> do
1822− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1823− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1824− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
1825− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1826− SubscriberHold _ -> return ()
1827− SubscriberHoldIdentity _ -> return ()
1828− SubscriberSwitch subscribed -> do
1829− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1830− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1831− when (oldHeight /= invalidHeight) $ do
1832− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1833− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1834− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1835− SubscriberCoincidenceOuter subscribed -> do
1836− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1837− invalidateCoincidenceHeight subscribed
1838− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1839− SubscriberCoincidenceInner subscribed -> do
1840− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1841− invalidateCoincidenceHeight subscribed
1842− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1843− SubscriberHandle -> return ()
1844−
1845−removeMergeHeight :: Height -> MergeSubscribed k -> IO ()
1846−removeMergeHeight oldParentHeight subscribed = do
1847− when debugInvalidateHeight $ putStrLn $ "removeMergeHeight: " <> show oldParentHeight <> " Merge" <> showNodeId subscribed
1848− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1849− let newHeights = heightBagRemove oldParentHeight oldHeights
1850− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1851−
1852−invalidateMergeHeight :: MergeSubscribed k -> IO ()
1853−invalidateMergeHeight subscribed = do
1854− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1855− -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1856− when (oldHeight /= invalidHeight) $ do
1857− writeIORef (mergeSubscribedHeight subscribed) $! invalidHeight
1858− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1859−
1860−addMergeHeight :: Height -> MergeSubscribed k -> IO ()
1861−addMergeHeight newParentHeight subscribed = do
1862− when debugInvalidateHeight $ putStrLn $ "addMergeHeight: " <> show newParentHeight <> " Merge" <> showNodeId subscribed
1863− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1864− let newHeights = heightBagAdd newParentHeight oldHeights
1865− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1866−
1867−revalidateMergeHeight :: MergeSubscribed k -> IO ()
1868−revalidateMergeHeight subscribed = do
1869− currentHeight <- readIORef $ mergeSubscribedHeight subscribed
1870− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1871− heights <- readIORef $ mergeSubscribedHeightBag subscribed
1872− parents <- readIORef $ mergeSubscribedParents subscribed
1873− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1874− case heightBagSize heights `compare` DMap.size parents of
1875− LT -> return ()
1876− EQ -> do
1877− let height = succHeight $ heightBagMax heights
1878− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1879− writeIORef (mergeSubscribedHeight subscribed) $! height
1880− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1881− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge" <> showNodeId subscribed
1882−
1883−invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1884−invalidateCoincidenceHeight subscribed = do
1885− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1886− when (oldHeight /= invalidHeight) $ do
1887− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1888− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1889−
1890−recalculateSubscriberHeight :: Height -> Subscriber a -> IO ()
1891−recalculateSubscriberHeight new s = case s of
1892− SubscriberPush _ subscribed -> do
1893− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1894− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1895− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1896− SubscriberMerge _ subscribed -> do
1897− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1898− addMergeHeight new subscribed
1899− revalidateMergeHeight subscribed
1900− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1901− SubscriberMergeChange _ -> return ()
1902− SubscriberFan subscribed -> do
1903− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1904− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1905− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
1906− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1907− SubscriberHold _ -> return ()
1908− SubscriberHoldIdentity _ -> return ()
1909− SubscriberSwitch subscribed -> do
1910− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1911− updateSwitchHeight new subscribed
1912− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1913− SubscriberCoincidenceOuter subscribed -> do
1914− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1915− recalculateCoincidenceHeight subscribed
1916− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1917− SubscriberCoincidenceInner subscribed -> do
1918− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1919− recalculateCoincidenceHeight subscribed
1920− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1921− SubscriberHandle -> return ()
1922−
1923−updateSwitchHeight :: Height -> SwitchSubscribed a -> IO ()
1924−updateSwitchHeight new subscribed = do
1925− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1926− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1927− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1928− writeIORef (switchSubscribedHeight subscribed) $! new
1929− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1930−
1931−recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1932−recalculateCoincidenceHeight subscribed = do
1933− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1934− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1935− height <- calculateCoincidenceHeight subscribed
1936− when (height /= invalidHeight) $ do
1937− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1938− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1939−
1940−getEventSubscribedHeight :: EventSubscribed a -> IO Height
1941−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
1942−
1943−calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1944−calculateSwitchHeight subscribed = readIORef . eventSubscribedHeightRef . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1945−
1946−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
1947−calculateCoincidenceHeight subscribed = do
1948− outerHeight <- readIORef $ eventSubscribedHeightRef $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1949− innerHeight <- maybe (return zeroHeight) (readIORef . eventSubscribedHeightRef) =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1950− return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1951−
1952−data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
1953−
1954−invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1955−invalidate toReconnectRef wis = do
1956− forM_ wis $ \wi -> do
1957− mi <- deRefWeak wi
1958− case mi of
1959− Nothing -> do
1960− traceInvalidate "invalidate Dead"
1961− return () --TODO: Should we clean this up here?
1962− Just i -> do
1963− finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1964− case i of
1965− InvalidatorPull p -> do
1966− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1967− mVal <- readIORef $ pullValue p
1968− forM_ mVal $ \val -> do
1969− writeIORef (pullValue p) $! Nothing
1970− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1971− InvalidatorSwitch subscribed -> do
1972− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1973− modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1974− return [] -- Since we always finalize everything, always return an empty list --TODO: There are some things that will need to be re-subscribed every time; we should try to avoid finalizing them
1975−
1976−--------------------------------------------------------------------------------
1977−-- Reflex integration
1978−--------------------------------------------------------------------------------
1979−
1980−type Spider = SpiderEnv Global
1981−
1982−data Global
1983−
1984−{-# NOINLINE globalSpiderEnv #-}
1985−globalSpiderEnv :: SpiderEnv Global
1986−globalSpiderEnv = unsafePerformIO unsafeNewSpiderEnv
1987−
1988−type role SpiderEnv nominal
1989−data SpiderEnv x = SpiderEnv
1990− { _spiderEnv_toUnsubscribe :: !(IORef [SomeEventSubscription])
1991− , _spiderEnv_lock :: !(MVar ())
1992− , _spiderEnv_cleanups :: !(IORef [IO ()])
1993−#ifdef DEBUG
1994− , _spiderEnv_depth :: !(IORef Int)
1995−#endif
1996− }
1997−
1998−instance GEq SpiderEnv where
1999− a `geq` b = if _spiderEnv_toUnsubscribe a == _spiderEnv_toUnsubscribe b
2000− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderEnv can't have two different 'x' arguments
2001− else Nothing
2002−
2003−unsafeNewSpiderEnv :: forall x. IO (SpiderEnv x)
2004−unsafeNewSpiderEnv = do
2005− toUnsubscribe <- newIORef []
2006− lock <- newMVar ()
2007− cleanups <- newIORef []
2008−#ifdef DEBUG
2009− depthRef <- newIORef 0
2010−#endif
2011− return $ SpiderEnv
2012− { _spiderEnv_toUnsubscribe = toUnsubscribe
2013− , _spiderEnv_lock = lock
2014− , _spiderEnv_cleanups = cleanups
2015−#ifdef DEBUG
2016− , _spiderEnv_depth = depthRef
2017−#endif
2018− }
2019−
2020−newSpiderEnv :: IO (Some SpiderEnv)
2021−newSpiderEnv = Some.This <$> unsafeNewSpiderEnv
2022−
2023−newtype SpiderPullM x a = SpiderPullM { runSpiderPullM :: BehaviorM a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2024−
2025−newtype SpiderPushM x a = SpiderPushM { runSpiderPushM :: ComputeM a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2026−
2027−instance R.Reflex (SpiderEnv x) where
2028− newtype Behavior (SpiderEnv x) a = SpiderBehavior { unSpiderBehavior :: Behavior a }
2029− newtype Event (SpiderEnv x) a = SpiderEvent { unSpiderEvent :: Event a }
2030− newtype Dynamic (SpiderEnv x) a = SpiderDynamic { unSpiderDynamic :: Dynamic Identity a } deriving (Functor, Applicative, Monad)
2031− newtype Incremental (SpiderEnv x) p a = SpiderIncremental { unSpiderIncremental :: Dynamic p a }
2032− type PullM (SpiderEnv x) = SpiderPullM x
2033− type PushM (SpiderEnv x) = SpiderPushM x
2034− {-# INLINABLE never #-}
2035− never = SpiderEvent EventNever
2036− {-# INLINABLE constant #-}
2037− constant = SpiderBehavior . BehaviorConst
2038− {-# INLINABLE push #-}
2039− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2040− {-# INLINABLE pull #-}
2041− pull = SpiderBehavior . pull . coerce
2042− {-# INLINABLE merge #-}
2043− merge = SpiderEvent . merge . DynamicConst . (unsafeCoerce :: DMap k (R.Event (SpiderEnv x)) -> DMap k Event)
2044− {-# INLINABLE fan #-}
2045− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2046− {-# INLINABLE switch #-}
2047− switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event (SpiderEnv x) a) -> Behavior (Event a)) . unSpiderBehavior
2048− {-# INLINABLE coincidence #-}
2049− coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event (SpiderEnv x) a) -> Event (Event a)) . unSpiderEvent
2050− {-# INLINABLE current #-}
2051− current = SpiderBehavior . current . unSpiderDynamic
2052− {-# INLINABLE updated #-}
2053− updated = SpiderEvent . fmap runIdentity . updated . unSpiderDynamic
2054− {-# INLINABLE unsafeBuildDynamic #-}
2055− unsafeBuildDynamic readV0 v' = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (coerce readV0) $ addIdentity $ unSpiderEvent v'
2056− {-# INLINABLE unsafeBuildIncremental #-}
2057− unsafeBuildIncremental readV0 dv = SpiderIncremental $ DynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2058− {-# INLINABLE mergeIncremental #-}
2059− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic R.PatchDMap (DMap k (R.Event (SpiderEnv x))) -> Dynamic R.PatchDMap (DMap k Event)) . unSpiderIncremental
2060− {-# INLINABLE currentIncremental #-}
2061− currentIncremental = SpiderBehavior . current . unSpiderIncremental
2062− {-# INLINABLE updatedIncremental #-}
2063− updatedIncremental = SpiderEvent . updated . unSpiderIncremental
2064− {-# INLINABLE incrementalToDynamic #-}
2065− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ current i) $ flip push (updated i) $ \p -> do
2066− c <- readBehaviorUntracked $ current i
2067− return $ fmap Identity $ R.apply p c --TODO: Avoid the redundant 'apply'
2068−
2069−instance R.MonadSample (SpiderEnv x) (EventM x) where
2070− {-# INLINABLE sample #-}
2071− sample (SpiderBehavior b) = readBehaviorUntracked b
2072−
2073−instance R.MonadHold (SpiderEnv x) (EventM x) where
2074− {-# INLINABLE hold #-}
2075− hold = holdSpiderEventM
2076− {-# INLINABLE holdDyn #-}
2077− holdDyn = holdDynSpiderEventM
2078− {-# INLINABLE holdIncremental #-}
2079− holdIncremental = holdIncrementalSpiderEventM
2080−
2081−holdSpiderEventM :: a -> R.Event (SpiderEnv x) a -> EventM x (R.Behavior (SpiderEnv x) a)
2082−holdSpiderEventM v0 e = liftM (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2083−
2084−holdDynSpiderEventM :: a -> R.Event (SpiderEnv x) a -> EventM x (R.Dynamic (SpiderEnv x) a)
2085−holdDynSpiderEventM v0 e = liftM (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2086−
2087−holdIncrementalSpiderEventM :: R.Patch p => a -> R.Event (SpiderEnv x) (p a) -> EventM x (R.Incremental (SpiderEnv x) p a)
2088−holdIncrementalSpiderEventM v0 e = liftM (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2089−
2090−instance R.MonadHold (SpiderEnv x) (SpiderHost x) where
2091− {-# INLINABLE hold #-}
2092− hold v0 e = R.runHostFrame $ R.hold v0 e
2093− {-# INLINABLE holdDyn #-}
2094− holdDyn v0 e = R.runHostFrame $ R.holdDyn v0 e
2095− {-# INLINABLE holdIncremental #-}
2096− holdIncremental v0 e = R.runHostFrame $ R.holdIncremental v0 e
2097−
2098−instance R.MonadSample (SpiderEnv x) (SpiderHost x) where
2099− {-# INLINABLE sample #-}
2100− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
2101−
2102−instance R.MonadSample (SpiderEnv x) (SpiderPullM x) where
2103− {-# INLINABLE sample #-}
2104− sample = coerce . readBehaviorTracked . unSpiderBehavior
2105−
2106−instance R.MonadSample (SpiderEnv x) (SpiderPushM x) where
2107− {-# INLINABLE sample #-}
2108− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
2109−
2110−instance R.MonadHold (SpiderEnv x) (SpiderPushM x) where
2111− {-# INLINABLE hold #-}
2112− hold v0 e = R.current <$> R.holdDyn v0 e
2113− {-# INLINABLE holdDyn #-}
2114− holdDyn v0 (SpiderEvent e) = SpiderPushM $ liftM (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity e
2115− {-# INLINABLE holdIncremental #-}
2116− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ liftM (SpiderIncremental . DynamicHold) $ hold v0 e
2117−
2118−data RootTrigger a = forall k. GCompare k => RootTrigger (SubscriberList a, IORef (DMap k Identity), k a)
2119−newtype SpiderEventHandle a = SpiderEventHandle { unSpiderEventHandle :: EventSubscription a }
2120−
2121−instance R.MonadSubscribeEvent (SpiderEnv x) (SpiderHostFrame x) where
2122− {-# INLINABLE subscribeEvent #-}
2123− subscribeEvent e = SpiderHostFrame $ do
2124− subscription <- subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2125− return $ SpiderEventHandle subscription
2126−
2127−instance R.ReflexHost (SpiderEnv x) where
2128− type EventTrigger (SpiderEnv x) = RootTrigger
2129− type EventHandle (SpiderEnv x) = SpiderEventHandle
2130− type HostFrame (SpiderEnv x) = SpiderHostFrame x
2131−
2132−instance R.MonadReadEvent (SpiderEnv x) (ReadPhase x) where
2133− {-# INLINABLE readEvent #-}
2134− readEvent h = ReadPhase $ liftM (fmap return) $ liftIO $ do
2135− let EventSubscription sln subscribed = unSpiderEventHandle h
2136− result <- readEventSubscribed subscribed
2137− touch sln
2138− return result
2139−
2140−instance MonadRef (EventM x) where
2141− type Ref (EventM x) = Ref IO
2142− {-# INLINABLE newRef #-}
2143− {-# INLINABLE readRef #-}
2144− {-# INLINABLE writeRef #-}
2145− newRef = liftIO . newRef
2146− readRef = liftIO . readRef
2147− writeRef r a = liftIO $ writeRef r a
2148−
2149−instance MonadAtomicRef (EventM x) where
2150− {-# INLINABLE atomicModifyRef #-}
2151− atomicModifyRef r f = liftIO $ atomicModifyRef r f
2152−
2153−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2154−
2155−instance Monad (SpiderHost x) where
2156− {-# INLINABLE (>>=) #-}
2157− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2158− {-# INLINABLE (>>) #-}
2159− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2160− {-# INLINABLE return #-}
2161− return x = SpiderHost $ return x
2162− {-# INLINABLE fail #-}
2163− fail s = SpiderHost $ fail s
2164−
2165−runSpiderHost :: SpiderHost Global a -> IO a
2166−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderEnv
2167−
2168−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2169−
2170−instance Monad (SpiderHostFrame x) where
2171− {-# INLINABLE (>>=) #-}
2172− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2173− {-# INLINABLE (>>) #-}
2174− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2175− {-# INLINABLE return #-}
2176− return x = SpiderHostFrame $ return x
2177− {-# INLINABLE fail #-}
2178− fail s = SpiderHostFrame $ fail s
2179−
2180−instance R.MonadSample (SpiderEnv x) (SpiderHostFrame x) where
2181− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
2182−
2183−addIdentity :: Event a -> Event (Identity a)
2184−--addIdentity = fmap Identity
2185−addIdentity = unsafeCoerce
2186−
2187−instance R.MonadHold (SpiderEnv x) (SpiderHostFrame x) where
2188− {-# INLINABLE hold #-}
2189− hold v0 e = SpiderHostFrame $ liftM (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2190− {-# INLINABLE holdDyn #-}
2191− holdDyn v0 e = SpiderHostFrame $ liftM (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2192− {-# INLINABLE holdIncremental #-}
2193− holdIncremental v0 e = SpiderHostFrame $ liftM (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2194−
2195−newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
2196−newEventWithTriggerIO f = do
2197− es <- newFanEventWithTriggerIO $ \Refl -> f
2198− return $ select es Refl
2199−
2200−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger a -> IO (IO ())) -> IO (EventSelector k)
2201−newFanEventWithTriggerIO f = do
2202− occRef <- newIORef DMap.empty
2203− subscribedRef <- newIORef DMap.empty
2204− let !r = Root
2205− { rootOccurrence = occRef
2206− , rootSubscribed = subscribedRef
2207− , rootInit = f
2208− }
2209− return $ EventSelector $ \k -> EventRoot k r
2210−
2211−instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHost x) where
2212− newEventWithTrigger = SpiderHost . lift . liftM SpiderEvent . newEventWithTriggerIO
2213− newFanEventWithTrigger f = SpiderHost $ lift $ do
2214− es <- newFanEventWithTriggerIO f
2215− return $ R.EventSelector $ SpiderEvent . select es
2216−
2217−instance R.MonadReflexCreateTrigger (SpiderEnv x) (SpiderHostFrame x) where
2218− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . liftM SpiderEvent . newEventWithTriggerIO
2219− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
2220− es <- newFanEventWithTriggerIO f
2221− return $ R.EventSelector $ SpiderEvent . select es
2222−
2223−instance R.MonadSubscribeEvent (SpiderEnv x) (SpiderHost x) where
2224− {-# INLINABLE subscribeEvent #-}
2225− subscribeEvent = subscribeEventSpiderHost
2226−
2227−subscribeEventSpiderHost :: R.Event (SpiderEnv x) a -> SpiderHost x (R.EventHandle (SpiderEnv x) a)
2228−subscribeEventSpiderHost e = do
2229− subscription <- runFrame $ subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2230− return $ SpiderEventHandle subscription
2231−
2232−newtype ReadPhase x a = ReadPhase { runReadPhase :: ResultM x a } deriving (Functor, Applicative, Monad, MonadFix)
2233−
2234−instance R.MonadSample (SpiderEnv x) (ReadPhase x) where
2235− {-# INLINABLE sample #-}
2236− sample = ReadPhase . R.sample
2237−
2238−instance R.MonadHold (SpiderEnv x) (ReadPhase x) where
2239− {-# INLINABLE hold #-}
2240− hold v0 e = ReadPhase $ R.hold v0 e
2241− {-# INLINABLE holdDyn #-}
2242− holdDyn v0 e = ReadPhase $ R.holdDyn v0 e
2243− {-# INLINABLE holdIncremental #-}
2244− holdIncremental v0 e = ReadPhase $ R.holdIncremental v0 e
2245−
2246−instance R.MonadReflexHost (SpiderEnv x) (SpiderHost x) where
2247− type ReadPhase (SpiderHost x) = ReadPhase x
2248− fireEventsAndRead es (ReadPhase a) = run es a
2249− runHostFrame = runFrame . runSpiderHostFrame
2250−
2251−instance MonadRef (SpiderHost x) where
2252− type Ref (SpiderHost x) = Ref IO
2253− newRef = SpiderHost . newRef
2254− readRef = SpiderHost . readRef
2255− writeRef r = SpiderHost . writeRef r
2256−
2257−instance MonadAtomicRef (SpiderHost x) where
2258− atomicModifyRef r = SpiderHost . atomicModifyRef r
2259−
2260−instance MonadRef (SpiderHostFrame x) where
2261− type Ref (SpiderHostFrame x) = Ref IO
2262− newRef = SpiderHostFrame . newRef
2263− readRef = SpiderHostFrame . readRef
2264− writeRef r = SpiderHostFrame . writeRef r
2265−
2266−instance MonadAtomicRef (SpiderHostFrame x) where
2267− atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
stylizenew file+4
@@ -0,0 +1,4 @@
1+#!/usr/bin/env bash
2+set -euo pipefail
3+
4+for x in **/*.hs ; do stylish-haskell -i "$x" ; done
test/Reflex/Bench/Focused.hsdeleted−361
@@ -1,361 +0,0 @@
1−{-# LANGUAGE RecursiveDo, ConstraintKinds, TypeSynonymInstances, BangPatterns, ScopedTypeVariables, TupleSections, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−
3−module Reflex.Bench.Focused where
4−
5−import Reflex
6−import Reflex.TestPlan
7−
8−import Control.Monad.Fix
9−import Control.Monad
10−import Control.Monad.Identity
11−import Control.Applicative
12−--import Data.Foldable
13−import Data.Traversable (for)
14−
15−import Data.Maybe
16−import Data.Map (Map)
17−import qualified Data.Map as Map
18−import qualified Data.Dependent.Map as DMap
19−
20−import Data.Functor.Misc
21−
22−import Data.Tuple
23−import Data.List
24−import Data.List.Split
25−
26−import Data.Word
27−import Data.Monoid
28−
29−import Control.DeepSeq
30−
31−import Prelude hiding (sum, concat)
32−
33−
34−mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
35−mergeTree n es | length es <= n = sum' es
36− | otherwise = mergeTree n subTrees
37− where
38− merges = chunksOf n es
39− subTrees = map sum' merges
40− sum' = fmap sum . mconcat . fmap (fmap (:[]) )
41−
42−mergeListDyn :: (Reflex t, MonadHold t m) => [Dynamic t a] -> m (Dynamic t [a])
43−mergeListDyn dyns = traverse (mapDyn pure) dyns >>= mconcatDyn
44−
45−mergeTreeDyn :: (Num a, Reflex t, MonadHold t m, MonadFix m) => Int -> [Dynamic t a] -> m (Dynamic t a)
46−mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
47− | otherwise = traverse sumDyns merges >>= mergeTreeDyn n
48− where
49− merges = chunksOf n dyns
50− sumDyns ds = mergeListDyn ds >>= mapDyn sum
51−
52−
53−
54−-- N events all firing for N frames
55−continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
56−continuous n frames = for [1..n] $ \i -> plan $ (, i) <$> [1..frames]
57−
58−-- Fire N events once every P frames for total N frames (interspersed evenly)
59−occasional :: TestPlan t m => Word -> Word -> Word -> m [Event t Word]
60−occasional n period frames = traverse plan $ zipWith occs [1..] phases
61− where
62− phases = take (fromIntegral n) $ concat (repeat [1..period])
63− occs i phase = (, i) <$> [1, phase+1..frames]
64−
65−
66−
67−-- Event which never fires (but isn't 'never')
68−never' :: TestPlan t m => m (Event t Word)
69−never' = plan []
70−
71−
72−-- Event which fires once on first frame
73−event :: TestPlan t m => m (Event t Word)
74−event = plan [(1, 0)]
75−
76−-- Event which fires constantly over N frames
77−events :: TestPlan t m => Word -> m (Event t Word)
78−events n = plan $ (\i -> (i, i)) <$> [1..n]
79−
80−
81−-- N events all originating from one event
82−fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
83−fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
84−
85−
86−-- Make a fan using the value in the event converting it to a singleton Map
87−-- use a fan of size n (key is the value mod n)
88−fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
89−fanMerge n e = leftmost $ s <$> [0..n - 1] where
90− s k = select f (Const2 k)
91− f = fan (toMap <$> e)
92− toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
93−
94−-- Dumb version of the above using simple fmapMaybe to filter
95−fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
96−fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
97− s k = flip fmapMaybe e $ \j -> if (k == j `mod` n)
98− then Just k
99− else Nothing
100−
101−
102−
103−-- Create an Event with a Map which is dense to size N
104−denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
105−denseMap n = plan [(1, m)] where
106− m = Map.fromList $ zip [1..n] [1..n]
107−
108−iter :: (a -> a) -> Word -> a -> a
109−iter _ 0 a = a
110−iter f n a = iter f (n - 1) (f a)
111−
112−iterM :: Monad m => (a -> m a) -> Word -> a -> m a
113−iterM _ 0 a = return a
114−iterM f n a = iterM f (n - 1) =<< f a
115−
116−fmapChain :: (Functor f) => Word -> f Word -> f Word
117−fmapChain = iter (fmap (+1))
118−
119−mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
120−mapDynChain = iterM (mapDyn (+1))
121−
122−joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
123−joinDynChain = iterM (\d -> joinDyn <$> mapDyn (const d) d)
124−
125−combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
126−combineDynChain = iterM (\d -> combineDyn (+) d d)
127−
128−pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
129−pingPong e1 e2 = do
130− rec
131− d <- foldDyn (const swap) (e1, e2) e
132− let e = switch (fst <$> current d)
133− return e
134−
135−switchFactors :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
136−switchFactors 0 e = return e
137−switchFactors i e = do
138− b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
139− switchFactors (i - 1) (switch b)
140−
141−
142−switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
143−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
144−
145−
146−switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
147−switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
148−
149−switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
150−switchPromptlyChain = iterM $ \e ->
151− switchPromptlyDyn <$> holdDyn e (e <$ e)
152−
153−
154−coinChain :: Reflex t => Word -> Event t Word -> Event t Word
155−coinChain = iter $ \e -> coincidence (e <$ e)
156−
157−fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
158−fanMergeChain width = iter $ fanMerge width
159−
160−fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
161−fmapFanMergeChain width = iter $ fmapFanMerge width
162−
163−-- | Data type for a collection (a map) which is updated by providing differences
164−data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
165−
166−-- This will hopefully become a primitive (faster!)
167−switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
168−switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
169−
170−-- switchMergeMaybe :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
171−-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
172−
173−switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
174−switchMergeBehaviors mapChanges = pull <$> joinMap <$> holdMap mapChanges
175− where joinMap m = traverse sample =<< sample m
176−
177−-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
178−holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
179−holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
180−
181− where
182− modify k Nothing items = Map.delete k items
183− modify k (Just item) items = Map.insert k item items
184−
185−
186−-- | Hold an UpdatedMap as a behavior by applying differences to the initial value
187−holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
188−holdMap = (current <$>) . holdMapDyn
189−
190−
191−increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
192−increasingMerge xs = do
193− e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
194− return (UpdatedMap Map.empty e)
195− where
196− reset = Map.fromList $ zip [1..] (const Nothing <$> xs)
197−
198−decreasingMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
199−decreasingMerge xs = do
200− e <- planList (zipWith Map.singleton [1..] (const Nothing <$> xs) ++ [reset])
201− return (UpdatedMap initial e)
202− where
203− initial = Map.fromList $ zip [1..] xs
204− reset = Just <$> initial
205−
206−modifyMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
207−modifyMerge xs = do
208− e <- planList $ zipWith Map.singleton [1..] (Just <$> xs)
209− return (UpdatedMap initial e)
210− where
211− initial = Map.fromList $ zip [1..] xs
212−
213−
214−countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
215−countMany = traverse (fmap current . count)
216−
217−countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
218−countManyDyn = traverse count
219−
220−
221−-- Given a function which creates a sub-network (from a single event)
222−-- Create a plan which uses a switch to substitute it
223−switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
224−switches numFrames f = do
225− es <- events numFrames
226− switch <$> hold never (makeEvents es)
227−
228− where
229− makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
230−
231−
232−
233−-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
234−-- the other, which we're only interested in time for running frames
235−subscribing :: Word -> Word -> [(String, TestCase)]
236−subscribing n frames =
237− [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
238− , testSub "fanMerge" $ return . fanMerge n
239− , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
240− , testSub "fmapChain" $ return . fmapChain n
241− , testSub "switchChain" $ switchChain n
242− , testSub "switchPromptlyChain" $ switchPromptlyChain n
243− , testSub "switchFactors" $ switchFactors n
244− , testSub "coincidenceChain" $ return . coinChain n
245− ]
246−
247− where
248− testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
249− testSub name test = testE name (switches frames test)
250−
251−-- Set of benchmark to test specifically merging dynamic collections
252−-- a pattern which occurs frequently in reflex-dom collections
253−merging :: Word -> [(String, TestCase)]
254−merging n =
255− [ testE "static events" $ mergeList <$> sparse
256− , testE "increasing events" $ switchMergeEvents =<< increasingMerge =<< sparse
257− , testE "decreasing events" $ switchMergeEvents =<< decreasingMerge =<< sparse
258− , testE "modify events" $ switchMergeEvents =<< modifyMerge =<< sparse
259−
260− , testB "static behaviors" $ pull . traverse sample <$> counters
261− , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
262− , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
263− , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
264−
265−
266− -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
267− -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
268− -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
269− ]
270−
271− where
272− sparse :: TestPlan t m => m [Event t Word]
273− sparse = fmap (fmap (+1)) <$> occasional n 8 n
274−
275− counters :: TestPlan t m => m [Behavior t Int]
276− counters = countMany =<< sparse
277−
278−
279−
280−fans :: Word -> [(String, TestCase)]
281−fans n =
282− [ testE "fanMapChain" $ fanMergeChain n 10 <$> e
283− , testE "fmapFanMapChain" $ fmapFanMergeChain n 10 <$> e
284−
285− , testE "fanMerge " $ fanMerge n <$> events n
286−
287− , testE "fmapFanMerge " $ fmapFanMerge n <$> events n
288− , testE "triggerMerge" $ leftmost <$> for [1..n] (\i -> plan [(i, i)])
289−
290− ]
291− where
292−
293− e :: TestPlan t m => m (Event t Word)
294− e = events 100
295−
296−
297−
298−
299−dynamics :: Word -> [(String, TestCase)]
300−dynamics n =
301− [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
302− , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
303− , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
304− , testE "dense mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< dense
305− , testE "sparse mergeTree" $ fmap updated $ mergeTreeDyn 8 =<< sparse
306− , testE "mergeDyn" $ fmap updated $ mapDyn sum =<< mergeListDyn =<< sparse
307− ] where
308−
309− d :: TestPlan t m => m (Dynamic t Word)
310− d = count =<< events 10
311−
312− sparse, dense :: TestPlan t m => m [Dynamic t Int]
313− sparse = countManyDyn =<< occasional n 8 16
314− dense = countManyDyn =<< continuous n 2
315−
316−
317−firing :: Word -> [(String, TestCase)]
318−firing n =
319− [ testE "fmapChain" $ fmapChain n <$> e
320− , testE "switchChain2" $ switchChain2 n =<< e
321− , testE "switchPromptlyChain" $ switchPromptlyChain n =<< e
322− , testE "switchFactors" $ switchFactors n =<< e
323− , testE "coincidenceChain" $ coinChain n <$> e
324−
325− , testE "dense mergeTree" $ mergeTree 8 <$> dense
326− , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
327−
328− -- Triggers test failure in Spider
329− --, testE "runFrame" $ events n
330−
331− , testB "sum counters" $ do
332− counts <- counters
333− return $ pull $ sum <$> traverse sample counts
334−
335− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
336− , testB "pullChain2" $ do
337− es <- events 4
338− e' <- plan [(0, ())]
339−
340− b <- hold (constant 0) $
341− pushAlways (const $ fmapChain n <$> hold 0 es) e'
342−
343− return (join b)
344− , testB "counters/pullTree" $ mergeTree 8 <$> counters
345−
346− ]
347− where
348− e :: TestPlan t m => m (Event t Word)
349− e = events 10
350−
351− dense, sparse :: TestPlan t m => m [Event t Word]
352− dense = continuous n 2
353− sparse = occasional n 8 16
354−
355− counters :: TestPlan t m => m [Behavior t Int]
356− counters = countMany =<< sparse
357−
358−
359−
360−
361−
test/Reflex/Plan/Pure.hsdeleted−64
@@ -1,64 +0,0 @@
1−{-# LANGUAGE GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving, ScopedTypeVariables #-}
2−
3−module Reflex.Plan.Pure where
4−
5−import Reflex
6−import Reflex.Pure
7−import Reflex.TestPlan
8−
9−import Control.Applicative
10−import Control.Monad.Fix
11−import Control.Monad.State
12−import Data.IntMap (IntMap)
13−import qualified Data.IntMap as IntMap
14−import Data.IntSet (IntSet)
15−import qualified Data.IntSet as IntSet
16−
17−import Data.Monoid
18−import Data.Bifunctor
19−import Data.Maybe
20−import Prelude
21−
22−
23−mapToPureEvent :: IntMap a -> Event (Pure Int) a
24−mapToPureEvent m = Event $ flip IntMap.lookup m
25−
26−type TimeM = (->) Int
27−newtype PurePlan a = PurePlan { unPlan :: StateT IntSet TimeM a } deriving (Functor, Applicative, Monad, MonadFix)
28−
29−liftPlan :: TimeM a -> PurePlan a
30−liftPlan = PurePlan . lift
31−
32−instance MonadHold (Pure Int) PurePlan where
33− hold initial = liftPlan . hold initial
34− holdDyn initial = liftPlan . holdDyn initial
35− holdIncremental initial = liftPlan . holdIncremental initial
36−
37−instance MonadSample (Pure Int) PurePlan where
38− sample = liftPlan . sample
39−
40−
41−instance TestPlan (Pure Int) PurePlan where
42− plan occs = do
43− PurePlan . modify $ IntSet.union (IntMap.keysSet m)
44− return $ mapToPureEvent m
45− where m = IntMap.fromList (first fromIntegral <$> occs)
46−
47−runPure :: PurePlan a -> (a, IntSet)
48−runPure (PurePlan p) = runStateT p mempty $ 0
49−
50−relavantTimes :: IntSet -> IntSet
51−relavantTimes occs = IntSet.fromList [0..l + 1]
52− where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
53−
54−testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
55−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
56−
57−testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
58−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
59−
60−
61−
62−
63−
64−
test/Reflex/Plan/Reflex.hsdeleted−126
@@ -1,126 +0,0 @@
1−{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
2−module Reflex.Plan.Reflex
3− ( TestPlan(..)
4− , runPlan
5− , Plan (..)
6− , Schedule
7− , Firing (..)
8− , MonadIORef
9−
10−
11− , readSchedule
12− , readSchedule_
13− , testSchedule
14− , readEvent'
15− , makeDense
16−
17− , runTestE
18− , runTestB
19−
20− ) where
21−
22−import Reflex.Class
23−import Reflex.Host.Class
24−import Reflex.TestPlan
25−
26−import Control.Applicative
27−import Control.Monad
28−import Control.Monad.Identity
29−import Control.Monad.State.Strict
30−
31−import Data.Dependent.Sum (DSum (..))
32−import Data.Monoid
33−import Data.Traversable (traverse, sequenceA)
34−import Data.Maybe
35−import qualified Data.IntMap.Strict as IntMap
36−import Control.Monad.Ref
37−
38−import Data.IntMap.Strict (IntMap)
39−import Data.IORef
40−import System.Mem
41−import Control.Exception
42−import Control.DeepSeq
43−
44−-- Note: this import must come last to silence warnings from AMP
45−import Prelude
46−
47−type MonadIORef m = (MonadIO m, MonadRef m, Ref m ~ Ref IO)
48−
49−data Firing t where
50− Firing :: IORef (Maybe (EventTrigger t a)) -> a -> Firing t
51−
52−
53−type Schedule t = IntMap [Firing t]
54−
55−-- Implementation of a TestPlan in terms of ReflexHost
56−newtype Plan t a = Plan (StateT (Schedule t) (HostFrame t) a)
57−
58−deriving instance ReflexHost t => Functor (Plan t)
59−deriving instance ReflexHost t => Applicative (Plan t)
60−deriving instance ReflexHost t => Monad (Plan t)
61−
62−deriving instance ReflexHost t => MonadSample t (Plan t)
63−deriving instance ReflexHost t => MonadHold t (Plan t)
64−deriving instance ReflexHost t => MonadFix (Plan t)
65−
66−
67−instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => TestPlan t (Plan t) where
68− plan occurances = Plan $ do
69− (e, ref) <- newEventWithTriggerRef
70− modify (IntMap.unionWith mappend (firings ref))
71− return e
72−
73− where
74− firings ref = IntMap.fromList (makeFiring ref <$> occurances)
75− makeFiring ref (t, a) = (fromIntegral t, [Firing ref a])
76−
77−
78−firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
79−firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
80−
81−runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
82−runPlan (Plan p) = runHostFrame $ runStateT p mempty
83−
84−
85−makeDense :: Schedule t -> Schedule t
86−makeDense s = fromMaybe (emptyRange 0) $ do
87− (end, _) <- fst <$> IntMap.maxViewWithKey s
88− return $ IntMap.union s (emptyRange end)
89− where
90− emptyRange end = IntMap.fromList (zip [0..end + 1] (repeat []))
91−
92−
93−-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
94−-- after the last event)
95−-- performGC is called at each frame to test for GC issues
96−testSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
97−testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
98−
99−readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
100−readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
101−
102−readSchedule_ :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m ()
103−readSchedule_ schedule readResult = mapM_ (uncurry $ triggerFrame readResult) $ IntMap.toList schedule
104−
105−triggerFrame :: (MonadReflexHost t m, MonadIORef m, NFData a) => ReadPhase m a -> Int -> [Firing t] -> m a
106−triggerFrame readResult _ occs = do
107− triggers <- catMaybes <$> traverse firingTrigger occs
108− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
109−
110−readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
111−readEvent' = readEvent >=> sequenceA
112−
113−
114−-- Convenience functions for running tests producing Events/Behaviors
115−runTestB :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Behavior t a) -> m (IntMap a)
116−runTestB p = do
117− (b, s) <- runPlan p
118− testSchedule s $ sample b
119−
120−runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
121−runTestE p = do
122− (e, s) <- runPlan p
123− h <- subscribeEvent e
124− testSchedule s (readEvent' h)
125−
126−
test/Reflex/Pure.hsdeleted−155
@@ -1,155 +0,0 @@
1−{- | This module provides a pure implementation of Reflex, which is intended
2−to serve as a reference for the semantics of the Reflex class. All implementations
3−of Reflex should produce the same results as this implementation, although performance
4−and laziness/strictness may differ.
5−-}
6−
7−{-# LANGUAGE TypeFamilies, MultiParamTypeClasses, EmptyDataDecls, InstanceSigs, FlexibleInstances, ScopedTypeVariables #-}
8−
9−-- There are two expected orphan instances in this module:
10−-- * MonadSample (Pure t) ((->) t)
11−-- * MonadHold (Pure t) ((->) t)
12−{-# OPTIONS_GHC -fno-warn-orphans #-}
13−
14−module Reflex.Pure where
15−
16−import Reflex.Class
17−import Data.Functor.Misc
18−import Data.Functor.Identity
19−import Data.MemoTrie
20−import Data.Dependent.Map (DMap, GCompare)
21−import qualified Data.Dependent.Map as DMap
22−import Control.Monad
23−import Data.Monoid
24−
25−data Pure t
26−
27−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
28−-- any y :: t such that pred x < y < x. The HasTrie instance will be used exclusively
29−-- to memoize functions of t, not for any of its other capabilities.
30−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
31−
32− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
33− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
34− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
35− newtype Incremental (Pure t) p a = Incremental { unIncremental :: t -> (a, Maybe (p a)) }
36−
37− type PushM (Pure t) = (->) t
38− type PullM (Pure t) = (->) t
39−
40− never :: Event (Pure t) a
41− never = Event $ \_ -> Nothing
42−
43− constant :: a -> Behavior (Pure t) a
44− constant x = Behavior $ \_ -> x
45−
46− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
47− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
48−
49− pull :: PullM (Pure t) a -> Behavior (Pure t) a
50− pull = Behavior . memo
51−
52− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a warning because the GCompare instance is not used; however, removing the GCompare instance produces a different warning, due to that constraint being present in the original class definition
53− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
54− merge events = Event $ memo $ \t ->
55− let currentOccurrences = unwrapDMapMaybe (($ t) . unEvent) events
56− in if DMap.null currentOccurrences
57− then Nothing
58− else Just currentOccurrences
59−
60− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
61− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
62−
63− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
64− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
65−
66− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
67− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
68−
69− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
70− current d = Behavior $ \t -> fst $ unDynamic d t
71−
72− updated :: Dynamic (Pure t) a -> Event (Pure t) a
73− updated d = Event $ \t -> snd $ unDynamic d t
74−
75− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
76− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
77−
78− -- See UNUSED_CONSTRAINT, above.
79− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p a) -> Incremental (Pure t) p a
80− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
81−
82− mergeIncremental (Incremental f) = Event $ \t ->
83− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ f t
84− in if DMap.null results
85− then Nothing
86− else Just results
87−
88− currentIncremental (Incremental f) = Behavior $ \t -> fst $ f t
89−
90− updatedIncremental (Incremental f) = Event $ \t -> snd $ f t
91−
92− incrementalToDynamic (Incremental f) = Dynamic $ \t ->
93− let (old, mPatch) = f t
94− e = case mPatch of
95− Nothing -> Nothing
96− Just patch -> apply patch old
97− in (old, e)
98−
99−instance Functor (Dynamic (Pure t)) where
100− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
101− in (f cur, fmap f upd)
102−
103−instance Applicative (Dynamic (Pure t)) where
104− pure a = Dynamic $ \_ -> (a, Nothing)
105− (<*>) = ap
106−
107−instance Monad (Dynamic (Pure t)) where
108− return = pure
109− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
110− let (curX :: a, updX :: Maybe a) = unDynamic x t
111− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
112− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
113− Nothing -> (Nothing, Nothing)
114− Just nextX -> let (c, u) = unDynamic (f nextX) t
115− in (Just c, u)
116− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
117−
118−instance MonadSample (Pure t) ((->) t) where
119−
120− sample :: Behavior (Pure t) a -> (t -> a)
121− sample = unBehavior
122−
123−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
124−
125− hold :: a -> Event (Pure t) a -> (t -> (Behavior (Pure t) a))
126− hold initialValue e initialTime = Behavior f
127− where f = memo $ \sampleTime ->
128− -- Really, the sampleTime should never be prior to the initialTime,
129− -- because that would mean the Behavior is being sampled before being created.
130− if sampleTime <= initialTime
131− then initialValue
132− else let lastTime = pred sampleTime
133− in case unEvent e lastTime of
134− Nothing -> f lastTime
135− Just x -> x
136−
137− holdDyn :: a -> Event (Pure t) a -> (t -> (Dynamic (Pure t) a))
138− holdDyn initialValue e initialTime =
139− let Behavior f = hold initialValue e initialTime
140− in Dynamic $ \t -> (f t, unEvent e t)
141−
142− holdIncremental :: Patch p => a -> Event (Pure t) (p a) -> (t -> (Incremental (Pure t) p a))
143− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
144− where f = memo $ \sampleTime ->
145− -- Really, the sampleTime should never be prior to the initialTime,
146− -- because that would mean the Behavior is being sampled before being created.
147− if sampleTime <= initialTime
148− then initialValue
149− else let lastTime = pred sampleTime
150− lastValue = f lastTime
151− in case unEvent e lastTime of
152− Nothing -> lastValue
153− Just x -> case apply x lastValue of
154− Nothing -> lastValue
155− Just v' -> v'
test/Reflex/Test.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−
3−module Reflex.Test
4− ( testAgreement
5− , compareResult
6− , runTests
7−
8− , module Reflex.TestPlan
9−
10− ) where
11−
12−import Reflex.Spider
13−
14−import Reflex.TestPlan
15−
16−import Reflex.Plan.Pure
17−import Reflex.Plan.Reflex
18−
19−import Control.Monad
20−import Data.Monoid
21−
22−import Data.IntMap (IntMap)
23−
24−--import Data.Foldable
25−import System.Exit
26−
27−import Prelude
28−
29−
30−testAgreement :: TestCase -> IO Bool
31−testAgreement (TestE p) = do
32− spider <- runSpiderHost $ runTestE p
33− let results = [("spider", spider)]
34−
35− compareResult results (testEvent $ runPure p)
36−
37−testAgreement (TestB p) = do
38− spider <- runSpiderHost $ runTestB p
39− let results = [("spider", spider)]
40−
41− compareResult results (testBehavior $ runPure p)
42−
43−
44−compareResult :: (Show a, Eq a) => [(String, IntMap a)] -> IntMap a -> IO Bool
45−compareResult results expected = fmap and $ forM results $ \(name, r) -> do
46−
47− when (r /= expected) $ do
48− putStrLn ("Got: " ++ show (name, r))
49− putStrLn ("Expected: " ++ show expected)
50− return (r == expected)
51−
52−
53−runTests :: [(String, TestCase)] -> IO ()
54−runTests testCases = do
55− results <- forM testCases $ \(name, test) -> do
56− putStrLn $ "Test: " <> name
57− testAgreement test
58− exitWith $ if and results
59− then ExitSuccess
60− else ExitFailure 1
61−
test/Reflex/Test/CrossImpl.hsdeleted−228
@@ -1,228 +0,0 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, BangPatterns, ConstraintKinds #-}
2−module Reflex.Test.CrossImpl (test) where
3−
4−import Prelude hiding (mapM, mapM_, sequence, sequence_, foldl, and)
5−
6−import Reflex.Class
7−import Reflex.Host.Class
8−import Reflex.Dynamic
9−import qualified Reflex.Spider.Internal as S
10−import qualified Reflex.Pure as P
11−import Control.Monad.Ref
12−
13−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
14−import qualified Data.Set as Set
15−import Data.Map.Strict (Map)
16−import qualified Data.Map.Strict as Map
17−import Control.Arrow (second, (&&&))
18−import Data.Traversable
19−import Data.Foldable
20−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
21−import Control.Monad.Writer hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
22−import Data.Dependent.Map (DSum (..))
23−import System.Mem
24−import System.Exit
25−
26−import System.IO.Unsafe
27−
28−mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
29−mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
30− Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
31− Just (_, v) -> v
32−
33−mapToPureEvent :: Map Int a -> Event PureReflexDomain a
34−mapToPureEvent m = P.Event $ flip Map.lookup m
35−
36−relevantTestingTimes :: (Map Int a, Map Int b) -> [Int]
37−relevantTestingTimes (b, e) = case Set.minView &&& Set.maxView $ Map.keysSet b `Set.union` Map.keysSet e of
38− (Just (t0, _), Just (t1, _)) -> [t0..t1+1] -- We need to go to b+1 to see the result of the final event
39− _ -> [] -- Doesn't actually make much sense
40−
41−type PureReflexDomain = P.Pure Int
42−type TimeM = (->) Int
43−
44−testPure :: (t ~ PureReflexDomain, m ~ TimeM) => ((Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> (Map Int c, Map Int d)
45−testPure builder (b, e) =
46− let (P.Behavior b', P.Event e') = ($ 0) $ builder (mapToPureBehavior b, mapToPureEvent e)
47− relevantTimes = relevantTestingTimes (b, e)
48− e'' = Map.mapMaybe id $ Map.fromList $ map (id &&& e') relevantTimes
49− b'' = Map.fromList $ map (id &&& b') relevantTimes
50− in (b'', e'')
51−
52−class MapMSignals a a' t t' | a -> t, a' -> t', a t' -> a', a' t -> a where
53− mapMSignals :: Monad m => (forall b. Behavior t b -> m (Behavior t' b)) -> (forall b. Event t b -> m (Event t' b)) -> a -> m a'
54−
55−instance MapMSignals (Behavior t a) (Behavior t' a) t t' where
56− mapMSignals fb _ = fb
57−
58−instance MapMSignals (Event t a) (Event t' a) t t' where
59− mapMSignals _ fe = fe
60−
61−instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
62− mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
63−
64−testSpider :: (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> (Map Int c, Map Int d)
65−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
66− (re, reTrigger) <- newEventWithTriggerRef
67− (rb, rbTrigger) <- newEventWithTriggerRef
68− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
69− (b', e') <- runHostFrame $ builder (b, re)
70− e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
71− let times = relevantTestingTimes (bMap, eMap)
72− liftIO performGC
73− outputs <- forM times $ \t -> do
74− forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
75− bOutput <- sample b'
76− eOutput <- liftM join $ forM (Map.lookup t eMap) $ \val -> do
77− mret <- readRef reTrigger
78− let firing = case mret of
79− Just ret -> [ret :=> Identity val]
80− Nothing -> []
81− fireEventsAndRead firing $ sequence =<< readEvent e'Handle
82− liftIO performGC
83− return (t, (bOutput, eOutput))
84− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
85−
86−tracePerf :: Show a => a -> b -> b
87−tracePerf = flip const
88−
89−testAgreement :: (Eq c, Eq d, Show c, Show d) => (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> IO Bool
90−testAgreement builder inputs = do
91− let identityResult = testPure builder inputs
92− tracePerf "---------" $ return ()
93− let spiderResult = testSpider builder inputs
94− tracePerf "---------" $ return ()
95− let resultsAgree = identityResult == spiderResult
96− if resultsAgree
97− then do putStrLn "Success:"
98− print identityResult
99− else do putStrLn "Failure:"
100− putStrLn $ "Pure result: " <> show identityResult
101− putStrLn $ "Spider result: " <> show spiderResult
102− return resultsAgree
103−
104−type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
105−
106−data TestCase = forall a b c d. (Eq c, Eq d, Show c, Show d) => TestCase (Map Int a, Map Int b) (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d))
107−
108−testCases :: [(String, TestCase)]
109−testCases =
110− [ (,) "hold" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
111− b' <- hold "123" e
112− return (b', e)
113− , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
114− e' <- liftM updated $ count e
115− b' <- hold (0 :: Int) e'
116− return (b', e')
117− , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
118− e' <- onceE $ leftmost [e, e]
119− return (b, e')
120− , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
121− let e' = fmap (const e) e
122− b' <- hold never e'
123− let e'' = switch b'
124− return (b, e'')
125− , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
126− let e' = flip pushAlways e $ const $ do
127− let ea = fmap (const "a") e
128− let eb = fmap (const "b") e
129− let eab = leftmost [ea, eb]
130− liftM switch $ hold eab never
131− e'' = coincidence e'
132− return (b, e'')
133− , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134− let e' = flip pushAlways e $ const $ do
135− let ea = fmap (const "a") e
136− let eb = fmap (const "b") e
137− let eab = leftmost [ea, eb]
138− liftM switch $ hold eab (fmap (const e) e)
139− e'' = coincidence e'
140− return (b, e'')
141− , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
142− let e' = leftmost [e, e]
143− e'' <- liftM switch $ hold e' (fmap (const e) e)
144− return (b, e'')
145− , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
146− let e' = fmap (const e) e
147− e'' <- switchPromptly never e'
148− return (b, e'')
149− , (,) "switchPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
150− let e' = fmap (const e) e
151− e'' <- switchPromptly never $ leftmost [e', e']
152− return (b, e'')
153− , (,) "switchPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
154− let e' = leftmost [e, e]
155− e'' <- switchPromptly never (fmap (const e) e')
156− return (b, e'')
157− , (,) "switchPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
158− let e' = leftmost [e, e]
159− e'' <- switchPromptly never (fmap (const e') e)
160− return (b, e'')
161− , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
162− let e' = leftmost [e, e]
163− e'' <- liftM switch $ hold never (fmap (const e') e)
164− return (b, e'')
165− , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
166− let e' = flip push e $ \_ -> do
167− return . Just =<< onceE e
168− e'' <- switchPromptly never e'
169− return (b, e'')
170− , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171− let e' = flip pushAlways e $ \_ -> do
172− switchPromptly e never
173− e'' <- switchPromptly never e'
174− return (b, e'')
175− , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
176− let e' = flip pushAlways e $ \_ -> return $ fmap id e
177− e'' = coincidence e'
178− return (b, e'')
179− , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
180− let e' = flip pushAlways e $ \_ -> return $ leftmost [e, e]
181− e'' = coincidence e'
182− return (b, e'')
183− , (,) "coincidence-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
184− let e' = flip pushAlways e $ \_ -> return $ coincidence $ fmap (const e) e
185− e'' = coincidence e'
186− return (b, e'')
187− , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
188− let e' = flip pushAlways e $ \_ -> onceE e
189− e'' = coincidence e'
190− return (b, e'')
191− , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
192− let eChild = flip pushAlways e $ const $ do
193− let eNewValues = leftmost [e, e]
194− return $ coincidence $ fmap (const eNewValues) eNewValues
195− e' = coincidence eChild
196− return (b, e')
197− , (,) "coincidence-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
198− let eChild = flip pushAlways e $ const $ do
199− let e' = coincidence $ fmap (const e) e
200− return $ leftmost [e', e']
201− e'' = coincidence eChild
202− return (b, e'')
203− , (,) "coincidence-7" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
204− let e' = leftmost [e, e]
205− eCoincidences = coincidence $ fmap (const e') e
206− return (b, eCoincidences)
207− , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
208− eo <- onceE e
209− bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
210− let b' = pull $ sample =<< sample bb
211− return (b', e)
212− , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
213− bb <- hold "b" e
214− bd <- hold never . fmap (const e) =<< onceE e
215− eOuter <- liftM (pushAlways sample . fmap (const bb)) $ onceE e
216− let eInner = switch bd
217− e' = leftmost [eOuter, eInner]
218− return (b, e')
219− ]
220−
221−test :: IO ()
222−test = do
223− results <- forM testCases $ \(name, TestCase inputs builder) -> do
224− putStrLn $ "Test: " <> name
225− testAgreement builder inputs
226− exitWith $ if and results
227− then ExitSuccess
228− else ExitFailure 1
test/Reflex/Test/Micro.hsdeleted−258
@@ -1,258 +0,0 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−
3−module Reflex.Test.Micro (testCases) where
4−
5−import Reflex
6−import Reflex.TestPlan
7−
8−import Control.Monad
9−import Control.Applicative
10−import Data.Char
11−import Data.Monoid
12−import Data.Functor.Misc
13−
14−import Data.Foldable
15−
16−import qualified Data.Map as Map
17−
18−import Prelude
19−
20−testCases :: [(String, TestCase)]
21−testCases =
22− [ testB "hold" $ hold "0" =<< events1
23−
24− , testB "count" $ do
25− b <- current <$> (count =<< events2)
26− return $ (+ (0::Int)) <$> b
27−
28− , testB "pull-1" $ do
29− b <- hold "0" =<< events1
30− return (id <$> id <$> b)
31−
32− , testB "pull-2" $ do
33− b1 <- behavior1
34− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b1))
35−
36− , testB "pull-3" $ do
37− b1 <- behavior1
38− b2 <- behavior2
39− return (id <$> pull $ liftA2 (<>) (sample b1) (sample b2))
40−
41− , testB "pull-4" $ do
42− es <- planList ["a", "b", "c"]
43− e <- plan [(0, ())]
44− b <- hold (constant "") $
45− pushAlways (const $ hold "z" es) e
46− return (join b)
47−
48− , testE "tag-1" $ do
49− b1 <- behavior1
50− e <- events2
51− return (tag b1 e)
52−
53− , testE "tag-2" $ do
54− b1 <- behavior1
55− e <- events2
56− return (tag (map toUpper <$> b1) e)
57−
58− , testE "attach-1" $ do
59− b1 <- behavior1
60− e <- events2
61− return (attachWith (++) (map toUpper <$> b1) e)
62−
63− , testE "leftmost" $ liftA2 leftmost2 events1 events2
64−
65− , testE "appendEvents-1" $ liftA2 appendEvents events1 events2
66−
67− , testE "appendEvents-2" $ liftA2 appendEvents events3 events2
68−
69− , testE "merge-1" $ do
70− e <- events1
71− return $ leftmost ["x" <$ e, "y" <$ e]
72−
73− , testE "merge-2" $ do
74− e <- events1
75− let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
76− let ee = flip pushAlways e $ const $ return m
77− return $ coincidence ee
78−
79− , testE "onceE-1" $ do
80− e <- events1
81− onceE $ leftmost [e, e]
82−
83− , testE "onceE-2" $ do
84− e <- events1
85− b <- hold never (e <$ e)
86− onceE $ switch b
87−
88− , testE "switch-1" $ do
89− e <- events1
90− b <- hold never (e <$ e)
91− return $ switch b
92−
93− , testE "switch-2" $ do
94− e <- events1
95− return $ coincidence $ flip pushAlways e $ const $ do
96− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e)
97−
98− , testE "switch-3" $ do
99− e <- events1
100− return $ coincidence $ flip pushAlways e $ const $ do
101− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never
102−
103− , testE "switch-4" $ do
104− e <- events1
105− switch <$> hold (deep e) (e <$ e)
106−
107− , testE "switch-5" $ do
108− e <- events1
109− return $ coincidence $ flip pushAlways e $ const $
110− return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e]
111−
112− , testE "switch-6" $ do
113− e <- events1
114− return $ coincidence $ flip pushAlways e $ const $ do
115− switch <$> hold ("x" <$ e) (e <$ e)
116−
117− , testE "switchPromptly-1" $ do
118− e <- events1
119− let e' = e <$ e
120− switchPromptly never $ e <$ e'
121−
122− , testE "switchPromptly-2" $ do
123− e <- events1
124− switchPromptly never $ deep (e <$ e)
125−
126− , testE "switchPromptly-3" $ do
127− e <- events1
128− switchPromptly never $ (e <$ deep e)
129−
130− , testE "switchPromptly-4" $ do
131− e <- events1
132− switchPromptly never $ (deep e <$ e)
133−
134− , testE "switch-5" $ do
135− e <- events1
136− switch <$> hold never (deep e <$ e)
137−
138− , testE "switchPromptly-5" $ do
139− e <- events1
140− switchPromptly never $ flip push e $
141− const (Just <$> onceE e)
142−
143− , testE "switchPromptly-6" $ do
144− e <- events1
145− switchPromptly never $ flip pushAlways e $
146− const (switchPromptly e never)
147−
148− , testE "coincidence-1" $ do
149− e <- events1
150− return $ coincidence $ flip pushAlways e $
151− const $ return (id <$> e)
152−
153− , testE "coincidence-2" $ do
154− e <- events1
155− return $ coincidence $ flip pushAlways e $
156− const $ return (deep e)
157−
158− , testE "coincidence-3" $ do
159− e <- events1
160− return $ coincidence $ flip pushAlways e $
161− const $ return (coincidence (e <$ e))
162−
163− , testE "coincidence-4" $ do
164− e <- events1
165− return $ coincidence $ flip pushAlways e $
166− const (onceE e)
167−
168− , testE "coincidence-5" $ do
169− e <- events1
170− return $ coincidence $ flip pushAlways e $ const $ do
171− let e' = deep e
172− return (coincidence (e' <$ e'))
173−
174− , testE "coincidence-6" $ do
175− e <- events1
176− return $ coincidence $ flip pushAlways e $ const $ do
177− let e' = coincidence (e <$ e)
178− return $ deep e'
179−
180− , testE "coincidence-7" $ do
181− e <- events1
182− return $ coincidence (deep e <$ e)
183−
184− , testB "holdWhileFiring" $ do
185− e <- events1
186− eo <- onceE e
187− bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
188− return $ pull $ sample =<< sample bb
189−
190− , testE "joinDyn" $ do
191− e <- events1
192− bb <- hold "b" e
193− bd <- hold never . fmap (const e) =<< onceE e
194−
195− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
196− let eInner = switch bd
197− return $ leftmost [eOuter, eInner]
198−
199− , testB "foldDyn" $ do
200− d <- foldDyn (++) "0" =<< events1
201− return (current d)
202−
203− , testB "mapDyn" $ do
204− d <- foldDyn (++) "0" =<< events1
205− current <$> mapDyn (map toUpper) d
206−
207− , testB "combineDyn" $ do
208− d1 <- foldDyn (++) "0" =<< events1
209− d2 <- mapDyn (map toUpper) =<< foldDyn (++) "0" =<< events2
210−
211− current <$> combineDyn (<>) d1 d2
212− , testE "fan-1" $ do
213− e <- fmap toMap <$> events1
214− let es = select (fanMap e) . Const2 <$> values
215−
216− return (mergeList es)
217−
218− , testE "fan-2" $ do
219− e <- fmap toMap <$> events3
220− let es = select (fanMap e) . Const2 <$> values
221−
222− return (mergeList es)
223−
224− , testE "fan-3" $ do
225− f <- fanMap <$> fmap toMap <$> events3
226− return $ select f (Const2 'c')
227−
228− , testE "fan-4" $ do
229− e <- fmap toMap <$> events1
230− return $ toUpper <$> select (fanMap e) (Const2 'a')
231−
232− , testE "fan-5" $ do
233− e <- fmap toMap <$> events2
234− return $ toUpper <$> select (fanMap e) (Const2 'c')
235−
236− , testE "fan-6" $ do
237− f <- fanMap <$> fmap toMap <$> events1
238− return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
239−
240− ] where
241−
242− events1, events2, events3 :: TestPlan t m => m (Event t String)
243− events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
244− events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
245−
246− events3 = liftA2 appendEvents events1 events2
247−
248− values = "abcde"
249− toMap str = Map.fromList $ map (\c -> (c, c)) str
250−
251− behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
252− behavior1 = hold "1" =<< events1
253− behavior2 = hold "2" =<< events2
254−
255− deep e = leftmost [e, e]
256− leftmost2 e1 e2 = leftmost [e1, e2]
257−
258−
test/Reflex/Test/RunAll.hsdeleted−44
@@ -1,44 +0,0 @@
1−{-# LANGUAGE ConstraintKinds, GADTs, RankNTypes, FlexibleInstances, FlexibleContexts, MultiParamTypeClasses, GeneralizedNewtypeDeriving #-}
2−
3−module Main (main) where
4−
5−import Reflex.Test
6−
7−import qualified Reflex.Bench.Focused as Focused
8−import qualified Reflex.Test.Micro as Micro
9−import Data.Functor
10−import Data.Bifunctor
11−import Data.List
12−
13−import System.Exit
14−import System.Environment
15−
16−import Prelude
17−
18−matchPrefixes :: [String] -> (String -> Bool)
19−matchPrefixes [] = const True
20−matchPrefixes args = \name -> any (`isPrefixOf` name) args
21−
22−
23−main :: IO ()
24−main = do
25− args <- getArgs
26−
27− case args of
28− ["--list"] -> mapM_ putStrLn (fst <$> allTests) >> exitWith (ExitFailure 1)
29− _ -> case filter (matchPrefixes args . fst) allTests of
30− [] -> putStrLn "filter did not match any tests" >> exitWith (ExitFailure 1)
31− tests -> runTests tests
32−
33− where
34− allTests = concat
35− [ makeGroup "micro" Micro.testCases
36− , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
37− , makeGroup "firing 1000" (Focused.firing 1000)
38− , makeGroup "merge 100" (Focused.merging 100)
39− , makeGroup "fan 50" (Focused.fans 50)
40− ]
41−
42− makeGroup name tests = first (\test -> intercalate "/" [name, test]) <$> tests
43−
44−
test/Reflex/TestPlan.hsdeleted−45
@@ -1,45 +0,0 @@
1−{-# LANGUAGE FunctionalDependencies, BangPatterns, UndecidableInstances, ConstraintKinds, GADTs, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, GeneralizedNewtypeDeriving, RankNTypes, RecursiveDo, FlexibleContexts, StandaloneDeriving #-}
2−module Reflex.TestPlan
3− ( TestPlan(..)
4−
5− , TestCase (..)
6− , testE, testB
7− , TestE, TestB
8−
9− , planList
10−
11− ) where
12−
13−import Reflex.Class
14−import Control.Monad.Fix
15−import Data.Word
16−import Control.DeepSeq
17−
18−
19−import Prelude
20−
21−class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
22− -- | Speicify a plan of an input Event firing
23− -- Occurances must be in the future (i.e. Time > 0)
24− -- Initial specification is
25−
26− plan :: [(Word, a)] -> m (Event t a)
27−
28−
29−planList :: TestPlan t m => [a] -> m (Event t a)
30−planList xs = plan $ zip [1..] xs
31−
32−type TestE a = forall t m. TestPlan t m => m (Event t a)
33−type TestB a = forall t m. TestPlan t m => m (Behavior t a)
34−
35−data TestCase where
36− TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
37− TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
38−
39−-- Helpers to declare test cases
40−testE :: (Eq a, Show a, NFData a) => String -> TestE a -> (String, TestCase)
41−testE name test = (name, TestE test)
42−
43−testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
44−testB name test = (name, TestB test)
45−
test/hlint.hsnew file+39
@@ -0,0 +1,39 @@
1+module Main where
2+
3+import Control.Monad
4+import Language.Haskell.HLint (hlint)
5+import System.Directory
6+import System.Exit (exitFailure, exitSuccess)
7+import System.FilePath
8+import System.FilePath.Find
9+
10+main :: IO ()
11+main = do
12+ pwd <- getCurrentDirectory
13+ let runHlint f = hlint $ f:
14+ [ "--ignore=Redundant do"
15+ , "--ignore=Use camelCase"
16+ , "--ignore=Redundant $"
17+ , "--ignore=Use &&"
18+ , "--ignore=Use &&&"
19+ , "--ignore=Use const"
20+ , "--ignore=Use >=>"
21+ , "--ignore=Use ."
22+ , "--ignore=Use unless"
23+ , "--ignore=Reduce duplication"
24+ , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
26+ , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
27+ ]
28+ recurseInto = and <$> sequence
29+ [ fileType ==? Directory
30+ , fileName /=? ".git"
31+ ]
32+ matchFile = and <$> sequence
33+ [ extension ==? ".hs"
34+ ]
35+ files <- find recurseInto matchFile (pwd </> "src") --TODO: Someday fix all hints in tests, etc.
36+ ideas <- fmap concat $ forM files $ \f -> do
37+ putStr $ "linting file " ++ drop (length pwd + 1) f ++ "... "
38+ runHlint f
39+ if null ideas then exitSuccess else exitFailure
test/tests.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+module Main where
3+
4+import Test.HUnit (runTestTT, (~:), assertEqual, errors, failures, test)
5+import Data.Patch ( Patch(apply) )
6+import Data.Patch.MapWithMove ( patchThatChangesMap )
7+import Data.Map as Map ( Map, fromList, singleton )
8+import Hedgehog (checkParallel, discover, Property, property, forAll, PropertyT, (===))
9+import Hedgehog.Gen as Gen ( int )
10+import Hedgehog.Range as Range ( linear )
11+import Control.Monad (replicateM)
12+import System.Exit (exitFailure, exitSuccess)
13+import Data.Sequence as Seq ( foldMapWithIndex, replicateM )
14+
15+main :: IO ()
16+main = do
17+ counts <- runTestTT $ test [
18+ "Simple Move" ~: (do
19+ let mapBefore = Map.fromList [(0,1)]
20+ mapAfter = Map.fromList [(0,0),(1,1)]
21+ patch = patchThatChangesMap mapBefore mapAfter
22+ afterPatch = apply patch mapBefore
23+ assertEqual "Patch creates the same Map" (Just mapAfter) afterPatch),
24+ "Property Checks" ~: propertyChecks
25+ ]
26+ if errors counts + failures counts == 0 then exitSuccess else exitFailure
27+
28+propertyChecks :: IO Bool
29+propertyChecks = checkParallel $$(discover)
30+
31+prop_patchThatChangesMap :: Property
32+prop_patchThatChangesMap = property $ do
33+ mapBefore <- makeRandomIntMap
34+ mapAfter <- makeRandomIntMap
35+ let patch = patchThatChangesMap mapBefore mapAfter
36+ Just mapAfter === apply patch mapBefore
37+
38+makeRandomIntMap :: Monad m => PropertyT m (Map Int Int)
39+makeRandomIntMap = do
40+ let genNum = Gen.int (Range.linear 0 100)
41+ length <- forAll genNum
42+ listOfNumbers <- forAll $ Seq.replicateM length genNum
43+ pure $ Seq.foldMapWithIndex Map.singleton listOfNumbers