on develop help sign in
55 files changed+2267−6924
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+47 .gitignore+5 .stylish-haskell.yaml+2−3 .travis.yml−26 CONTRIBUTING.md+66 ChangeLog.md+61 Quickref.md−176 README.md+44−11 bench-cbits/checkCapability.c+11−12 bench/Main.hs−109 bench/RunAll.hs−157 cabal.project+1 default.nix−26 dep/reflex-platform/default.nix+8 dep/reflex-platform/github.json+8 patch.cabal+100 reflex.cabal−134 release.nix+49 src/Data/Functor/Misc.hs+119−57 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−120 src/Reflex.hs−12 src/Reflex/Class.hs−814 src/Reflex/Deletable/Class.hs−17 src/Reflex/Dynamic.hs−561 src/Reflex/Dynamic/TH.hs−88 src/Reflex/Dynamic/Uniq.hs−101 src/Reflex/DynamicWriter.hs−181 src/Reflex/Host/Class.hs−282 src/Reflex/PerformEvent/Base.hs−168 src/Reflex/PerformEvent/Class.hs−46 src/Reflex/PostBuild/Class.hs−91 src/Reflex/Pure.hs−167 src/Reflex/Spider.hs−17 src/Reflex/Spider/Internal.hs−2289 stylize+4 test/Reflex/Bench/Focused.hs−369 test/Reflex/Plan/Pure.hs−70 test/Reflex/Plan/Reflex.hs−135 test/Reflex/Test.hs−66 test/Reflex/Test/CrossImpl.hs−244 test/Reflex/Test/Micro.hs−263 test/Reflex/TestPlan.hs−52 test/hlint.hs+34−11 test/semantics.hs−49 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+5
@@ -1,4 +1,7 @@
11dist
2+cabal.sandbox.config
3+.cabal-sandbox/
4+dist-*
25cabal-dev
36*.o
47*.hi
@@ -31,3 +34,5 @@ hsenv.log
3134.#*
3235/shell.nix
3336/ghci-tmp
37+*.dump-*
38+*.verbose-core2core
.stylish-haskell.yaml+2−3
@@ -149,6 +149,5 @@ columns: 110
149149# needs to be aware of these, so it can parse the file correctly.
150150#
151151# No language extensions are enabled by default.
152−# language_extensions:
153− # - TemplateHaskell
154− # - QuasiQuotes
152+language_extensions:
153+ - TemplateHaskell
.travis.ymldeleted−26
@@ -1,26 +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− - travis_wait 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− - env: CABALVER=1.24 GHCVER=8.0.1
25− addons: {apt: {packages: [cabal-install-1.24, ghc-8.0.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
26− 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−176
@@ -1,176 +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−[ ] tagPromptlyDyn :: Dynamic a -> Event b -> Event a
43−[ ] attach :: Behavior a -> Event b -> Event (a, b)
44−[ ] attachPromptlyDyn :: Dynamic a -> Event b -> Event (a, b)
45−[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46−[ ] attachPromptlyDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47−[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48−[ ] attachPromptlyDynWithMaybe :: (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−[ ] ffor :: Dynamic a -> (a -> b) -> Dynamic b
121−[ ] fmap :: (a -> b) -> Dynamic a -> Dynamic b
122−[ ] splitDynPure :: Dynamic (a, b) -> (Dynamic a, Dynamic b)
123−
124−-- Combine multiple Dynamics
125−[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
126−[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
127−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
128−
129−-- Efficient one-to-many fanout
130−[ ] demux :: Ord k => Dynamic k -> Demux k
131−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
132−
133−-- Dynamic to Dynamic, removing updates w/o value change
134−[ ] uniqDyn :: Eq a => Dynamic a -> Dynamic a
135−
136−-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
137−[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
138−[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
139−```
140−
141−## Flattening functions
142−
143−These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
144−
145−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.
146−
147−```haskell
148−-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
149−[ ] switch :: Behavior (Event a) -> Event a
150−
151−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
152−[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
153−
154−-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
155−[ ] coincidence :: Event (Event a) -> Event a
156−
157−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
158−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
159−-- a Behavior whose value is equal to the current value of the current value of
160−-- the input.
161−[ ] join :: Behavior (Behavior a) -> Behavior a
162−
163−
164−-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
165−-- Output updated whenever inner OR outer Dynamic updates.
166−[ ] join :: Dynamic (Dynamic a) -> Dynamic a
167−[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
168−
169−-- Analogous to 'hold': Create a Behavior that is initially identical to the
170−-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
171−[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
172−
173−-- Similar to above, for Events. Created Event initially tracks the first argument.
174−-- At switchover, the output Event immediately tracks the new Event.
175−[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
176−```
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 BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE TupleSections #-}
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 Control.Concurrent.STM
13−import Control.DeepSeq
14−import Control.Exception (evaluate)
15−import Control.Monad.Identity
16−import Control.Monad.IO.Class
17−import Criterion.Main
18−import Data.Dependent.Map (DMap)
19−import qualified Data.Dependent.Map as DMap
20−import Data.Dependent.Sum
21−import Data.Functor.Misc
22−import Data.IORef
23−import Reflex
24−import Reflex.Host.Class
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−157
@@ -1,157 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE ForeignFunctionInterface #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TemplateHaskell #-}
13−{-# LANGUAGE TupleSections #-}
14−{-# LANGUAGE TypeSynonymInstances #-}
15−{-# LANGUAGE ViewPatterns #-}
16−{-# OPTIONS_GHC -fno-warn-orphans #-}
17−
18−module Main where
19−
20−import Criterion.Main
21−import Criterion.Types
22−
23−import Reflex
24−import Reflex.Host.Class
25−
26−import Reflex.Plan.Reflex
27−import Reflex.TestPlan
28−
29−import qualified Reflex.Bench.Focused as Focused
30−import Reflex.Spider.Internal (SpiderEventHandle)
31−
32−import Control.Applicative
33−import Control.DeepSeq (NFData (..))
34−
35−import Prelude
36−import System.IO
37−import System.Mem
38−
39−import Control.Arrow
40−import Control.Concurrent
41−import Control.Concurrent.STM
42−import Control.Exception
43−import Control.Monad
44−import Control.Monad.Trans
45−import Data.Bool
46−import Data.Function
47−import Data.Int
48−import Data.IORef
49−import Data.Monoid
50−import Data.Time.Clock
51−import Debug.Trace.LocationTH
52−import GHC.Stats
53−import System.Environment
54−import System.Mem.Weak
55−import System.Process
56−import Text.Read
57−
58−import Unsafe.Coerce
59−
60−import Data.Map (Map)
61−import qualified Data.Map as Map
62−
63−type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
64−
65−
66−setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (EventHandle t a, Schedule t)
67−setupFiring p = do
68− (e, s) <- runPlan p
69− h <- subscribeEvent e
70− return (h, s)
71−
72−-- Hack to avoid the NFData constraint for EventHandle which is a synonym
73−newtype Ignore a = Ignore a
74−instance NFData (Ignore a) where
75− rnf !_ = ()
76−
77−instance NFData (SpiderEventHandle a) where
78− rnf !_ = ()
79−
80−instance NFData (Behavior t a) where
81− rnf !_ = ()
82−
83−instance NFData (Firing t) where
84− rnf !_ = ()
85−
86−-- Measure the running time
87−benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
88−benchFiring runHost tc n = runHost $ do
89− let runIterations :: m a -> m ()
90− runIterations test = replicateM_ (10*n) $ do
91− result <- test
92− liftIO $ evaluate result
93− case tc of
94− TestE p -> do
95− (h, s) <- setupFiring p
96− runIterations $ readSchedule_ s $ readEvent' h
97− TestB p -> do
98− (b, s) <- runPlan p
99− runIterations $ readSchedule_ (makeDense s) $ sample b
100−
101−waitForFinalizers :: IO ()
102−waitForFinalizers = do
103− performGC
104− x <- getCurrentTime
105− isFinalized <- newIORef False
106− mkWeakPtr x $ Just $ writeIORef isFinalized True
107− performGC
108− fix $ \loop -> do
109− f <- readIORef isFinalized
110− unless f $ do
111− threadDelay 1
112− loop
113−
114−benchmarks :: [(String, Int -> IO ())]
115−benchmarks = implGroup "spider" runSpiderHost cases
116− where
117− implGroup :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> [(String, TestCase)] -> [(String, Int -> IO ())]
118− implGroup name runHost = group name . fmap (second (benchFiring runHost))
119− group name = fmap $ first ((name <> "/") <>)
120− sub n frames = group ("subscribing " ++ show (n, frames)) $ Focused.subscribing n frames
121− firing n = group ("firing " <> show n) $ Focused.firing n
122− merging n = group ("merging " <> show n) $ Focused.merging n
123− dynamics n = group ("dynamics " <> show n) $ Focused.dynamics n
124− cases = concat
125− [ sub 100 40
126− , dynamics 100
127− , dynamics 1000
128− , firing 1000
129− , firing 10000
130− , merging 10
131− , merging 50
132− , merging 100
133− , merging 200
134− ]
135−
136−pattern RunTestCaseFlag = "--run-test-case"
137−
138−spawnBenchmark :: String -> Benchmark
139−spawnBenchmark name = Benchmark name $ Benchmarkable $ \n -> do
140− self <- getExecutablePath
141− callProcess self [RunTestCaseFlag, name, show n, "+RTS", "-N1"]
142−
143−foreign import ccall unsafe "myCapabilityHasOtherRunnableThreads" myCapabilityHasOtherRunnableThreads :: IO Bool
144−
145−main :: IO ()
146−main = do
147− args <- getArgs
148− case args of
149− RunTestCaseFlag : t -> case t of
150− [name, readMaybe -> Just count] -> do
151− case lookup name benchmarks of
152− Just testCase -> testCase count
153− performGC
154− fix $ \loop -> bool (return ()) (yield >> loop) =<< myCapabilityHasOtherRunnableThreads
155− return ()
156− _ -> $failure "--run-test-case: expected test name and iteration count to follow"
157− _ -> 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−26
@@ -1,26 +0,0 @@
1−{ mkDerivation, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl , primitive, ref-tf, semigroupoids
5−, semigroups, split, stdenv, stm , syb, template-haskell
6−, these, transformers, transformers-compat
7−}:
8−mkDerivation {
9− pname = "reflex";
10− version = "0.4.0";
11− src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
12− libraryHaskellDepends = [
13− base bifunctors containers dependent-map dependent-sum
14− exception-transformers haskell-src-exts haskell-src-meta lens
15− MemoTrie monad-control mtl primitive ref-tf semigroupoids
16− semigroups stm syb template-haskell these transformers
17− transformers-compat
18− ];
19− testHaskellDepends = [
20− base bifunctors containers deepseq dependent-map dependent-sum
21− hlint mtl ref-tf split transformers
22− ];
23− homepage = "https://github.com/reflex-frp/reflex";
24− description = "Higher-order Functional Reactive Programming";
25− license = stdenv.lib.licenses.bsd3;
26−}
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−134
@@ -1,134 +0,0 @@
1−Name: reflex
2−Version: 0.5.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− MemoTrie == 0.6.*,
20− base >= 4.7 && < 4.10,
21− bifunctors >= 5.2 && < 5.5,
22− containers == 0.5.*,
23− dependent-map >= 0.2.2 && < 0.3,
24− dependent-sum == 0.3.*,
25− exception-transformers == 0.4.*,
26− haskell-src-exts >= 1.16 && < 1.18,
27− haskell-src-meta == 0.6.*,
28− lens >= 4.7 && < 5,
29− monad-control >= 1.0.1 && < 1.1,
30− mtl >= 2.1 && < 2.3,
31− primitive >= 0.5 && < 0.7,
32− ref-tf == 0.4.*,
33− semigroupoids >= 4.0 && < 6,
34− semigroups >= 0.16 && < 0.19,
35− stm == 2.4.*,
36− syb >= 0.5 && < 0.7,
37− template-haskell >= 2.9 && < 2.12,
38− these >= 0.4 && < 0.8,
39− transformers >= 0.2,
40− transformers-compat >= 0.3
41−
42− exposed-modules:
43− Data.Functor.Misc,
44− Data.WeakBag,
45− Reflex,
46− Reflex.Class,
47− Reflex.Deletable.Class,
48− Reflex.Dynamic,
49− Reflex.Dynamic.TH,
50− Reflex.Dynamic.Uniq,
51− Reflex.DynamicWriter,
52− Reflex.Host.Class,
53− Reflex.PerformEvent.Base,
54− Reflex.PerformEvent.Class,
55− Reflex.PostBuild.Class,
56− Reflex.Pure,
57− Reflex.Spider,
58− Reflex.Spider.Internal
59−
60− other-extensions: TemplateHaskell
61− ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
62− ghc-prof-options: -auto-all
63−
64−test-suite semantics
65− type: exitcode-stdio-1.0
66− main-is: semantics.hs
67− hs-source-dirs: test
68− ghc-options: -O2 -Wall -rtsopts
69− build-depends:
70− base,
71− bifunctors,
72− containers,
73− deepseq >= 1.3 && < 1.5,
74− dependent-map,
75− dependent-sum,
76− mtl,
77− ref-tf,
78− reflex,
79− split,
80− transformers >= 0.3
81−
82−test-suite hlint
83− type: exitcode-stdio-1.0
84− main-is: hlint.hs
85− hs-source-dirs: test
86− build-depends: base, hlint == 1.9.*
87− if impl(ghcjs)
88− buildable: False
89−
90−benchmark spider-bench
91− type: exitcode-stdio-1.0
92− hs-source-dirs: bench
93− main-is: Main.hs
94− ghc-options: -Wall -O2 -rtsopts
95− build-depends:
96− base,
97− criterion == 1.1.*,
98− deepseq >= 1.3 && < 1.5,
99− dependent-map,
100− dependent-sum,
101− mtl,
102− primitive,
103− reflex,
104− stm,
105− transformers >= 0.3
106−
107−benchmark saulzar-bench
108− type: exitcode-stdio-1.0
109− hs-source-dirs: bench test
110− c-sources: bench-cbits/checkCapability.c
111− main-is: RunAll.hs
112− ghc-options: -Wall -O2 -rtsopts -threaded
113− ghc-prof-options: -auto-all
114− build-depends:
115− base,
116− containers == 0.5.*,
117− criterion == 1.1.*,
118− deepseq >= 1.3 && < 1.5,
119− dependent-map,
120− dependent-sum,
121− loch-th,
122− mtl,
123− primitive,
124− process,
125− ref-tf == 0.4,
126− reflex,
127− split,
128− stm,
129− time,
130− transformers >= 0.3
131−
132−source-repository head
133− type: git
134− 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+119−57
@@ -1,8 +1,10 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveFunctor #-}
24{-# LANGUAGE FlexibleContexts #-}
35{-# LANGUAGE FlexibleInstances #-}
46{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE KindSignatures #-}
7+{-# LANGUAGE LambdaCase #-}
68{-# LANGUAGE MultiParamTypeClasses #-}
79{-# LANGUAGE PolyKinds #-}
810{-# LANGUAGE RankNTypes #-}
@@ -12,34 +14,42 @@
1214-- which are relevant to the use of 'Functor'-based datastructures like
1315-- 'Data.Dependent.Map.DMap'.
1416module Data.Functor.Misc
15− ( -- * Const2
16− Const2 (..)
17− , dmapToMap
18− , mapToDMap
19− -- * WrapArg
20− , WrapArg (..)
21− -- * Convenience functions for DMap
22− , mapWithFunctorToDMap
23− , combineDMapsWithKey
24− -- * Deprecated functions
25− , sequenceDmap
26− , wrapDMap
27− , rewrapDMap
28− , unwrapDMap
29− , unwrapDMapMaybe
30− , extractFunctorDMap
31− ) where
32−
33−import Control.Applicative (Applicative, (<$>))
34−import Control.Monad.Identity
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 ((<$>))
3540import Data.Dependent.Map (DMap)
3641import qualified Data.Dependent.Map as DMap
3742import Data.Dependent.Sum
43+import Data.Functor.Identity
3844import Data.GADT.Compare
3945import Data.GADT.Show
46+import Data.IntMap (IntMap)
47+import qualified Data.IntMap as IntMap
4048import Data.Map (Map)
4149import qualified Data.Map as Map
50+import Data.Some (Some, mkSome)
4251import Data.These
52+import Data.Type.Equality ((:~:)(Refl))
4353import Data.Typeable hiding (Refl)
4454
4555--------------------------------------------------------------------------------
@@ -52,14 +62,18 @@ data Const2 :: * -> x -> x -> * where
5262 Const2 :: k -> Const2 k v v
5363 deriving (Typeable)
5464
55−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)
5673
5774instance Show k => GShow (Const2 k v) where
5875 gshowsPrec n x@(Const2 _) = showsPrec n x
5976
60−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
61− showTaggedPrec (Const2 _) = showsPrec
62−
6377instance Eq k => GEq (Const2 k v) where
6478 geq (Const2 a) (Const2 b) =
6579 if a == b
@@ -76,6 +90,15 @@ instance Ord k => GCompare (Const2 k v) where
7690dmapToMap :: DMap (Const2 k v) Identity -> Map k v
7791dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
7892
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+
79102-- | Convert a regular 'Map' to a 'DMap'
80103mapToDMap :: Map k v -> DMap (Const2 k v) Identity
81104mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
@@ -85,6 +108,16 @@ mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v)
85108mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
86109mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
87110
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+
88121--------------------------------------------------------------------------------
89122-- WrapArg
90123--------------------------------------------------------------------------------
@@ -95,6 +128,11 @@ mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v
95128data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
96129 WrapArg :: f a -> WrapArg g f (g a)
97130
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+
98136instance GEq f => GEq (WrapArg g f) where
99137 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
100138
@@ -132,38 +170,62 @@ combineDMapsWithKey f mg mh = DMap.fromList $ go (DMap.toList mg) (DMap.toList m
132170 GEQ -> (gk :=> f gk (These gv hv)) : go gs' hs'
133171 GGT -> (hk :=> f hk (That hv)) : go gs hs'
134172
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)
186+
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+
135221--------------------------------------------------------------------------------
136−-- Deprecated functions
222+-- ComposeMaybe
137223--------------------------------------------------------------------------------
138224
139−{-# INLINE sequenceDmap #-}
140−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
141−-- | Run the actions contained in the 'DMap'
142−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
143−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
144−
145−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
146−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
147−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
148−wrapDMap f = DMap.map $ f . runIdentity
149−
150−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
151−-- | Replace one functor for a 'DMap''s values with a different functor
152−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
153−rewrapDMap = DMap.map
154−
155−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
156−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
157−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
158−unwrapDMap f = DMap.map $ Identity . f
159−
160−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
161−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
162−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
163−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
164−
165−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
166−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
167−-- underlying functor
168−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
169−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
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−120
@@ -1,120 +0,0 @@
1−-- | This module defines the 'WeakBag' type, which represents a mutable
2−-- collection of items that does not cause the items to be retained in memory.
3−-- This is useful for situations where a value needs to be inspected or modified
4−-- if it is still alive, but can be ignored if it is dead.
5−module Data.WeakBag
6− ( WeakBag
7− , WeakBagTicket
8− , empty
9− , singleton
10− , insert
11− , traverse
12− , remove
13− ) where
14−
15−import Control.Concurrent.STM
16−import Control.Exception
17−import Control.Monad hiding (forM_, mapM_)
18−import Control.Monad.IO.Class
19−import Data.Foldable (forM_, mapM_)
20−import Data.IntMap.Strict (IntMap)
21−import qualified Data.IntMap.Strict as IntMap
22−import Data.IORef
23−import System.Mem.Weak
24−
25−import Prelude hiding (mapM_, traverse)
26−
27−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
28−-- that is, they can still be garbage-collected. As long as the @a@s remain
29−-- alive, the 'WeakBag' will continue to refer to them.
30−data WeakBag a = WeakBag
31− { _weakBag_nextId :: {-# UNPACK #-} !(TVar Int) --TODO: what if this wraps around?
32− , _weakBag_children :: {-# UNPACK #-} !(TVar (IntMap (Weak a)))
33− }
34−
35−-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
36−-- the caller retains the ticket, the item is guranteed to stay in memory (and
37−-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
38−-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
39−data WeakBagTicket a = WeakBagTicket
40− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
41− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
42− }
43−
44−-- | Insert an item into a 'WeakBag'.
45−{-# INLINE insert #-}
46−insert :: a -- ^ The item
47− -> WeakBag a -- ^ The 'WeakBag' to insert into
48− -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
49− -- callback
50− -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
51− -- (whether automatically by the item being garbage
52− -- collected or manually via 'remove')
53− -> IO (WeakBagTicket a) -- ^ Returns a 'WeakBagTicket' that ensures the
54− -- item is retained and allows the item to be
55− -- removed.
56−insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
57− a' <- evaluate a
58− wbRef' <- evaluate wbRef
59− myId <- atomically $ do
60− myId <- readTVar nextId
61− writeTVar nextId $! succ myId
62− return myId
63− let cleanup = do
64− wb <- readIORef wbRef'
65− mb <- deRefWeak wb
66− forM_ mb $ \b -> do
67− isLastNode <- atomically $ do --TODO: Should this run even when mb is Nothing?
68− cs <- readTVar children
69− let csWithoutMe = IntMap.delete myId cs
70− writeTVar children $! csWithoutMe
71− return $ IntMap.size csWithoutMe == 0
72− when isLastNode $ finalizer b
73− return ()
74− return ()
75− wa <- mkWeakPtr a' $ Just cleanup
76− atomically $ modifyTVar' children $ IntMap.insert myId wa
77− return $ WeakBagTicket
78− { _weakBagTicket_weakItem = wa
79− , _weakBagTicket_item = a'
80− }
81−
82−-- | Create an empty 'WeakBag'.
83−{-# INLINE empty #-}
84−empty :: IO (WeakBag a)
85−empty = {-# SCC "empty" #-} do
86− nextId <- newTVarIO 1
87− children <- newTVarIO IntMap.empty
88− let bag = WeakBag
89− { _weakBag_nextId = nextId
90− , _weakBag_children = children
91− }
92− return bag
93−
94−-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
95−-- 'empty', then using 'insert'.
96−{-# INLINE singleton #-}
97−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket a)
98−singleton a wbRef finalizer = {-# SCC "singleton" #-} do
99− bag <- empty
100− ticket <- insert a bag wbRef finalizer
101− return (bag, ticket)
102−
103−{-# INLINE traverse #-}
104−-- | Visit every node in the given list. If new nodes are appended during the
105−-- traversal, they will not be visited. Every live node that was in the list
106−-- when the traversal began will be visited exactly once; however, no guarantee
107−-- is made about the order of the traversal.
108−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
109−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
110− cs <- liftIO $ readTVarIO children
111− forM_ cs $ \c -> do
112− ma <- liftIO $ deRefWeak c
113− mapM_ f ma
114−
115−-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
116−-- on the same 'WeakBagTicket'.
117−{-# INLINE remove #-}
118−remove :: WeakBagTicket a -> IO ()
119−remove = {-# SCC "remove" #-} finalize . _weakBagTicket_weakItem
120−--TODO: Should 'remove' also drop the reference to the item?
src/Reflex.hsdeleted−12
@@ -1,12 +0,0 @@
1−-- | This module exports all of the commonly-used functionality of Reflex; if
2−-- you are just getting started with Reflex, this is probably what you want.
3−module Reflex (module X) where
4−
5−import Reflex.Class as X
6−import Reflex.Dynamic as X
7−import Reflex.Dynamic.TH as X
8−import Reflex.Dynamic.Uniq as X
9−import Reflex.DynamicWriter as X
10−import Reflex.PerformEvent.Class as X
11−import Reflex.PostBuild.Class as X
12−import Reflex.Spider as X
src/Reflex/Class.hsdeleted−814
@@ -1,814 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE RecursiveDo #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−
16−-- | This module contains the Reflex interface, as well as a variety of
17−-- convenience functions for working with 'Event's, 'Behavior's, and other
18−-- signals.
19−module Reflex.Class
20− ( -- * Primitives
21− Reflex (..)
22− , MonadSample (..)
23− , MonadHold (..)
24− -- ** 'fan'-related types
25− , EventSelector (..)
26− -- ** 'Incremental'-related types
27− , Patch (..)
28− , PatchDMap (..)
29− -- * Convenience functions
30− , constDyn
31− , pushAlways
32− -- ** Combining 'Event's
33− , leftmost
34− , mergeMap
35− , mergeList
36− , mergeWith
37− -- ** Breaking up 'Event's
38− , splitE
39− , fanEither
40− , fanThese
41− , fanMap
42− , dmapToThese
43− , EitherTag (..)
44− , eitherToDSum
45− , dsumToEither
46− -- ** Switching between 'Event's
47− , switchPromptly
48− -- ** Using 'Event's to sample 'Behavior's
49− , tag
50− , attach
51− , attachWith
52− , attachWithMaybe
53− -- ** Blocking an 'Event' based on a 'Behavior'
54− , gate
55− -- ** Combining 'Dynamic's
56− , distributeDMapOverDynPure
57− , distributeListOverDyn
58− , distributeListOverDynWith
59− , zipDyn
60− , zipDynWith
61− -- ** Accumulating state
62− , Accumulator (..)
63− , mapAccum_
64− , mapAccumM_
65− , mapAccumMaybe_
66− , mapAccumMaybeM_
67− , zipListWithEvent
68− , headE
69− , tailE
70− , headTailE
71− , switcher
72− -- * Debugging functions
73− , traceEvent
74− , traceEventWith
75− -- * Unsafe functions
76− , unsafeDynamic
77− -- * 'FunctorMaybe'
78− , FunctorMaybe (..)
79− , fforMaybe
80− , ffilter
81− -- * Miscellaneous convenience functions
82− , ffor
83− -- * Deprecated functions
84− , appendEvents
85− , onceE
86− , sequenceThese
87− ) where
88−
89−import Control.Applicative
90−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
91−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
92−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
93−import Control.Monad.Trans.Cont (ContT ())
94−import Control.Monad.Trans.Except (ExceptT ())
95−import Control.Monad.Trans.RWS (RWST ())
96−import Control.Monad.Trans.Writer (WriterT ())
97−import Data.Align
98−import Data.Bifunctor
99−import Data.Dependent.Map (DMap, DSum (..), GCompare (..), GOrdering (..))
100−import qualified Data.Dependent.Map as DMap
101−import Data.Dependent.Sum (ShowTag (..))
102−import Data.Either
103−import Data.Foldable
104−import Data.Functor.Bind hiding (join)
105−import qualified Data.Functor.Bind as Bind
106−import Data.Functor.Compose
107−import Data.Functor.Constant
108−import Data.Functor.Misc
109−import Data.Functor.Plus
110−import Data.GADT.Compare ((:~:) (..), GEq (..))
111−import Data.GADT.Show (GShow (..))
112−import Data.List.NonEmpty (NonEmpty (..))
113−import Data.Map (Map)
114−import Data.Maybe
115−import Data.Monoid hiding (Alt, (<>))
116−import Data.Semigroup (Semigroup, sconcat, stimes, stimesIdempotentMonoid, (<>))
117−import Data.String
118−import Data.These
119−import Data.Traversable
120−
121−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
122−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
123−
124−import Debug.Trace (trace)
125−
126−-- | The 'Reflex' class contains all the primitive functionality needed for
127−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
128−-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
129−-- and the type of the timeline determines the FRP engine to be used. For most
130−-- purposes, the 'Reflex.Spider' implementation is recommended.
131−class ( MonadHold t (PushM t)
132− , MonadSample t (PullM t)
133− , MonadFix (PushM t)
134− , Functor (Event t)
135− , Functor (Behavior t)
136− , Functor (Dynamic t)
137− , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
138− , Monad (Dynamic t)
139− ) => Reflex t where
140− -- | A container for a value that can change over time. 'Behavior's can be
141− -- sampled at will, but it is not possible to be notified when they change
142− data Behavior t :: * -> *
143− -- | A stream of occurrences. During any given frame, an 'Event' is either
144− -- occurring or not occurring; if it is occurring, it will contain a value of
145− -- the given type (its "occurrence type")
146− data Event t :: * -> *
147− -- | A container for a value that can change over time and allows
148− -- notifications on changes. Basically a combination of a 'Behavior' and an
149− -- 'Event', with a rule that the 'Behavior' will change if and only if the
150− -- 'Event' fires.
151− data Dynamic t :: * -> *
152− -- | A container for a value that can change over time and allows
153− -- notifications on changes. Basically a combination of a 'Behavior' and an
154− -- 'Event', with a rule that the 'Behavior' will change if and only if the
155− -- 'Event' fires.
156− data Incremental t :: (* -> *) -> * -> *
157− -- | An 'Event' with no occurrences
158− never :: Event t a
159− -- | Create a 'Behavior' that always has the given value
160− 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 'Behavior's doesn't have a negative performance impact
161− -- | Create an 'Event' from another 'Event'; the provided function can sample
162− -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
163− -- (Just) or non-occurring (Nothing) result
164− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
165− -- | A monad for doing complex push-based calculations efficiently
166− type PushM t :: * -> *
167− -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
168− -- recomputed whenever any of the read 'Behavior's changes
169− pull :: PullM t a -> Behavior t a
170− -- | A monad for doing complex pull-based calculations efficiently
171− type PullM t :: * -> *
172− -- | Merge a collection of events; the resulting 'Event' will only occur if at
173− -- least one input event is occuring, and will contain all of the input keys
174− -- that are occurring simultaneously
175− 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
176− -- | Efficiently fan-out an event to many destinations. This function should
177− -- be partially applied, and then the result applied repeatedly to create
178− -- child events
179− 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
180− -- | Create an 'Event' that will occur whenever the currently-selected input
181− -- 'Event' occurs
182− switch :: Behavior t (Event t a) -> Event t a
183− -- | Create an 'Event' that will occur whenever the input event is occurring
184− -- and its occurrence value, another 'Event', is also occurring
185− coincidence :: Event t (Event t a) -> Event t a
186− -- | Extract the 'Behavior' of a 'Dynamic'.
187− current :: Dynamic t a -> Behavior t a
188− -- | Extract the 'Event' of the 'Dynamic'.
189− updated :: Dynamic t a -> Event t a
190− -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
191− -- recent firing of the given 'Event', if any.
192− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
193− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
194− -- in the same way that the accumulated application of patches would change
195− -- that value.
196− unsafeBuildIncremental :: Patch p => PullM t a -> Event t (p a) -> Incremental t p a
197− -- | Create a merge whose parents can change over time
198− mergeIncremental :: GCompare k => Incremental t PatchDMap (DMap k (Event t)) -> Event t (DMap k Identity)
199− -- | Extract the 'Behavior' component of an 'Incremental'
200− currentIncremental :: Patch p => Incremental t p a -> Behavior t a
201− -- | Extract the 'Event' component of an 'Incremental'
202− updatedIncremental :: Patch p => Incremental t p a -> Event t (p a)
203− -- | Convert an 'Incremental' to a 'Dynamic'
204− incrementalToDynamic :: Patch p => Incremental t p a -> Dynamic t a
205−
206−-- | A 'Patch' type represents a kind of change made to a datastructure.
207−class Patch p where
208− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
209− -- 'Nothing'.
210− apply :: p a -> a -> Maybe a
211−
212−instance Patch Identity where
213− apply (Identity a) _ = Just a
214−
215−-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
216−data PatchDMap a where
217− PatchDMap :: GCompare k => DMap k (Compose Maybe v) -> PatchDMap (DMap k v)
218−
219−instance GCompare k => Semigroup (PatchDMap (DMap k v)) where
220− PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
221− -- PatchDMap is idempotent, so stimes n is id for every n
222−#if MIN_VERSION_semigroups(0,17,0)
223− stimes = stimesIdempotentMonoid
224−#else
225− times1p n x = case compare n 0 of
226− LT -> error "stimesIdempotentMonoid: negative multiplier"
227− EQ -> mempty
228− GT -> x
229−#endif
230−
231−instance GCompare k => Monoid (PatchDMap (DMap k v)) where
232− mempty = PatchDMap mempty
233− mappend = (<>)
234−
235−instance Patch PatchDMap where
236− 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?
237− where insertions = DMap.mapMaybeWithKey (const $ getCompose) diff
238− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getCompose) diff
239− nothingToJust = \case
240− Nothing -> Just $ Constant ()
241− Just _ -> Nothing
242−
243−-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
244−-- _must_ change when and only when the 'Event' fires, such that the
245−-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
246−-- if this is not the case, the resulting 'Dynamic' will behave
247−-- nondeterministically.
248−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
249−unsafeDynamic = unsafeBuildDynamic . sample
250−
251−-- | Construct a 'Dynamic' value that never changes
252−constDyn :: Reflex t => a -> Dynamic t a
253−constDyn = pure
254−
255−-- | 'MonadSample' designates monads that can read the current value of a
256−-- 'Behavior'. This includes both 'PullM' and 'PushM'.
257−class (Applicative m, Monad m) => MonadSample t m | m -> t where
258− -- | Get the current value in the 'Behavior'
259− sample :: Behavior t a -> m a
260−
261−-- | 'MonadHold' designates monads that can create new 'Behavior's based on
262−-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
263−-- is required to create any stateful computations with Reflex.
264−class MonadSample t m => MonadHold t m where
265− -- | Create a new 'Behavior' whose value will initially be equal to the given
266− -- value and will be updated whenever the given 'Event' occurs. The update
267− -- takes effect immediately after the 'Event' occurs; if the occurrence that
268− -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
269− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
270− -- one.
271− hold :: a -> Event t a -> m (Behavior t a)
272− -- | Create a 'Dynamic' value using the given initial value that changes every
273− -- time the 'Event' occurs.
274− holdDyn :: a -> Event t a -> m (Dynamic t a)
275− -- | Create an 'Incremental' value using the given initial value that changes
276− -- every time the 'Event' occurs.
277− holdIncremental :: Patch p => a -> Event t (p a) -> m (Incremental t p a)
278−
279−-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
280−-- key. This is much more efficient than filtering for each key with
281−-- 'fmapMaybe'.
282−newtype EventSelector t k = EventSelector
283− { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
284− -- determined by the type of the key, so this can be used to fan-out
285− -- 'Event's whose sub-'Event's have different types.
286− --
287− -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
288− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
289− -- occurrences of an 'Event'.
290− select :: forall a. k a -> Event t a
291− }
292−
293−--------------------------------------------------------------------------------
294−-- Instances
295−--------------------------------------------------------------------------------
296−
297−instance MonadSample t m => MonadSample t (ReaderT r m) where
298− sample = lift . sample
299−
300−instance MonadHold t m => MonadHold t (ReaderT r m) where
301− hold a0 = lift . hold a0
302− holdDyn a0 = lift . holdDyn a0
303− holdIncremental a0 = lift . holdIncremental a0
304−
305−instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
306− sample = lift . sample
307−
308−instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
309− hold a0 = lift . hold a0
310− holdDyn a0 = lift . holdDyn a0
311− holdIncremental a0 = lift . holdIncremental a0
312−
313−instance MonadSample t m => MonadSample t (StateT s m) where
314− sample = lift . sample
315−
316−instance MonadHold t m => MonadHold t (StateT s m) where
317− hold a0 = lift . hold a0
318− holdDyn a0 = lift . holdDyn a0
319− holdIncremental a0 = lift . holdIncremental a0
320−
321−instance MonadSample t m => MonadSample t (ExceptT e m) where
322− sample = lift . sample
323−
324−instance MonadHold t m => MonadHold t (ExceptT e m) where
325− hold a0 = lift . hold a0
326− holdDyn a0 = lift . holdDyn a0
327− holdIncremental a0 = lift . holdIncremental a0
328−
329−instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
330− sample = lift . sample
331−
332−instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
333− hold a0 = lift . hold a0
334− holdDyn a0 = lift . holdDyn a0
335− holdIncremental a0 = lift . holdIncremental a0
336−
337−instance MonadSample t m => MonadSample t (ContT r m) where
338− sample = lift . sample
339−
340−instance MonadHold t m => MonadHold t (ContT r m) where
341− hold a0 = lift . hold a0
342− holdDyn a0 = lift . holdDyn a0
343− holdIncremental a0 = lift . holdIncremental a0
344−
345−--------------------------------------------------------------------------------
346−-- Convenience functions
347−--------------------------------------------------------------------------------
348−
349−-- | Create an 'Event' from another 'Event'. The provided function can sample
350−-- 'Behavior's and hold 'Event's.
351−pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
352−pushAlways f = push (fmap Just . f)
353−
354−-- | Flipped version of 'fmap'.
355−ffor :: Functor f => f a -> (a -> b) -> f b
356−ffor = flip fmap
357−
358−instance Reflex t => Applicative (Behavior t) where
359− pure = constant
360− f <*> x = pull $ sample f `ap` sample x
361− _ *> b = b
362− a <* _ = a
363−
364−instance Reflex t => Apply (Behavior t) where
365− (<.>) = (<*>)
366−
367−instance Reflex t => Bind (Behavior t) where
368− (>>-) = (>>=)
369−
370−instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
371− (/) = liftA2 (/)
372− fromRational = pure . fromRational
373− recip = fmap recip
374−
375−instance Reflex t => Functor (Behavior t) where
376− fmap f = pull . fmap f . sample
377−
378−instance (Reflex t, IsString a) => IsString (Behavior t a) where
379− fromString = pure . fromString
380−
381−instance Reflex t => Monad (Behavior t) where
382− a >>= f = pull $ sample a >>= sample . f
383− -- 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 'Behavior's other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
384− return = constant
385− fail = error "Monad (Behavior t) does not support fail"
386−
387−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
388− mempty = constant mempty
389− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
390− mconcat = pull . fmap mconcat . mapM sample
391−
392−instance (Reflex t, Num a) => Num (Behavior t a) where
393− (+) = liftA2 (+)
394− (-) = liftA2 (-)
395− (*) = liftA2 (*)
396− abs = fmap abs
397− fromInteger = pure . fromInteger
398− negate = fmap negate
399− signum = fmap signum
400−
401−instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
402− a <> b = pull $ liftM2 (<>) (sample a) (sample b)
403− sconcat = pull . fmap sconcat . mapM sample
404−#if MIN_VERSION_semigroups(0,17,0)
405− stimes n = fmap $ stimes n
406−#else
407− times1p n = fmap $ times1p n
408−#endif
409−
410−--TODO: See if there's a better class in the standard libraries already
411−-- | A class for values that combines filtering and mapping using 'Maybe'.
412−class FunctorMaybe f where
413− -- | Combined mapping and filtering function.
414− fmapMaybe :: (a -> Maybe b) -> f a -> f b
415−
416−instance FunctorMaybe [] where
417− fmapMaybe f = catMaybes . fmap f
418−
419−-- | Flipped version of 'fmapMaybe'.
420−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
421−fforMaybe = flip fmapMaybe
422−
423−-- | Filter 'f a' using the provided predicate.
424−-- Relies on 'fforMaybe'.
425−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
426−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
427−
428−-- | Left-biased event union (prefers left event on simultaneous
429−-- occurrence).
430−instance Reflex t => Alt (Event t) where
431− ev1 <!> ev2 = leftmost [ev1, ev2]
432−
433−-- | 'Event' intersection (convenient interface to 'coincidence').
434−instance Reflex t => Apply (Event t) where
435− evf <.> evx = coincidence (fmap (<$> evx) evf)
436−
437−-- | 'Event' intersection (convenient interface to 'coincidence').
438−instance Reflex t => Bind (Event t) where
439− evx >>- f = coincidence (f <$> evx)
440− join = coincidence
441−
442−instance Reflex t => Functor (Event t) where
443− fmap f = fmapMaybe $ Just . f
444−
445−instance Reflex t => FunctorMaybe (Event t) where
446− fmapMaybe f = push $ return . f
447−
448−-- | Never: @'zero' = 'never'@.
449−instance Reflex t => Plus (Event t) where
450− zero = never
451−
452−-- | Replace each occurrence value of the 'Event' with the value of the
453−-- 'Behavior' at the time of that occurrence.
454−tag :: Reflex t => Behavior t b -> Event t a -> Event t b
455−tag b = pushAlways $ \_ -> sample b
456−
457−-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
458−-- current value of the 'Behavior'.
459−attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
460−attach = attachWith (,)
461−
462−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
463−-- combining it with the current value of the 'Behavior'.
464−attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
465−attachWith f = attachWithMaybe $ \a b -> Just $ f a b
466−
467−-- | Create a new 'Event' by combining each occurence with the current value of
468−-- the 'Behavior'. The occurrence is discarded if the combining function returns
469−-- Nothing
470−attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
471−attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
472−
473−-- | Create a new 'Event' that only occurs only once, on the first occurence of
474−-- the supplied 'Event'.
475−headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
476−headE e = do
477− rec be <- hold e $ fmap (const never) e'
478− let e' = switch be
479− e' `seq` return ()
480− return e'
481−
482−-- | Create a new 'Event' that occurs on all but the first occurence of the
483−-- supplied 'Event'.
484−tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
485−tailE e = snd <$> headTailE e
486−
487−-- | Create a tuple of two 'Event's with the first one occuring only the first
488−-- time the supplied 'Event' occurs and the second occuring on all but the first
489−-- occurence.
490−headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
491−headTailE e = do
492− eHead <- headE e
493− be <- hold never $ fmap (const e) eHead
494− return (eHead, switch be)
495−
496−-- | Split the supplied 'Event' into two individual 'Event's occuring at the
497−-- same time with the respective values from the tuple.
498−splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
499−splitE e = (fmap fst e, fmap snd e)
500−
501−-- | Print the supplied 'String' and the value of the 'Event' on each
502−-- occurence. This should /only/ be used for debugging.
503−--
504−-- Note: As with Debug.Trace.trace, the message will only be printed if the
505−-- 'Event' is actually used.
506−traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
507−traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
508−
509−-- | Print the output of the supplied function on each occurence of the
510−-- 'Event'. This should /only/ be used for debugging.
511−--
512−-- Note: As with Debug.Trace.trace, the message will only be printed if the
513−-- 'Event' is actually used.
514−traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
515−traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
516−
517−-- | Tag type for 'Either' to use it as a 'DSum'.
518−data EitherTag l r a where
519− LeftTag :: EitherTag l r l
520− RightTag :: EitherTag l r r
521−
522−instance GEq (EitherTag l r) where
523− geq a b = case (a, b) of
524− (LeftTag, LeftTag) -> Just Refl
525− (RightTag, RightTag) -> Just Refl
526− _ -> Nothing
527−
528−instance GCompare (EitherTag l r) where
529− gcompare a b = case (a, b) of
530− (LeftTag, LeftTag) -> GEQ
531− (LeftTag, RightTag) -> GLT
532− (RightTag, LeftTag) -> GGT
533− (RightTag, RightTag) -> GEQ
534−
535−instance GShow (EitherTag l r) where
536− gshowsPrec _ a = case a of
537− LeftTag -> showString "LeftTag"
538− RightTag -> showString "RightTag"
539−
540−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
541− showTaggedPrec t n (Identity a) = case t of
542− LeftTag -> showsPrec n a
543− RightTag -> showsPrec n a
544−
545−-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
546−eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
547−eitherToDSum = \case
548− Left a -> (LeftTag :=> Identity a)
549− Right b -> (RightTag :=> Identity b)
550−
551−-- | Convert 'DSum' to 'Either'. Inverse of 'eitherToDSum'.
552−dsumToEither :: DSum (EitherTag a b) Identity -> Either a b
553−dsumToEither = \case
554− (LeftTag :=> Identity a) -> Left a
555− (RightTag :=> Identity b) -> Right b
556−
557−-- | Extract the values of a 'DMap' of 'EitherTag's.
558−dmapToThese :: DMap (EitherTag a b) Identity -> Maybe (These a b)
559−dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
560− (Nothing, Nothing) -> Nothing
561− (Just (Identity a), Nothing) -> Just $ This a
562− (Nothing, Just (Identity b)) -> Just $ That b
563− (Just (Identity a), Just (Identity b)) -> Just $ These a b
564−
565−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
566− (<>) = alignWith (mergeThese (<>))
567− sconcat = fmap sconcat . mergeList . toList
568−#if MIN_VERSION_semigroups(0,17,0)
569− stimes n = fmap $ stimes n
570−#else
571− times1p n = fmap $ times1p n
572−#endif
573−
574−instance (Semigroup a, Reflex t) => Monoid (Event t a) where
575− mempty = never
576− mappend = (<>)
577− mconcat = fmap sconcat . mergeList
578−
579−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
580−-- list occurs. If multiple occur at the same time they are folded from the left
581−-- with the given function.
582−mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
583−mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
584− . merge
585− . DMap.fromDistinctAscList
586− . map (\(k, v) -> Const2 k :=> v)
587− $ zip [0 :: Int ..] es
588−
589−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
590−-- list occurs. If multiple occur at the same time the value is the value of the
591−-- leftmost event.
592−leftmost :: Reflex t => [Event t a] -> Event t a
593−leftmost = mergeWith const
594−
595−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
596−-- list occurs and has a list of the values of all 'Event's occuring at that
597−-- time.
598−mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
599−mergeList [] = never
600−mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
601−
602−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
603−-- occurs if at least one of them occurs and has a map of values of all 'Event's
604−-- occuring at that time.
605−mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
606−mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
607−
608−-- | Split the event into separate events for 'Left' and 'Right' values.
609−fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
610−fanEither e =
611− let justLeft = either Just (const Nothing)
612− justRight = either (const Nothing) Just
613− in (fmapMaybe justLeft e, fmapMaybe justRight e)
614−
615−-- | Split the event into separate events for 'This' and 'That' values,
616−-- allowing them to fire simultaneously when the input value is 'These'.
617−fanThese :: Reflex t => Event t (These a b) -> (Event t a, Event t b)
618−fanThese e =
619− let this (This x) = Just x
620− this (These x _) = Just x
621− this _ = Nothing
622− that (That y) = Just y
623− that (These _ y) = Just y
624− that _ = Nothing
625− in (fmapMaybe this e, fmapMaybe that e)
626−
627−-- | Split the event into an 'EventSelector' that allows efficient selection of
628−-- the individual 'Event's.
629−fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
630−fanMap = fan . fmap mapToDMap
631−
632−-- | Switches to the new event whenever it receives one; the new event is used
633−-- immediately, on the same frame that it is switched to
634−switchPromptly :: forall t m a. (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
635−switchPromptly ea0 eea = do
636− bea <- hold ea0 eea
637− let eLag = switch bea
638− eCoincidences = coincidence eea
639− return $ leftmost [eCoincidences, eLag]
640−
641−instance Reflex t => Align (Event t) where
642− nil = never
643− align ea eb = fmapMaybe dmapToThese $ merge $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
644−
645−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
646−-- the 'Behavior' is true at the time of occurence.
647−gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
648−gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
649−
650−-- | Create a new behavior given a starting behavior and switch to a the behvior
651−-- carried by the event when it fires.
652−switcher :: (Reflex t, MonadHold t m)
653− => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
654−switcher b eb = pull . (sample <=< sample) <$> hold b eb
655−
656−-- | Combine two 'Dynamic's. The result will change whenever either (or both)
657−-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
658−zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
659−zipDyn = zipDynWith (,)
660−
661−-- | Combine two 'Dynamic's with a combining function. The result will change
662−-- whenever either (or both) input 'Dynamic' changes.
663−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
664−zipDynWith f da db =
665− let eab = align (updated da) (updated db)
666− ec = flip push eab $ \o -> do
667− (a, b) <- case o of
668− This a -> do
669− b <- sample $ current db
670− return (a, b)
671− That b -> do
672− a <- sample $ current da
673− return (a, b)
674− These a b -> return (a, b)
675− return $ Just $ f a b
676− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
677−
678−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
679− mconcat = distributeListOverDynWith mconcat
680− mempty = constDyn mempty
681− mappend = zipDynWith mappend
682−
683−-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
684−-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
685−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
686−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
687−distributeDMapOverDynPure dm = case DMap.toList dm of
688− [] -> constDyn DMap.empty
689− [k :=> v] -> fmap (DMap.singleton k . Identity) v
690− _ ->
691− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
692− edmPre = merge $ DMap.map updated dm
693− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
694− olds <- sample $ current result
695− return $ DMap.unionWithKey (\_ _ new -> new) olds news
696− in result
697−
698−-- | Convert a list of 'Dynamic's into a 'Dynamic' list.
699−distributeListOverDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
700−distributeListOverDyn = distributeListOverDynWith id
701−
702−-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
703−distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
704−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
705−
706−
707−--------------------------------------------------------------------------------
708−-- Accumulator
709−--------------------------------------------------------------------------------
710−
711−-- | An 'Accumulator' type can be built by accumulating occurrences of an
712−-- 'Event'.
713−class Reflex t => Accumulator t f | f -> t where
714− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
715− accum f = accumMaybe $ \v o -> Just $ f v o
716− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
717− accumM f = accumMaybeM $ \v o -> Just <$> f v o
718− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
719− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
720− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
721− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
722− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
723− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
724− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
725− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
726− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
727− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
728−
729−-- | Accumulate occurrences of an 'Event', producing an output occurrence each
730−-- time. Discard the underlying 'Accumulator'.
731−mapAccum_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
732−mapAccum_ f z e = do
733− (_ :: Behavior t a, result) <- mapAccum f z e
734− return result
735−
736−-- | Accumulate occurrences of an 'Event', possibly producing an output
737−-- occurrence each time. Discard the underlying 'Accumulator'.
738−mapAccumMaybe_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
739−mapAccumMaybe_ f z e = do
740− (_ :: Behavior t a, result) <- mapAccumMaybe f z e
741− return result
742−
743−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
744−-- an output occurrence each time. Discard the underlying 'Accumulator'.
745−mapAccumM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
746−mapAccumM_ f z e = do
747− (_ :: Behavior t a, result) <- mapAccumM f z e
748− return result
749−
750−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
751−-- producing an output occurrence each time. Discard the underlying
752−-- 'Accumulator'.
753−mapAccumMaybeM_ :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
754−mapAccumMaybeM_ f z e = do
755− (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
756− return result
757−
758−instance Reflex t => Accumulator t (Dynamic t) where
759− accumMaybeM f z e = do
760− rec let e' = flip push e $ \o -> do
761− v <- sample $ current d'
762− f v o
763− d' <- holdDyn z e'
764− return d'
765− mapAccumMaybeM f z e = do
766− rec let e' = flip push e $ \o -> do
767− v <- sample $ current d'
768− result <- f v o
769− return $ case result of
770− (Nothing, Nothing) -> Nothing
771− _ -> Just result
772− d' <- holdDyn z $ fmapMaybe fst e'
773− return (d', fmapMaybe snd e')
774−
775−instance Reflex t => Accumulator t (Behavior t) where
776− accumMaybeM f z e = current <$> accumMaybeM f z e
777− mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
778−
779−instance Reflex t => Accumulator t (Event t) where
780− accumMaybeM f z e = updated <$> accumMaybeM f z e
781− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
782−
783−-- | Create a new 'Event' by combining each occurence with the next value of the
784−-- list using the supplied function. If the list runs out of items, all
785−-- subsequent 'Event' occurrences will be ignored.
786−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
787−zipListWithEvent f l e = do
788− let f' a b = case a of
789− h:t -> (Just t, Just $ f h b)
790− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
791− mapAccumMaybe_ f' l e
792−
793−--------------------------------------------------------------------------------
794−-- Deprecated functions
795−--------------------------------------------------------------------------------
796−
797−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
798−-- occurs. If both occur at the same time they are combined using 'mappend'.
799−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
800−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
801−appendEvents = alignWith $ mergeThese mappend
802−
803−-- | Alias for 'headE'
804−{-# DEPRECATED onceE "Use 'headE' instead" #-}
805−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
806−onceE = headE
807−
808−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
809−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
810−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
811−sequenceThese t = case t of
812− This ma -> liftM This ma
813− These ma mb -> liftM2 These ma mb
814− That mb -> liftM That mb
src/Reflex/Deletable/Class.hsdeleted−17
@@ -1,17 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE FunctionalDependencies #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE UndecidableInstances #-}
5−module Reflex.Deletable.Class where
6−
7−import Reflex.Class
8−
9−import Control.Monad.Reader
10−import Control.Monad.Trans.Control
11−
12−class (Reflex t, Monad m) => Deletable t m | m -> t where
13− deletable :: Event t () -> m a -> m a
14−
15−instance Deletable t m => Deletable t (ReaderT r m) where
16− {-# INLINABLE deletable #-}
17− deletable = liftThrough . deletable
src/Reflex/Dynamic.hsdeleted−561
@@ -1,561 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE PolyKinds #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−-- | This module contains various functions for working with 'Dynamic' values.
16−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
17−module Reflex.Dynamic
18− ( -- * Basics
19− Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
20− , current
21− , updated
22− , holdDyn
23− , constDyn
24− , uniqDyn
25− , uniqDynBy
26− , count
27− , toggle
28− , switchPromptlyDyn
29− , tagPromptlyDyn
30− , attachPromptlyDyn
31− , attachPromptlyDynWith
32− , attachPromptlyDynWithMaybe
33− , foldDyn
34− , foldDynM
35− , foldDynMaybe
36− , foldDynMaybeM
37− , joinDynThroughMap
38− , traceDyn
39− , traceDynWith
40− , splitDynPure
41− , distributeDMapOverDynPure
42− , Demux
43− , demux
44− , demuxed
45− -- Things that probably aren't very useful:
46− , HList (..)
47− , FHList (..)
48− , collectDynPure
49− , distributeFHListOverDynPure
50− -- Unsafe
51− , unsafeDynamic
52− -- * Deprecated functions
53− , apDyn
54− , attachDyn
55− , attachDynWith
56− , attachDynWithMaybe
57− , collectDyn
58− , combineDyn
59− , distributeDMapOverDyn
60− , distributeFHListOverDyn
61− , forDyn
62− , forDynM
63− , getDemuxed
64− , joinDyn
65− , mapDyn
66− , mapDynM
67− , mconcatDyn
68− , nubDyn
69− , splitDyn
70− , tagDyn
71− ) where
72−
73−import Prelude hiding (mapM, mapM_)
74−
75−import Data.Functor.Misc
76−import Reflex.Class
77−
78−import Control.Applicative ((<*>))
79−import Control.Monad hiding (forM, forM_, mapM, mapM_)
80−import Control.Monad.Fix
81−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
82−import Data.Align
83−import Data.Dependent.Map (DMap)
84−import qualified Data.Dependent.Map as DMap
85−import Data.Dependent.Sum (DSum (..))
86−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
87−import Data.Map (Map)
88−import Data.Monoid
89−import Data.These
90−
91−import Debug.Trace
92−
93−-- | Create a new 'Dynamic' that only signals changes if the values actually
94−-- changed.
95−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
96−uniqDyn = uniqDynBy (==)
97−
98−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
99−-- changes and the given function returns 'False' when given both the old and
100−-- the new values.
101−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
102−uniqDynBy eq d =
103− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
104− in unsafeDynamic (current d) e'
105−
106−-- | Create a 'Dynamic' using the initial value and change it each time the
107−-- 'Event' occurs using a folding function on the previous value and the value
108−-- of the 'Event'.
109−foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
110−foldDyn = accum . flip
111−
112−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
113−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
114−foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
115−foldDynM = accumM . flip
116−
117−-- | Create a 'Dynamic' using the provided initial value and change it each time
118−-- the provided 'Event' occurs, using a function to combine the old value with
119−-- the 'Event''s value. If the function returns 'Nothing', the value is not
120−-- changed; this is distinct from returning 'Just' the old value, since the
121−-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
122−-- in the 'Nothing' case.
123−foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
124−foldDynMaybe = accumMaybe . flip
125−
126−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
127−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
128−foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
129−foldDynMaybeM = accumMaybeM . flip
130−
131−-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
132−count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
133−count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
134−
135−-- | Create a new 'Dynamic' using the initial value that flips its
136−-- value every time the 'Event' occurs.
137−toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
138−toggle = foldDyn (const not)
139−
140−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
141−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
142−-- old and new inner Events fire simultaneously, the output will fire with the
143−-- value of the *new* 'Event'.
144−switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
145−switchPromptlyDyn de =
146− let eLag = switch $ current de
147− eCoincidences = coincidence $ updated de
148− in leftmost [eCoincidences, eLag]
149−
150−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
151−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
152−splitDynPure d = (fmap fst d, fmap snd d)
153−
154−-- | Create a 'Dynamic' with a 'DMap' of values out of a 'DMap' of 'Dynamic'
155−-- values. The resulting 'Dynamic' will change whenever one of the input
156−-- 'Dynamic's changes.
157−--
158−-- This function's implementation is more efficient than simply using
159−-- 'zipDynWith' or applicative syntax to achieve the same result, especially for
160−-- maps with many elements.
161−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
162−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
163−
164−--TODO: Generalize this to functors other than Maps
165−-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
166−-- with the current values of the 'Dynamic's in a map.
167−joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
168−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
169−
170−-- | Print the value of the 'Dynamic' on each change and prefix it
171−-- with the provided string. This should /only/ be used for debugging.
172−--
173−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
174−-- else in the system is depending on it.
175−traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
176−traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
177−
178−-- | Print the result of applying the provided function to the value
179−-- of the 'Dynamic' on each change. This should /only/ be used for
180−-- debugging.
181−--
182−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
183−-- else in the system is depending on it.
184−traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
185−traceDynWith f d =
186− let e' = traceEventWith f $ updated d
187− getV0 = do
188− x <- sample $ current d
189− trace (f x) $ return x
190− in unsafeBuildDynamic getV0 e'
191−
192−-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
193−-- each time the 'Event' occurs.
194−--
195−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
196−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
197−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
198−-- will be used, since the 'Behavior' won't be updated until the end of the frame.
199−-- Additionally, this means that the output 'Event' may not be used to directly change
200−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
201−-- cyclic data flows, generally `tag (current d) e` is preferred.
202−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
203−tagPromptlyDyn = attachPromptlyDynWith const
204−
205−-- | Attach the current value of the 'Dynamic' to the value of the
206−-- 'Event' each time it occurs.
207−--
208−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
209−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
210−attachPromptlyDyn = attachPromptlyDynWith (,)
211−
212−-- | Combine the current value of the 'Dynamic' with the value of the
213−-- 'Event' each time it occurs.
214−--
215−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
216−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
217−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
218−
219−-- | Create a new 'Event' by combining the value at each occurence with the
220−-- current value of the 'Dynamic' value and possibly filtering if the combining
221−-- function returns 'Nothing'.
222−--
223−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
224−-- (current d)`. See 'tagPromptlyDyn' for details.
225−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
226−attachPromptlyDynWithMaybe f d e =
227− let e' = attach (current d) e
228− in fforMaybe (align e' $ updated d) $ \case
229− This (a, b) -> f a b -- Only the tagging event is firing, so use that
230− These (_, b) a -> f a b -- Both events are firing, so use the newer value
231− That _ -> Nothing -- The tagging event isn't firing, so don't fire
232−
233−--------------------------------------------------------------------------------
234−-- Demux
235−--------------------------------------------------------------------------------
236−
237−-- | Represents a time changing value together with an 'EventSelector' that can
238−-- efficiently detect when the underlying 'Dynamic' has a particular value.
239−-- This is useful for representing data like the current selection of a long
240−-- list.
241−--
242−-- Semantically,
243−--
244−-- > demuxed (demux d) k === fmap (== k) d
245−--
246−-- However, the when getDemuxed is used multiple times, the complexity is only
247−-- /O(log(n))/, rather than /O(n)/ for fmap.
248−data Demux t k = Demux { demuxValue :: Behavior t k
249− , demuxSelector :: EventSelector t (Const2 k Bool)
250− }
251−
252−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
253−-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
254−demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
255−demux k = Demux (current k)
256− (fan $ attachWith (\k0 k1 -> if k0 == k1
257− then DMap.empty
258− else DMap.fromList [Const2 k0 :=> Identity False,
259− Const2 k1 :=> Identity True])
260− (current k) (updated k))
261−
262−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
263−-- faster than) mapping over the original 'Dynamic' and checking whether it is
264−-- equal to the given key.
265−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
266−demuxed d k =
267− let e = select (demuxSelector d) (Const2 k)
268− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
269−
270−--------------------------------------------------------------------------------
271−-- collectDyn
272−--------------------------------------------------------------------------------
273−
274−--TODO: This whole section is badly in need of cleanup
275−
276−-- | A heterogeneous list whose type and length are fixed statically. This is
277−-- reproduced from the 'HList' package due to integration issues, and because
278−-- very little other functionality from that library is needed.
279−data HList (l::[*]) where
280− HNil :: HList '[]
281− HCons :: e -> HList l -> HList (e ': l)
282−
283−infixr 2 `HCons`
284−
285−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
286−type instance HRevApp '[] l = l
287−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
288−
289−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
290−hRevApp HNil l = l
291−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
292−
293−hReverse :: HList l -> HList (HRevApp l '[])
294−hReverse l = hRevApp l HNil
295−
296−hBuild :: (HBuild' '[] r) => r
297−hBuild = hBuild' HNil
298−
299−class HBuild' l r where
300− hBuild' :: HList l -> r
301−
302−instance (l' ~ HRevApp l '[])
303− => HBuild' l (HList l') where
304− hBuild' = hReverse
305−
306−instance HBuild' (a ': l) r
307− => HBuild' l (a->r) where
308− hBuild' l x = hBuild' (HCons x l)
309−
310−-- | Like 'HList', but with a functor wrapping each element.
311−data FHList f l where
312− FHNil :: FHList f '[]
313− FHCons :: f e -> FHList f l -> FHList f (e ': l)
314−
315−instance GEq (HListPtr l) where
316− HHeadPtr `geq` HHeadPtr = Just Refl
317− HHeadPtr `geq` HTailPtr _ = Nothing
318− HTailPtr _ `geq` HHeadPtr = Nothing
319− HTailPtr a `geq` HTailPtr b = a `geq` b
320−
321−instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
322− HHeadPtr `gcompare` HHeadPtr = GEQ
323− HHeadPtr `gcompare` HTailPtr _ = GLT
324− HTailPtr _ `gcompare` HHeadPtr = GGT
325− HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
326−
327−data HListPtr l a where
328− HHeadPtr :: HListPtr (h ': t) h
329− HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
330−
331−fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
332−fhlistToDMap = DMap.fromList . go
333− where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
334− go = \case
335− FHNil -> []
336− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
337−
338−class RebuildSortedHList l where
339− rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
340− rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
341−
342−instance RebuildSortedHList '[] where
343− rebuildSortedFHList l = case l of
344− [] -> FHNil
345− _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
346− rebuildSortedHList l = case l of
347− [] -> HNil
348− _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
349−
350−instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
351− rebuildSortedFHList l = case l of
352− ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
353− _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
354− rebuildSortedHList l = case l of
355− ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
356− _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
357−
358−dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
359−dmapToHList = rebuildSortedHList . DMap.toList
360−
361−-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
362−-- single 'Dynamic' whose value represents the current values of all of the
363−-- input 'Dynamic's.
364−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
365−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
366−
367−class AllAreFunctors (f :: a -> *) (l :: [a]) where
368− type FunctorList f l :: [*]
369− toFHList :: HList (FunctorList f l) -> FHList f l
370− fromFHList :: FHList f l -> HList (FunctorList f l)
371−
372−instance AllAreFunctors f '[] where
373− type FunctorList f '[] = '[]
374− toFHList l = case l of
375− HNil -> FHNil
376−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
377− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
378−#endif
379− fromFHList FHNil = HNil
380−
381−instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
382− type FunctorList f (h ': t) = f h ': FunctorList f t
383− toFHList l = case l of
384− a `HCons` b -> a `FHCons` toFHList b
385−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
386− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
387−#endif
388− fromFHList (a `FHCons` b) = a `HCons` fromFHList b
389−
390−-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
391−-- single 'Dynamic' whose value represents all the current values of the input's
392−-- consitutent 'Dynamic's.
393−collectDynPure :: ( RebuildSortedHList (HListElems b)
394− , IsHList a, IsHList b
395− , AllAreFunctors (Dynamic t) (HListElems b)
396− , Reflex t
397− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
398− ) => a -> Dynamic t b
399−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
400−
401−-- Poor man's Generic
402−class IsHList a where
403− type HListElems a :: [*]
404− toHList :: a -> HList (HListElems a)
405− fromHList :: HList (HListElems a) -> a
406−
407−instance IsHList (a, b) where
408− type HListElems (a, b) = [a, b]
409− toHList (a, b) = hBuild a b
410− fromHList l = case l of
411− a `HCons` b `HCons` HNil -> (a, b)
412−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
413− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
414−#endif
415−
416−instance IsHList (a, b, c, d) where
417− type HListElems (a, b, c, d) = [a, b, c, d]
418− toHList (a, b, c, d) = hBuild a b c d
419− fromHList l = case l of
420− a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
421−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
422− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
423−#endif
424−
425−instance IsHList (a, b, c, d, e, f) where
426− type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
427− toHList (a, b, c, d, e, f) = hBuild a b c d e f
428− fromHList l = case l of
429− a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
430−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
431− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
432−#endif
433−
434−--------------------------------------------------------------------------------
435−-- Deprecated functions
436−--------------------------------------------------------------------------------
437−
438−-- | Map a function over a 'Dynamic'.
439−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
440−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
441−mapDyn f = return . fmap f
442−
443−-- | Map a sampling function over a 'Dynamic'. The sampling function will use the input 'Dynamic''s 'current' 'Behavior' until the first time its 'updated' 'Event' fires, at which point it will be invoked only once each time the 'Event' fires.
444−{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
445−mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
446−mapDynM f d = do
447− let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
448− eb' = fmap constant e'
449− v0 = pull $ f =<< sample (current d)
450− bb' :: Behavior t (Behavior t b) <- hold v0 eb'
451− let b' = pull $ sample =<< sample bb'
452− return $ unsafeDynamic b' e'
453−
454−-- | Flipped version of 'mapDyn'.
455−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
456−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
457−forDyn a = return . ffor a
458−
459−-- | Flipped version of 'mapDynM'
460−{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
461−forDynM :: forall t m a b. (Reflex t, MonadHold t m) => Dynamic t a -> (forall m'. MonadSample t m' => a -> m' b) -> m (Dynamic t b)
462−forDynM d f = mapDynM f d
463−
464−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
465−-- of the tuple.
466−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
467−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
468−splitDyn = return . splitDynPure
469−
470−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
471−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
472−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
473−mconcatDyn = return . mconcat
474−
475−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
476−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
477−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
478−distributeDMapOverDyn = return . distributeDMapOverDynPure
479−
480−-- | Merge two 'Dynamic's into a new one using the provided function. The new
481−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
482−-- its value.
483−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
484−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
485−combineDyn f a b = return $ zipDynWith f a b
486−
487−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
488−--
489−-- > do
490−-- > person <- Person `mapDyn` dynFirstName
491−-- > `apDyn` dynListName
492−-- > `apDyn` dynAge
493−-- > `apDyn` dynAddress
494−{-# DEPRECATED apDyn "Use 'ffor m (<*> a)' instead of 'apDyn m a'; consider eliminating monadic style, since Dynamics are now Applicative and can be used with applicative style directly" #-}
495−{-# ANN apDyn "HLint: ignore Use fmap" #-}
496−apDyn :: forall t m a b. (Reflex t, Monad m)
497− => m (Dynamic t (a -> b))
498− -> Dynamic t a
499− -> m (Dynamic t b)
500−apDyn m a = liftM (<*> a) m
501−
502−--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
503−-- | This function no longer needs to be monadic, so it has been replaced by
504−-- 'demuxed', which is pure.
505−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
506−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
507−getDemuxed d = return . demuxed d
508−
509−-- | This function no longer needs to be monadic, so it has been replaced by
510−-- 'distributeFHListOverDynPure', which is pure.
511−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
512−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
513−distributeFHListOverDyn = return . distributeFHListOverDynPure
514−
515−-- | This function no longer needs to be monadic, so it has been replaced by
516−-- 'collectDynPure', which is pure.
517−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
518−collectDyn :: ( RebuildSortedHList (HListElems b)
519− , IsHList a, IsHList b
520− , AllAreFunctors (Dynamic t) (HListElems b)
521− , Reflex t, Monad m
522− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
523− ) => a -> m (Dynamic t b)
524−collectDyn = return . collectDynPure
525−
526−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
527−-- semantics.
528−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
529−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
530−tagDyn = tagPromptlyDyn
531−
532−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
533−-- semantics.
534−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
535−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
536−attachDyn = attachPromptlyDyn
537−
538−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
539−-- semantics.
540−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
541−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
542−attachDynWith = attachPromptlyDynWith
543−
544−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
545−-- its semantics.
546−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
547−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
548−attachDynWithMaybe = attachPromptlyDynWithMaybe
549−
550−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
551−-- current value will always be equal to the current value's current value, and
552−-- will change whenever either the inner or the outer (or both) values change.
553−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
554−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
555−joinDyn = join
556−
557−-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
558−-- been renamed to 'uniqDyn'
559−{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
560−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
561−nubDyn = uniqDyn
src/Reflex/Dynamic/TH.hsdeleted−88
@@ -1,88 +0,0 @@
1−{-# LANGUAGE EmptyDataDecls #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE PatternGuards #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−{-# LANGUAGE TemplateHaskell #-}
6−{-# LANGUAGE TypeOperators #-}
7−-- | Template Haskell helper functions for building complex 'Dynamic' values.
8−module Reflex.Dynamic.TH (qDyn, unqDyn, mkDyn) where
9−
10−import Reflex.Dynamic
11−
12−import Control.Monad.State
13−import Data.Data
14−import Data.Generics
15−import Data.Monoid
16−import qualified Language.Haskell.Exts as Hs
17−import qualified Language.Haskell.Meta.Syntax.Translate as Hs
18−import Language.Haskell.TH
19−import Language.Haskell.TH.Quote
20−import qualified Language.Haskell.TH.Syntax as TH
21−
22−-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
23−-- @$(unqDyn [| x |])@ to refer to any expression @x@ of type @Dynamic t a@; the
24−-- unquoted result will be of type @a@
25−qDyn :: Q Exp -> Q Exp
26−qDyn qe = do
27− e <- qe
28− let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
29− f d = case eqT of
30− Just (Refl :: d :~: Exp)
31− | AppE (VarE m) eInner <- d
32− , m == 'unqMarker
33− -> do n <- lift $ newName "dynamicQuotedExpressionVariable"
34− modify ((n, eInner):)
35− return $ VarE n
36− _ -> gmapM f d
37− (e', exprsReversed) <- runStateT (gmapM f e) []
38− let exprs = reverse exprsReversed
39− arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
40− param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
41− [| return $ $(return $ LamE [param] e') $ distributeFHListOverDynPure $(return arg) |]
42−
43−-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
44−-- 'qDyn' quotation.
45−unqDyn :: Q Exp -> Q Exp
46−unqDyn e = [| unqMarker $e |]
47−
48−-- | This type represents an occurrence of unqDyn before it has been processed
49−-- by qDyn. If you see it in a type error, it probably means that unqDyn has
50−-- been used outside of a qDyn context.
51−data UnqDyn
52−
53−-- unqMarker must not be exported; it is used only as a way of smuggling data
54−-- from unqDyn to qDyn
55−
56−--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
57−unqMarker :: a -> UnqDyn
58−unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
59−
60−-- | Create a 'Dynamic' value using other 'Dynamic's as inputs. The result is
61−-- sometimes more concise and readable than the equivalent 'Applicative'-based
62−-- expression. For example:
63−--
64−-- > [mkDyn| $x + $v * $t + 1/2 * $a * $t ^ 2 |]
65−--
66−-- would have a very cumbersome 'Applicative' encoding.
67−mkDyn :: QuasiQuoter
68−mkDyn = QuasiQuoter
69− { quoteExp = mkDynExp
70− , quotePat = error "mkDyn: pattern splices are not supported"
71− , quoteType = error "mkDyn: type splices are not supported"
72− , quoteDec = error "mkDyn: declaration splices are not supported"
73− }
74−
75−mkDynExp :: String -> Q Exp
76−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
77− Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
78− Hs.ParseOk e -> qDyn $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
79− where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
80− antiE x = case x of
81− Hs.SpliceExp se ->
82− Hs.App (Hs.Var $ Hs.Qual (Hs.ModuleName modName) (Hs.Ident occName)) $ case se of
83− Hs.IdSplice v -> Hs.Var $ Hs.UnQual $ Hs.Ident v
84− Hs.ParenSplice ps -> ps
85− _ -> x
86− reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
87− | modName == modName' && occName == occName' = 'unqMarker
88− reinstateUnqDyn x = x
src/Reflex/Dynamic/Uniq.hsdeleted−101
@@ -1,101 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE MagicHash #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−-- | This module provides a variation of 'Dynamic' values that uses cheap
5−-- pointer equality checks to reduce the amount of signal propagation needed.
6−module Reflex.Dynamic.Uniq
7− ( UniqDynamic
8− , uniqDynamic
9− , fromUniqDynamic
10− , alreadyUniqDynamic
11− ) where
12−
13−import Control.Applicative (Applicative (..))
14−import GHC.Exts
15−import Reflex.Class
16−import Reflex.Dynamic
17−
18−-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
19−-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
20−-- the value inside a 'UniqDynamic' is always evaluated to
21−-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
22−--
23−-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
24−-- unnecessary update propagation; this is much more efficient than performing
25−-- full comparisons. However, when the 'UniqDynamic' is converted back into a
26−-- regular 'Dynamic', a full comparison is performed.
27−newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
28−
29−-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
30−uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
31−uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
32− old <- sample $ current d --TODO: Is it better to sample ourselves here?
33− return $ unsafeJustChanged old new
34−
35−-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
36−-- final check using the output type's 'Eq' instance to ensure deterministic
37−-- behavior.
38−--
39−-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
40−-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
41−-- eliminate more 'updated' occurrences than it should. For example, NaN values
42−-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
43−-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
44−-- to lose changes from NaN to NaN.
45−fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
46−fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
47− where
48− -- Only consider values different if they fail both pointer equality /and/
49− -- 'Eq' equality. This is to make things a bit more deterministic in the
50− -- case of unlawful 'Eq' instances. However, it is still possible to
51− -- achieve nondeterminism by constructing elements that are identical in
52− -- value, unequal according to 'Eq', and nondeterministically equal or
53− -- nonequal by pointer quality. I suspect that it is impossible to make the
54− -- behavior deterministic in this case.
55− superEq a b = a `unsafePtrEq` b || a == b
56−
57−-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
58−-- faster than uniqDynamic when used with a Dynamic whose values are always (or
59−-- nearly always) different from its previous values; if used with a Dynamic
60−-- whose values do not change frequently, it may be much slower than uniqDynamic
61−alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
62−alreadyUniqDynamic = UniqDynamic
63−
64−unsafePtrEq :: a -> a -> Bool
65−unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
66− 0# -> False
67− _ -> True
68−
69−unsafeJustChanged :: a -> a -> Maybe a
70−unsafeJustChanged old new =
71− if old `unsafePtrEq` new
72− then Nothing
73− else Just new
74−
75−instance Reflex t => Accumulator t (UniqDynamic t) where
76− accumMaybeM f z e = do
77− let f' old change = do
78− mNew <- f old change
79− return $ unsafeJustChanged old =<< mNew
80− d <- accumMaybeM f' z e
81− return $ UniqDynamic d
82− mapAccumMaybeM f z e = do
83− let f' old change = do
84− (mNew, output) <- f old change
85− return (unsafeJustChanged old =<< mNew, output)
86− (d, out) <- mapAccumMaybeM f' z e
87− return (UniqDynamic d, out)
88−
89−instance Reflex t => Functor (UniqDynamic t) where
90− fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
91−
92−instance Reflex t => Applicative (UniqDynamic t) where
93− pure = UniqDynamic . constDyn
94− UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
95− _ *> b = b
96− a <* _ = a
97−
98−instance Reflex t => Monad (UniqDynamic t) where
99− UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
100− _ >> b = b
101− return = pure
src/Reflex/DynamicWriter.hsdeleted−181
@@ -1,181 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RecursiveDo #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−module Reflex.DynamicWriter where
13−
14−import Control.Monad.Exception
15−import Control.Monad.IO.Class
16−import Control.Monad.Reader
17−import Control.Monad.Ref
18−import Control.Monad.State.Strict
19−import Data.Dependent.Map (DMap)
20−import Data.Functor.Compose
21−import Data.Functor.Misc
22−import Data.Map (Map)
23−import qualified Data.Map as Map
24−import Data.Semigroup
25−import Reflex.Class
26−import Reflex.Deletable.Class
27−import Reflex.Dynamic
28−import Reflex.PerformEvent.Class
29−import Reflex.PostBuild.Class
30−import Reflex.Host.Class
31−
32−instance MonadTrans (DynamicWriterT t w) where
33− lift = DynamicWriterT . lift
34−
35−data PatchMap a where
36− PatchMap :: Ord k => Map k (Maybe v) -> PatchMap (Map k v)
37−
38−instance Patch PatchMap where
39− 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?
40− where insertions = Map.mapMaybeWithKey (const id) p
41− deletions = Map.mapMaybeWithKey (const nothingToJust) p
42− nothingToJust = \case
43− Nothing -> Just ()
44− Just _ -> Nothing
45−
46−instance Ord k => Semigroup (PatchMap (Map k v)) where
47− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
48− -- PatchMap is idempotent, so stimes n is id for every n
49−#if MIN_VERSION_semigroups(0,17,0)
50− stimes = stimesIdempotentMonoid
51−#else
52− times1p n x = case compare n 0 of
53− LT -> error "stimesIdempotentMonoid: negative multiplier"
54− EQ -> mempty
55− GT -> x
56−#endif
57−
58−instance Ord k => Monoid (PatchMap (Map k v)) where
59− mempty = PatchMap mempty
60− mappend = (<>)
61−
62−mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v) => Map k v -> Event t (PatchMap (Map k v)) -> m (Dynamic t v)
63−mconcatIncremental m0 e = do
64− d <- foldDynMaybe apply m0 e
65− return $ fmap (mconcat . Map.elems) d
66−
67−mapIncrementalMapValuesWithKey :: Reflex t => (k -> v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
68−mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
69−
70−mapIncrementalMapValues :: Reflex t => (v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
71−mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
72−
73−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (a -> a') -> (p a -> p' a') -> Incremental t p a -> Incremental t p' a'
74−unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
75−
76−incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
77−incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
78−
79−mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Event t v)) -> Event t (Map k v)
80−mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
81−
82−holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap (Map k (Replaceable t v))) -> m (Incremental t PatchMap (Map k v))
83−holdIncrementalReplaceableMap m0 m' = do
84− rec vals <- holdIncremental m0 $ m' <> valChanges
85− let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
86− return $ mapIncrementalMapValues _replaceable_value vals
87−
88−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Dynamic t v)) -> Incremental t PatchMap (Map k v)
89−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
90− where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
91− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
92−
93−data Replaceable t w = Replaceable
94− { _replaceable_value :: w
95− , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
96− }
97−
98−type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
99−
100−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT (DynamicWriterAccumulator t w) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
101−
102−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
103− type Ref (DynamicWriterT t w m) = Ref m
104− newRef = lift . newRef
105− readRef = lift . readRef
106− writeRef r = lift . writeRef r
107−
108−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
109− atomicModifyRef r = lift . atomicModifyRef r
110−
111−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
112− newEventWithTrigger = lift . newEventWithTrigger
113− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
114−
115−runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
116−runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
117−
118−mconcatIncrementalReplaceableDynMap :: forall m t k v. (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k) => Map k (Replaceable t (Dynamic t v)) -> Event t (PatchMap (Map k (Replaceable t (Dynamic t v)))) -> Event t () -> m (Replaceable t (Dynamic t v))
119−mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
120− rec vals <- holdIncremental m0 $ m' <> valChanges
121− let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
122− let i = mapIncrementalMapValues _replaceable_value vals
123− noMoreAdditions <- holdDyn False $ True <$ additionsCeased
124− let replaceSelf nma x = do
125− guard nma
126− case Map.toList x of
127− [] -> return Nothing -- Delete self
128− [(_, n)] -> return $ Just n --TODO: What if this one is also replacing itself simultaneously?
129− _ -> mzero
130− result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
131− return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
132−
133−runDynamicWriterT :: (MonadFix m, Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
134−runDynamicWriterT (DynamicWriterT a) = do
135− (result, ws) <- runStateT a []
136− Replaceable w _ <- mconcatIncrementalReplaceableDynMap (Map.fromList $ zip [1 :: Int ..] $ reverse ws) never never
137− return (result, w)
138−
139−class Monad m => MonadDynamicWriter t w m | m -> t w where
140− tellDyn :: Dynamic t w -> m ()
141−
142−instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
143− tellDyn w = DynamicWriterT $ modify (Replaceable w never :)
144−
145−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
146− ask = lift ask
147− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
148− reader = lift . reader
149−
150−liftDynamicWriterTThroughSync :: Monad m' => (m (a, DynamicWriterAccumulator t w) -> m' (b, (a, DynamicWriterAccumulator t w))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
151−liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
152− s <- get
153− (b, (a, newS)) <- lift $ f $ runStateT child s
154− put newS
155− return (b, a)
156−
157−instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
158− deletable delete child = do
159− (result, output) <- lift $ deletable delete $ runDynamicWriterT child
160− DynamicWriterT $ modify (Replaceable output (Nothing <$ delete) :)
161− return result
162−
163−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
164− type Performable (DynamicWriterT t w m) = Performable m
165− performEvent_ = lift . performEvent_
166− performEvent = lift . performEvent
167−
168−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
169− newTriggerEvent = lift newTriggerEvent
170− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
171− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
172−
173−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
174− getPostBuild = lift getPostBuild
175−
176−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
177− tellDyn = lift . tellDyn
178−
179−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
180− get = lift get
181− put = lift . put
src/Reflex/Host/Class.hsdeleted−282
@@ -1,282 +0,0 @@
1−{-# LANGUAGE ExistentialQuantification #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE RoleAnnotations #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−-- | This module provides the interface for hosting 'Reflex' engines. This
13−-- should only be necessary if you're writing a binding or some other library
14−-- that provides a core event loop.
15−module Reflex.Host.Class
16− ( ReflexHost (..)
17− , MonadSubscribeEvent (..)
18− , MonadReadEvent (..)
19− , MonadReflexCreateTrigger (..)
20− , MonadReflexHost (..)
21− , fireEvents
22− , newEventWithTriggerRef
23− , fireEventRef
24− , fireEventRefAndRead
25− ) where
26−
27−import Reflex.Class
28−
29−import Control.Applicative
30−import Control.Monad
31−import Control.Monad.Fix
32−import Control.Monad.Identity
33−import Control.Monad.Ref
34−import Control.Monad.Trans
35−import Control.Monad.Trans.Cont (ContT ())
36−import Control.Monad.Trans.Except (ExceptT ())
37−import Control.Monad.Trans.Reader (ReaderT ())
38−import Control.Monad.Trans.RWS (RWST ())
39−import Control.Monad.Trans.State (StateT ())
40−import qualified Control.Monad.Trans.State.Strict as Strict
41−import Control.Monad.Trans.Writer (WriterT ())
42−import Data.Dependent.Sum (DSum (..))
43−import Data.GADT.Compare
44−import Data.Monoid
45−
46−-- Note: this import must come last to silence warnings from AMP
47−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
48−
49−-- | Framework implementation support class for the reflex implementation
50−-- represented by @t@.
51−class ( Reflex t
52− , MonadReflexCreateTrigger t (HostFrame t)
53− , MonadSample t (HostFrame t)
54− , MonadHold t (HostFrame t)
55− , MonadFix (HostFrame t)
56− , MonadSubscribeEvent t (HostFrame t)
57− ) => ReflexHost t where
58− type EventTrigger t :: * -> *
59− type EventHandle t :: * -> *
60− type HostFrame t :: * -> *
61−
62−-- | Monad in which Events can be 'subscribed'. This forces all underlying
63−-- event sources to be initialized, so that the event will fire whenever it
64−-- ought to. Events must be subscribed before they are read using readEvent
65−class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
66− -- | Subscribe to an event and set it up if needed.
67− --
68− -- This function will create a new 'EventHandle' from an 'Event'. This handle
69− -- may then be used via 'readEvent' in the read callback of
70− -- 'fireEventsAndRead'.
71− --
72− -- If the event wasn't subscribed to before (either manually or through a
73− -- dependent event or behavior) then this function will cause the event and
74− -- all dependencies of this event to be set up. For example, if the event was
75− -- created by 'newEventWithTrigger', then it's callback will be executed.
76− --
77− -- It's safe to call this function multiple times.
78− subscribeEvent :: Event t a -> m (EventHandle t a)
79−
80−-- | Monad that allows to read events' values.
81−class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
82− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling
83− -- 'subscribeEvent').
84− --
85− -- After event propagation is done, all events can be in two states: either
86− -- they are firing with some value or they are not firing. In the former
87− -- case, this function returns @Just act@, where @act@ in an action to read
88− -- the current value of the event. In the latter case, the function returns
89− -- @Nothing@.
90− --
91− -- This function is normally used in the calllback for 'fireEventsAndRead'.
92− readEvent :: EventHandle t a -> m (Maybe (m a))
93−
94−-- | A monad where new events feed from external sources can be created.
95−class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
96− -- | Creates a root 'Event' (one that is not based on any other event).
97− --
98− -- When a subscriber first subscribes to an event (building another event that
99− -- depends on the subscription) the given callback function is run and passed
100− -- a trigger. The callback function can then set up the event source in IO.
101− -- After this is done, the callback function must return an accompanying
102− -- teardown action.
103− --
104− -- Any time between setup and teardown the trigger can be used to fire the
105− -- event, by passing it to 'fireEventsAndRead'.
106− --
107− -- Note: An event may be set up multiple times. So after the teardown action
108− -- is executed, the event may still be set up again in the future.
109− newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
110− newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
111−
112−-- | 'MonadReflexHost' designates monads that can run reflex frames.
113−class ( ReflexHost t
114− , MonadReflexCreateTrigger t m
115− , MonadSubscribeEvent t m
116− , MonadReadEvent t (ReadPhase m)
117− , MonadSample t (ReadPhase m)
118− , MonadHold t (ReadPhase m)
119− ) => MonadReflexHost t m | m -> t where
120− type ReadPhase m :: * -> *
121− -- | Propagate some events firings and read the values of events afterwards.
122− --
123− -- This function will create a new frame to fire the given events. It will
124− -- then update all dependent events and behaviors. After that is done, the
125− -- given callback is executed which allows to read the final values of events
126− -- and check whether they have fired in this frame or not.
127− --
128− -- All events that are given are fired at the same time.
129− --
130− -- This function is typically used in the main loop of a reflex framework
131− -- implementation. The main loop waits for external events to happen (such as
132− -- keyboard input or a mouse click) and then fires the corresponding events
133− -- using this function. The read callback can be used to read output events
134− -- and perform a corresponding response action to the external event.
135− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
136−
137− -- | Run a frame without any events firing.
138− --
139− -- This function should be used when you want to use 'sample' and 'hold' when
140− -- no events are currently firing. Using this function in that case can
141− -- improve performance, since the implementation can assume that no events are
142− -- firing when 'sample' or 'hold' are called.
143− --
144− -- This function is commonly used to set up the basic event network when the
145− -- application starts up.
146− runHostFrame :: HostFrame t a -> m a
147−
148−-- | Like 'fireEventsAndRead', but without reading any events.
149−fireEvents :: MonadReflexHost t m => [DSum (EventTrigger t) Identity] -> m ()
150−fireEvents dm = fireEventsAndRead dm $ return ()
151−{-# INLINE fireEvents #-}
152−
153−-- | Create a new event and store its trigger in an 'IORef' while it's active.
154−--
155−-- An event is only active between the set up (when it's first subscribed to)
156−-- and the teardown phases (when noboby is subscribing the event anymore). This
157−-- function returns an Event and an 'IORef'. As long as the event is active, the
158−-- 'IORef' will contain 'Just' the event trigger to trigger this event. When the
159−-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
160−-- sources to be more efficient, since they don't need to produce events when
161−-- nobody is listening.
162−newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
163−newEventWithTriggerRef = do
164− rt <- newRef Nothing
165− e <- newEventWithTrigger $ \t -> do
166− writeRef rt $ Just t
167− return $ writeRef rt Nothing
168− return (e, rt)
169−{-# INLINE newEventWithTriggerRef #-}
170−
171−-- | Fire the given trigger if it is not 'Nothing'.
172−fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
173−fireEventRef mtRef input = do
174− mt <- readRef mtRef
175− case mt of
176− Nothing -> return ()
177− Just trigger -> fireEvents [trigger :=> Identity input]
178−
179−-- | Fire the given trigger if it is not 'Nothing', and read from the given
180−-- 'EventHandle'.
181−fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
182−fireEventRefAndRead mtRef input e = do
183− mt <- readRef mtRef
184− case mt of
185− Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
186− Just trigger -> fireEventsAndRead [trigger :=> Identity input] $ do
187− mGetValue <- readEvent e
188− case mGetValue of
189− Nothing -> return Nothing
190− Just getValue -> fmap Just getValue
191−
192−
193−--------------------------------------------------------------------------------
194−-- Instances
195−--------------------------------------------------------------------------------
196−
197−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ReaderT r m) where
198− newEventWithTrigger = lift . newEventWithTrigger
199− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
200−
201−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ReaderT r m) where
202− subscribeEvent = lift . subscribeEvent
203−
204−instance MonadReflexHost t m => MonadReflexHost t (ReaderT r m) where
205− type ReadPhase (ReaderT r m) = ReadPhase m
206− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
207− runHostFrame = lift . runHostFrame
208−
209−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (WriterT w m) where
210− newEventWithTrigger = lift . newEventWithTrigger
211− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
212−
213−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (WriterT w m) where
214− subscribeEvent = lift . subscribeEvent
215−
216−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (WriterT w m) where
217− type ReadPhase (WriterT w m) = ReadPhase m
218− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
219− runHostFrame = lift . runHostFrame
220−
221−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StateT s m) where
222− newEventWithTrigger = lift . newEventWithTrigger
223− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
224−
225−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (StateT r m) where
226− subscribeEvent = lift . subscribeEvent
227−
228−instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
229− type ReadPhase (StateT s m) = ReadPhase m
230− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
231− runHostFrame = lift . runHostFrame
232−
233−
234−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
235− newEventWithTrigger = lift . newEventWithTrigger
236− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
237−
238−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
239− subscribeEvent = lift . subscribeEvent
240−
241−instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
242− type ReadPhase (Strict.StateT s m) = ReadPhase m
243− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
244− runHostFrame = lift . runHostFrame
245−
246−
247−
248−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
249− newEventWithTrigger = lift . newEventWithTrigger
250− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
251−
252−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ContT r m) where
253− subscribeEvent = lift . subscribeEvent
254−
255−instance MonadReflexHost t m => MonadReflexHost t (ContT r m) where
256− type ReadPhase (ContT r m) = ReadPhase m
257− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
258− runHostFrame = lift . runHostFrame
259−
260−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ExceptT e m) where
261− newEventWithTrigger = lift . newEventWithTrigger
262− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
263−
264−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ExceptT r m) where
265− subscribeEvent = lift . subscribeEvent
266−
267−instance MonadReflexHost t m => MonadReflexHost t (ExceptT e m) where
268− type ReadPhase (ExceptT e m) = ReadPhase m
269− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
270− runHostFrame = lift . runHostFrame
271−
272−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (RWST r w s m) where
273− newEventWithTrigger = lift . newEventWithTrigger
274− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
275−
276−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (RWST r w s m) where
277− subscribeEvent = lift . subscribeEvent
278−
279−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (RWST r w s m) where
280− type ReadPhase (RWST r w s m) = ReadPhase m
281− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
282− runHostFrame = lift . runHostFrame
src/Reflex/PerformEvent/Base.hsdeleted−168
@@ -1,168 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE RecursiveDo #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE StandaloneDeriving #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−module Reflex.PerformEvent.Base where
13−
14−import Reflex.Class
15−import Reflex.Deletable.Class
16−import Reflex.Host.Class
17−import Reflex.PerformEvent.Class
18−
19−import Control.Lens
20−import Control.Monad.Exception
21−import Control.Monad.Identity
22−import Control.Monad.Ref
23−import Control.Monad.State.Strict
24−import Data.Align
25−import Data.Dependent.Map (DMap)
26−import qualified Data.Dependent.Map as DMap
27−import Data.Dependent.Sum
28−import Data.Functor.Compose
29−import Data.Functor.Misc
30−import Data.Maybe
31−import Data.Semigroup
32−import Data.These
33−import Data.Word
34−
35−newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
36−
37−newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
38−
39−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: StateT [Event t (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())] (HostFrame t) a }
40−
41−deriving instance ReflexHost t => Functor (PerformEventT t m)
42−deriving instance ReflexHost t => Applicative (PerformEventT t m)
43−deriving instance ReflexHost t => Monad (PerformEventT t m)
44−deriving instance ReflexHost t => MonadFix (PerformEventT t m)
45−deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
46−deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
47−
48−instance (ReflexHost t, MonadAsyncException (HostFrame t)) => MonadAsyncException (PerformEventT t m) where
49− mask f = PerformEventT $ mask $ \unmask -> unPerformEventT $ f $ PerformEventT . unmask . unPerformEventT
50−
51−instance (ReflexHost t, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEvent t (PerformEventT t m) where
52− type Performable (PerformEventT t m) = PerformEventT t m
53− {-# INLINABLE performEvent_ #-}
54− performEvent_ = PerformEventT . modify . (:) . fmap (This . (>> return []))
55− {-# INLINABLE performEvent #-}
56− performEvent e = PerformEventT $ do
57− (eResult, reResultTrigger) <- newEventWithTriggerRef
58− modify $ (:) $ ffor e $ \o -> This $ do
59− result <- o
60− return [EventTriggerRef reResultTrigger :=> Identity result]
61− return eResult
62−
63−instance (ReflexHost t, Monad (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => Deletable t (PerformEventT t m) where
64− {-# INLINABLE deletable #-}
65− deletable d a = PerformEventT $ do
66− (result, reverseEventsToPerform) <- lift $ runStateT (unPerformEventT a) []
67− let (numberedEvents, _) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
68− eventToPerform <- lift $ mergePerformEventsAndAdd (DMap.fromList numberedEvents) never
69− modify (align eventToPerform d :)
70− return result
71−
72−instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
73− {-# INLINABLE newEventWithTrigger #-}
74− newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
75− {-# INLINABLE newFanEventWithTrigger #-}
76− newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
77−
78−{-# INLINABLE numberWith #-}
79−numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
80−numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
81−
82−{-# INLINABLE mergePerformEventsAndAdd #-}
83−mergePerformEventsAndAdd :: forall t m. ReflexHost t
84− => DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)
85− -> Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
86− -> HostFrame t (Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity]))
87−mergePerformEventsAndAdd initial eAddNew = do
88− rec events :: Incremental t PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t))
89− <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
90− let event = mergeWith DMap.union [mergeIncremental events, coincidence (fmap (\(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getCompose) m) eAddNew)]
91− eventToPerform :: Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity])
92− eventToPerform = ffor event $
93− fmap concat .
94− sequence .
95− fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
96− DMap.toList
97− eDelete :: Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
98− eDelete = fforMaybe event $
99− fmap (PatchDMap . DMap.fromList) .
100− (\l -> if null l then Nothing else Just l) .
101− fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> Compose Nothing) <$ x ^? there) .
102− DMap.toList
103− return eventToPerform
104−
105−{-# INLINABLE runPerformEventT #-}
106−runPerformEventT :: forall t m a. (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => PerformEventT t m a -> HostFrame t (a, Event t (HostFrame t [DSum (EventTriggerRef t m) Identity]))
107−runPerformEventT (PerformEventT a) = do
108− (result, reverseEventsToPerform) <- runStateT a []
109− (eAddNew, reAddNewTrigger) <- newEventWithTriggerRef
110− let (numberedEvents, nextNumber) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
111− nextNumberRef <- newRef nextNumber
112− let runResult :: PerformEventT t m [DSum (EventTriggerRef t m) Identity] -> HostFrame t [DSum (EventTriggerRef t m) Identity]
113− runResult (PerformEventT r) = do
114− (followups, reverseNewEventsToPerform) <- runStateT r []
115− oldN <- readRef nextNumberRef
116− let (numberedNewEvents, newN) = numberWith (\n e -> Const2 n :=> Compose (Just e)) (reverse reverseNewEventsToPerform) oldN
117− if newN == oldN then return followups else do
118− writeRef nextNumberRef newN
119− return $ (EventTriggerRef reAddNewTrigger :=> Identity (PatchDMap (DMap.fromList numberedNewEvents))) : followups
120− eventToPerform <- mergePerformEventsAndAdd (DMap.fromList numberedEvents) eAddNew
121− return (result, runResult <$> eventToPerform)
122−
123−{-# INLINABLE hostPerformEventT #-}
124−hostPerformEventT :: forall t m a. (Monad m, MonadSubscribeEvent t m, MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEventT t m a -> m (a, FireCommand t m)
125−hostPerformEventT a = do
126− (result, eventToPerform) <- runHostFrame $ runPerformEventT a
127− eventToPerformHandle <- subscribeEvent eventToPerform
128− return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
129− let go :: [DSum (EventTrigger t) Identity] -> m [a']
130− go ts = do
131− (result', mToPerform) <- fireEventsAndRead ts $ do
132− mToPerform <- sequence =<< readEvent eventToPerformHandle
133− result' <- readPhase
134− return (result', mToPerform)
135− case mToPerform of
136− Nothing -> return [result']
137− Just toPerform -> do
138− followupEventTriggerRefs <- runHostFrame toPerform
139− followupEventTriggers <- forM followupEventTriggerRefs $ \(EventTriggerRef rt :=> x) -> do
140− mt <- readRef rt
141− return $ fmap (:=> x) mt
142− fmap (result':) $ go $ catMaybes followupEventTriggers
143− go triggers
144−
145−instance ReflexHost t => MonadSample t (PerformEventT t m) where
146− {-# INLINABLE sample #-}
147− sample = PerformEventT . lift . sample
148−
149−instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
150− {-# INLINABLE hold #-}
151− hold v0 v' = PerformEventT $ lift $ hold v0 v'
152− {-# INLINABLE holdDyn #-}
153− holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
154− {-# INLINABLE holdIncremental #-}
155− holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
156−
157−instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
158− type Ref (PerformEventT t m) = Ref (HostFrame t)
159− {-# INLINABLE newRef #-}
160− newRef = PerformEventT . lift . newRef
161− {-# INLINABLE readRef #-}
162− readRef = PerformEventT . lift . readRef
163− {-# INLINABLE writeRef #-}
164− writeRef r = PerformEventT . lift . writeRef r
165−
166−instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
167− {-# INLINABLE atomicModifyRef #-}
168− atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
src/Reflex/PerformEvent/Class.hsdeleted−46
@@ -1,46 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−module Reflex.PerformEvent.Class where
11−
12−import Reflex.Class
13−
14−import Control.Monad.Reader
15−
16−class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
17− type Performable m :: * -> *
18− performEvent :: Event t (Performable m a) -> m (Event t a)
19− performEvent_ :: Event t (Performable m ()) -> m ()
20−
21−--TODO: Shouldn't have IO hard-coded
22−class Monad m => TriggerEvent t m | m -> t where
23− newTriggerEvent :: m (Event t a, a -> IO ())
24− newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
25− newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
26−
27−{-# INLINABLE performEventAsync #-}
28−performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
29−performEventAsync e = do
30− (eOut, triggerEOut) <- newTriggerEvent
31− performEvent_ $ fmap ($ triggerEOut) e
32− return eOut
33−
34−instance PerformEvent t m => PerformEvent t (ReaderT r m) where
35− type Performable (ReaderT r m) = ReaderT r (Performable m)
36− performEvent_ e = do
37− r <- ask
38− lift $ performEvent_ $ flip runReaderT r <$> e
39− performEvent e = do
40− r <- ask
41− lift $ performEvent $ flip runReaderT r <$> e
42−
43−instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
44− newTriggerEvent = lift newTriggerEvent
45− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
46− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
src/Reflex/PostBuild/Class.hsdeleted−91
@@ -1,91 +0,0 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−module Reflex.PostBuild.Class where
11−
12−import Control.Monad.Exception
13−import Control.Monad.Reader
14−import Control.Monad.Ref
15−import Control.Monad.Trans.Control
16−import Reflex.Class
17−import Reflex.Deletable.Class
18−import Reflex.Host.Class
19−import Reflex.PerformEvent.Class
20−
21−class (Reflex t, Monad m) => PostBuild t m | m -> t where
22− getPostBuild :: m (Event t ())
23−
24−newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
25−
26−instance MonadTransControl (PostBuildT t) where
27− type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
28− {-# INLINABLE liftWith #-}
29− liftWith = defaultLiftWith PostBuildT unPostBuildT
30− {-# INLINABLE restoreT #-}
31− restoreT = defaultRestoreT PostBuildT
32−
33−instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
34− {-# INLINABLE getPostBuild #-}
35− getPostBuild = PostBuildT ask
36−
37−instance Deletable t m => Deletable t (PostBuildT t m) where
38− {-# INLINABLE deletable #-}
39− deletable = liftThrough . deletable
40−
41−instance MonadSample t m => MonadSample t (PostBuildT t m) where
42− {-# INLINABLE sample #-}
43− sample = lift . sample
44−
45−instance MonadHold t m => MonadHold t (PostBuildT t m) where
46− {-# INLINABLE hold #-}
47− hold v0 = lift . hold v0
48− {-# INLINABLE holdDyn #-}
49− holdDyn v0 = lift . holdDyn v0
50− {-# INLINABLE holdIncremental #-}
51− holdIncremental v0 = lift . holdIncremental v0
52−
53−instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
54− type Performable (PostBuildT t m) = PostBuildT t (Performable m)
55− {-# INLINABLE performEvent_ #-}
56− performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
57− {-# INLINABLE performEvent #-}
58− performEvent e = liftWith $ \run -> performEvent $ fmap run e
59−
60−instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
61− {-# INLINABLE newEventWithTrigger #-}
62− newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
63− {-# INLINABLE newFanEventWithTrigger #-}
64− newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
65−
66−instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
67− {-# INLINABLE newTriggerEvent #-}
68− newTriggerEvent = lift newTriggerEvent
69− {-# INLINABLE newTriggerEventWithOnComplete #-}
70− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
71− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
72−
73−instance MonadRef m => MonadRef (PostBuildT t m) where
74− type Ref (PostBuildT t m) = Ref m
75− {-# INLINABLE newRef #-}
76− newRef = lift . newRef
77− {-# INLINABLE readRef #-}
78− readRef = lift . readRef
79− {-# INLINABLE writeRef #-}
80− writeRef r = lift . writeRef r
81−
82−instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
83− {-# INLINABLE atomicModifyRef #-}
84− atomicModifyRef r = lift . atomicModifyRef r
85−
86−{-# INLINABLE runPostBuildT #-}
87−runPostBuildT :: PostBuildT t m a -> Event t () -> m a
88−runPostBuildT (PostBuildT a) = runReaderT a
89−
90−instance PostBuild t m => PostBuild t (ReaderT r m) where
91− getPostBuild = lift getPostBuild
src/Reflex/Pure.hsdeleted−167
@@ -1,167 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE InstanceSigs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
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−-- | This module provides a pure implementation of Reflex, which is intended to
15−-- serve as a reference for the semantics of the Reflex class. All
16−-- implementations of Reflex should produce the same results as this
17−-- implementation, although performance and laziness/strictness may differ.
18−module Reflex.Pure where
19−
20−#if !MIN_VERSION_base(4,8,0)
21−import Control.Applicative -- Necessary for GHC 7.8
22−#endif
23−
24−import Control.Monad
25−import Data.Dependent.Map (DMap, GCompare)
26−import qualified Data.Dependent.Map as DMap
27−import Data.Functor.Identity
28−import Data.Maybe
29−import Data.MemoTrie
30−import Data.Monoid
31−import Reflex.Class
32−
33−data Pure t
34−
35−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
36−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
37−-- exclusively to memoize functions of t, not for any of its other capabilities.
38−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
39−
40− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
41− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
42− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
43− newtype Incremental (Pure t) p a = Incremental { unIncremental :: t -> (a, Maybe (p a)) }
44−
45− type PushM (Pure t) = (->) t
46− type PullM (Pure t) = (->) t
47−
48− never :: Event (Pure t) a
49− never = Event $ \_ -> Nothing
50−
51− constant :: a -> Behavior (Pure t) a
52− constant x = Behavior $ \_ -> x
53−
54− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
55− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
56−
57− pull :: PullM (Pure t) a -> Behavior (Pure t) a
58− pull = Behavior . memo
59−
60− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
61− -- warning because the GCompare instance is not used; however, removing the
62− -- GCompare instance produces a different warning, due to that constraint
63− -- being present in the original class definition
64−
65− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
66− merge events = Event $ memo $ \t ->
67− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
68− in if DMap.null currentOccurrences
69− then Nothing
70− else Just currentOccurrences
71−
72− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
73− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
74−
75− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
76− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
77−
78− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
79− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
80−
81− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
82− current d = Behavior $ \t -> fst $ unDynamic d t
83−
84− updated :: Dynamic (Pure t) a -> Event (Pure t) a
85− updated d = Event $ \t -> snd $ unDynamic d t
86−
87− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
88− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
89−
90− -- See UNUSED_CONSTRAINT, above.
91−
92− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
93− --a) -> Incremental (Pure t) p a
94− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
95−
96− mergeIncremental (Incremental f) = Event $ \t ->
97− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ f t
98− in if DMap.null results
99− then Nothing
100− else Just results
101−
102− currentIncremental (Incremental f) = Behavior $ \t -> fst $ f t
103−
104− updatedIncremental (Incremental f) = Event $ \t -> snd $ f t
105−
106− incrementalToDynamic (Incremental f) = Dynamic $ \t ->
107− let (old, mPatch) = f t
108− e = case mPatch of
109− Nothing -> Nothing
110− Just patch -> apply patch old
111− in (old, e)
112−
113−instance Functor (Dynamic (Pure t)) where
114− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
115− in (f cur, fmap f upd)
116−
117−instance Applicative (Dynamic (Pure t)) where
118− pure a = Dynamic $ \_ -> (a, Nothing)
119− (<*>) = ap
120−
121−instance Monad (Dynamic (Pure t)) where
122− return = pure
123− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
124− let (curX :: a, updX :: Maybe a) = unDynamic x t
125− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
126− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
127− Nothing -> (Nothing, Nothing)
128− Just nextX -> let (c, u) = unDynamic (f nextX) t
129− in (Just c, u)
130− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
131−
132−instance MonadSample (Pure t) ((->) t) where
133−
134− sample :: Behavior (Pure t) a -> (t -> a)
135− sample = unBehavior
136−
137−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
138−
139− hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
140− hold initialValue e initialTime = Behavior f
141− where f = memo $ \sampleTime ->
142− -- Really, the sampleTime should never be prior to the initialTime,
143− -- because that would mean the Behavior is being sampled before
144− -- being created.
145− if sampleTime <= initialTime
146− then initialValue
147− else let lastTime = pred sampleTime
148− in fromMaybe (f lastTime) $ unEvent e lastTime
149−
150− holdDyn :: a -> Event (Pure t) a -> t -> Dynamic (Pure t) a
151− holdDyn initialValue e initialTime =
152− let Behavior f = hold initialValue e initialTime
153− in Dynamic $ \t -> (f t, unEvent e t)
154−
155− holdIncremental :: Patch p => a -> Event (Pure t) (p a) -> t -> Incremental (Pure t) p a
156− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
157− where f = memo $ \sampleTime ->
158− -- Really, the sampleTime should never be prior to the initialTime,
159− -- because that would mean the Behavior is being sampled before
160− -- being created.
161− if sampleTime <= initialTime
162− then initialValue
163− else let lastTime = pred sampleTime
164− lastValue = f lastTime
165− in case unEvent e lastTime of
166− Nothing -> lastValue
167− Just x -> fromMaybe lastValue $ apply x lastValue
src/Reflex/Spider.hsdeleted−17
@@ -1,17 +0,0 @@
1−-- | This module exports all of the user-facing functionality of the 'Spider'
2−-- 'Reflex' engine
3−module Reflex.Spider
4− ( Spider
5− , SpiderTimeline
6− , Global
7− , SpiderHost
8− , runSpiderHost
9− , runSpiderHostForTimeline
10− , newSpiderTimeline
11− , withSpiderTimeline
12− , askSpiderTimeline
13− -- * Deprecated
14− , SpiderEnv
15− ) where
16−
17−import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hsdeleted−2289
@@ -1,2289 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ConstraintKinds #-}
4−{-# LANGUAGE EmptyDataDecls #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6−{-# LANGUAGE FlexibleContexts #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE GADTs #-}
10−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11−{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MagicHash #-}
13−{-# LANGUAGE MultiParamTypeClasses #-}
14−{-# LANGUAGE MultiWayIf #-}
15−{-# LANGUAGE RankNTypes #-}
16−{-# LANGUAGE RoleAnnotations #-}
17−{-# LANGUAGE ScopedTypeVariables #-}
18−{-# LANGUAGE TypeFamilies #-}
19−{-# LANGUAGE TypeOperators #-}
20−{-# LANGUAGE UndecidableInstances #-}
21−-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
22−-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
23−module Reflex.Spider.Internal where
24−
25−import Control.Applicative
26−import Control.Concurrent
27−import Control.Exception
28−import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
29−import Control.Monad.Exception
30−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
31−import Control.Monad.Primitive
32−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
33−import Control.Monad.Ref
34−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
35−import Data.Align
36−import Data.Coerce
37−import Data.Dependent.Map (DMap, DSum (..))
38−import qualified Data.Dependent.Map as DMap
39−import Data.Foldable hiding (concat, elem, sequence_)
40−import Data.Function
41−import Data.Functor.Compose
42−import Data.GADT.Compare
43−import Data.IntMap.Strict (IntMap)
44−import qualified Data.IntMap.Strict as IntMap
45−import Data.IORef
46−import Data.List hiding (foldl')
47−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
48−import qualified Data.List.NonEmpty as NonEmpty
49−import Data.Maybe
50−import Data.Monoid (mempty, (<>))
51−import Data.Some (Some)
52−import qualified Data.Some as Some
53−import Data.These
54−import Data.Traversable
55−import Data.Tree (Forest, Tree (..), drawForest)
56−import Data.Word
57−import GHC.IORef (IORef (..))
58−import GHC.Stack
59−import System.IO.Unsafe
60−import System.Mem.Weak
61−import Unsafe.Coerce
62−
63−import Data.WeakBag (WeakBag, WeakBagTicket)
64−import qualified Data.WeakBag as WeakBag
65−import qualified Reflex.Class as R
66−import qualified Reflex.Host.Class as R
67−
68−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
69−import Prelude hiding (any, concat, mapM, mapM_, sequence)
70−
71−type SubscriberList a = WeakBag (Subscriber a)
72−type SubscriberListTicket a = WeakBagTicket (Subscriber a)
73−
74−debugPropagate :: Bool
75−
76−debugInvalidateHeight :: Bool
77−
78−debugInvalidate :: Bool
79−
80−#ifdef DEBUG
81−
82−#define DEBUG_NODEIDS
83−
84−debugPropagate = True
85−
86−debugInvalidateHeight = False
87−
88−debugInvalidate = True
89−
90−class HasNodeId a where
91− getNodeId :: a -> Int
92−
93−instance HasNodeId (Hold p a) where
94− getNodeId = holdNodeId
95−
96−instance HasNodeId (PushSubscribed a b) where
97− getNodeId = pushSubscribedNodeId
98−
99−instance HasNodeId (SwitchSubscribed a) where
100− getNodeId = switchSubscribedNodeId
101−
102−instance HasNodeId (MergeSubscribed a) where
103− getNodeId = mergeSubscribedNodeId
104−
105−instance HasNodeId (FanSubscribed a) where
106− getNodeId = fanSubscribedNodeId
107−
108−instance HasNodeId (CoincidenceSubscribed a) where
109− getNodeId = coincidenceSubscribedNodeId
110−
111−instance HasNodeId (RootSubscribed a) where
112− getNodeId = rootSubscribedNodeId
113−
114−instance HasNodeId (Pull a) where
115− getNodeId = pullNodeId
116−
117−{-# INLINE showNodeId #-}
118−showNodeId :: HasNodeId a => a -> String
119−showNodeId = ("#"<>) . show . getNodeId
120−
121−showSubscriberType :: Subscriber a -> String
122−showSubscriberType = \case
123− SubscriberPush _ _ -> "SubscriberPush"
124− SubscriberMerge _ _ -> "SubscriberMerge"
125− SubscriberFan _ -> "SubscriberFan"
126− SubscriberMergeChange _ -> "SubscriberMergeChange"
127− SubscriberHold _ -> "SubscriberHold"
128− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
129− SubscriberSwitch _ -> "SubscriberSwitch"
130− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
131− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
132− SubscriberHandle -> "SubscriberHandle"
133−
134−showEventType :: Event a -> String
135−showEventType = \case
136− EventRoot _ _ -> "EventRoot"
137− EventNever -> "EventNever"
138− EventPush _ -> "EventPush"
139− EventMerge _ -> "EventMerge"
140− EventFan _ _ -> "EventFan"
141− EventSwitch _ -> "EventSwitch"
142− EventCoincidence _ -> "EventCoincidence"
143− EventHold _ -> "EventHold"
144− EventDyn _ -> "EventDyn"
145− EventHoldIdentity _ -> "EventHoldIdentity"
146− EventDynIdentity _ -> "EventDynIdentity"
147−
148−#else
149−
150−debugPropagate = False
151−
152−debugInvalidateHeight = False
153−
154−debugInvalidate = False
155−
156−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
157−{-# INLINE showNodeId #-}
158−showNodeId :: a -> String
159−showNodeId _ = ""
160−
161−#endif
162−
163−#ifdef DEBUG_NODEIDS
164−{-# NOINLINE nextNodeIdRef #-}
165−nextNodeIdRef :: IORef Int
166−nextNodeIdRef = unsafePerformIO $ newIORef 1
167−
168−newNodeId :: IO Int
169−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
170−
171−{-# NOINLINE unsafeNodeId #-}
172−unsafeNodeId :: a -> Int
173−unsafeNodeId a = unsafePerformIO $ do
174− touch a
175− newNodeId
176−#endif
177−
178−--------------------------------------------------------------------------------
179−-- Event
180−--------------------------------------------------------------------------------
181−
182−data Event a
183− = forall k. GCompare k => EventRoot !(k a) !(Root k)
184− | EventNever
185− | forall b. EventPush !(Push b a)
186− | forall k. (GCompare k, a ~ DMap k Identity) => EventMerge !(Merge k)
187− | forall k. GCompare k => EventFan !(k a) !(Fan k)
188− | EventSwitch !(Switch a)
189− | EventCoincidence !(Coincidence a)
190− | forall p b. (a ~ p b, R.Patch p) => EventHold !(Hold p b)
191− | forall p b. (a ~ p b, R.Patch p) => EventDyn !(Dyn p b)
192− | forall b. (a ~ Identity b) => EventHoldIdentity !(Hold Identity b)
193− | forall b. (a ~ Identity b) => EventDynIdentity !(Dyn Identity b)
194−
195−{-# SPECIALIZE subscribe :: Event a -> Subscriber a -> EventM x (EventSubscription a) #-}
196−subscribe :: forall x m a. CanSubscribe x m => Event a -> Subscriber a -> m (EventSubscription a)
197−subscribe e sub = case e of
198− EventRoot k r -> wrap EventSubscribedRoot $ liftIO . getRootSubscribed k r
199− EventNever -> return $ EventSubscription Nothing EventSubscribedNever
200− EventPush p -> wrap EventSubscribedPush $ getPushSubscribed p
201− EventFan k f -> wrap (EventSubscribedFan k) $ getFanSubscribed k f
202− EventMerge m -> wrap EventSubscribedMerge $ getMergeSubscribed m
203− EventSwitch s -> wrap EventSubscribedSwitch $ getSwitchSubscribed s
204− EventCoincidence c -> wrap EventSubscribedCoincidence $ getCoincidenceSubscribed c
205− EventHold h -> subscribeHoldEvent h sub
206− EventDyn j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
207− EventHoldIdentity h -> subscribeHoldEvent h sub
208− EventDynIdentity j -> getDynHold j >>= \h -> subscribeHoldEvent h sub
209− where
210− wrap :: (t -> EventSubscribed a) -> (Subscriber a -> m (SubscriberListTicket a, t)) -> m (EventSubscription a)
211− wrap tag getSpecificSubscribed = do
212− (sln, subd) <- getSpecificSubscribed sub
213− return $ EventSubscription (Just sln) $ tag subd
214−
215−{-# INLINE subscribeCoincidenceInner #-}
216−subscribeCoincidenceInner :: CanSubscribe x m => Event a -> Height -> CoincidenceSubscribed a -> m (Maybe a, Height, EventSubscribed a)
217−subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
218− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
219− subscription@(EventSubscription _ innerSubd) <- subscribe inner subInner
220− innerOcc <- liftIO $ readEventSubscribed innerSubd
221− innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
222− let height = max innerHeight outerHeight
223− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
224− return (innerOcc, height, innerSubd)
225−
226−--------------------------------------------------------------------------------
227−-- EventSubscribed
228−--------------------------------------------------------------------------------
229−
230−data EventSubscribed a
231− = EventSubscribedRoot {-# NOUNPACK #-} (RootSubscribed a)
232− | EventSubscribedNever
233− | forall b. EventSubscribedPush !(PushSubscribed b a)
234− | forall k. (GCompare k, a ~ DMap k Identity) => EventSubscribedMerge !(MergeSubscribed k)
235− | forall k. GCompare k => EventSubscribedFan !(k a) !(FanSubscribed k)
236− | EventSubscribedSwitch !(SwitchSubscribed a)
237− | EventSubscribedCoincidence !(CoincidenceSubscribed a)
238−
239−readEventSubscribed :: EventSubscribed a -> IO (Maybe a)
240−readEventSubscribed = \case
241− EventSubscribedRoot rs -> readRootSubscribed rs
242− EventSubscribedNever -> return Nothing
243− EventSubscribedPush ps -> readPushSubscribed ps
244− EventSubscribedMerge ms -> readMergeSubscribed ms
245− EventSubscribedFan k fs -> readFanSubscribed k fs
246− EventSubscribedSwitch ss -> readSwitchSubscribed ss
247− EventSubscribedCoincidence cs -> readCoincidenceSubscribed cs
248−
249−{-# INLINE subscribeEventSubscribed #-}
250−subscribeEventSubscribed :: EventSubscribed a -> Subscriber a -> IO (Maybe (SubscriberListTicket a))
251−subscribeEventSubscribed es sub = case es of
252− EventSubscribedRoot r -> do
253− traceSubscribe $ "subscribeEventSubscribed Root"
254− Just <$> subscribeRootSubscribed r sub
255− EventSubscribedNever -> do
256− traceSubscribe $ "subscribeEventSubscribed Never"
257− return Nothing
258− EventSubscribedPush subscribed -> do
259− traceSubscribe $ "subscribeEventSubscribed Push"
260− Just <$> subscribePushSubscribed subscribed sub
261− EventSubscribedFan k subscribed -> do
262− traceSubscribe $ "subscribeEventSubscribed Fan"
263− Just <$> subscribeFanSubscribed k subscribed sub
264− EventSubscribedMerge subscribed -> do
265− traceSubscribe $ "subscribeEventSubscribed Merge"
266− Just <$> subscribeMergeSubscribed subscribed sub
267− EventSubscribedSwitch subscribed -> do
268− traceSubscribe $ "subscribeEventSubscribed Switch"
269− Just <$> subscribeSwitchSubscribed subscribed sub
270− EventSubscribedCoincidence subscribed -> do
271− traceSubscribe $ "subscribeEventSubscribed Coincidence"
272− Just <$> subscribeCoincidenceSubscribed subscribed sub
273−
274−{-# INLINE eventSubscribedGetParents #-}
275−eventSubscribedGetParents :: EventSubscribed a -> IO [Some EventSubscribed]
276−eventSubscribedGetParents = \case
277− EventSubscribedRoot _ -> return []
278− EventSubscribedNever -> return []
279− EventSubscribedPush subscribed -> return [Some.This $ _eventSubscription_subscribed $ pushSubscribedParent subscribed]
280− EventSubscribedFan _ subscribed -> return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
281− EventSubscribedMerge subscribed -> do
282− ps <- readIORef $ mergeSubscribedParents subscribed
283− return $ R.ffor (DMap.toList ps) $ \(_ :=> v) -> Some.This $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription v
284− EventSubscribedSwitch subscribed -> do
285− s <- readIORef $ switchSubscribedCurrentParent subscribed
286− return [Some.This $ _eventSubscription_subscribed s]
287− EventSubscribedCoincidence subscribed -> do
288− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
289− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
290− innerParents = maybeToList $ fmap Some.This innerSubscription
291− return $ outerParent : innerParents
292−
293−getEventSubscribedHeight :: EventSubscribed a -> IO Height
294−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
295−
296−{-# INLINE eventSubscribedHeightRef #-}
297−eventSubscribedHeightRef :: EventSubscribed a -> IORef Height
298−eventSubscribedHeightRef = \case
299− EventSubscribedRoot _ -> zeroRef
300− EventSubscribedNever -> zeroRef
301− EventSubscribedPush subscribed -> pushSubscribedHeight subscribed
302− EventSubscribedFan _ subscribed -> eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
303− EventSubscribedMerge subscribed -> mergeSubscribedHeight subscribed
304− EventSubscribedSwitch subscribed -> switchSubscribedHeight subscribed
305− EventSubscribedCoincidence subscribed -> coincidenceSubscribedHeight subscribed
306−
307−whoCreatedEventSubscribed :: EventSubscribed a -> IO [String]
308−whoCreatedEventSubscribed = \case
309− EventSubscribedRoot _ -> return ["root"]
310− EventSubscribedNever -> return ["never"]
311− EventSubscribedPush subscribed -> whoCreatedIORef $ pushSubscribedCachedSubscribed subscribed
312− EventSubscribedFan _ subscribed -> whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
313− EventSubscribedMerge subscribed -> whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
314− EventSubscribedSwitch subscribed -> whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
315− EventSubscribedCoincidence subscribed -> whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
316−
317−{-# INLINE eventSubscribedHasOwnHeightRef #-}
318−eventSubscribedHasOwnHeightRef :: EventSubscribed a -> Bool
319−eventSubscribedHasOwnHeightRef = \case
320− EventSubscribedRoot _ -> False
321− EventSubscribedNever -> False
322− EventSubscribedPush _ -> False
323− EventSubscribedFan _ _ -> False
324− EventSubscribedMerge _ -> True
325− EventSubscribedSwitch _ -> True
326− EventSubscribedCoincidence _ -> True
327−
328−walkInvalidHeightParents :: EventSubscribed a -> IO [Some EventSubscribed]
329−walkInvalidHeightParents s0 = do
330− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
331− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
332− when (h == invalidHeight) $ do
333− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
334− modify (Some.This s :)
335− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
336− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
337− return subscribers
338−
339−--------------------------------------------------------------------------------
340−-- EventSubscription
341−--------------------------------------------------------------------------------
342−
343−data EventSubscription a = EventSubscription
344− { _eventSubscription_ticket :: !(Maybe (SubscriberListTicket a))
345− , _eventSubscription_subscribed :: !(EventSubscribed a)
346− }
347−
348−unsubscribe :: EventSubscription a -> IO ()
349−unsubscribe (EventSubscription msln _) = forM_ msln $ \sln -> WeakBag.remove sln
350−
351−--------------------------------------------------------------------------------
352−-- Combinators
353−--------------------------------------------------------------------------------
354−
355−--TODO: Figure out why certain things are not 'representational', then make them
356−--representational so we can use coerce
357−
358−--type role Hold representational
359−data Hold p a
360− = Hold { holdValue :: !(IORef a)
361− , holdInvalidators :: !(IORef [Weak Invalidator])
362− , 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
363−#ifdef DEBUG_NODEIDS
364− , holdNodeId :: Int
365−#endif
366− }
367−
368−data EventEnv x
369− = EventEnv { eventEnvSpiderTimeline :: !(SpiderTimeline x)
370− , eventEnvAssignments :: !(IORef [SomeAssignment]) -- Needed for Subscribe
371− , eventEnvHoldInits :: !(IORef [SomeHoldInit]) -- Needed for Subscribe
372− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate])
373− , eventEnvMergeInits :: !(IORef [SomeMergeInit]) -- Needed for Subscribe
374− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
375− , eventEnvRootClears :: !(IORef [SomeRootClear])
376− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
377− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence]) -- Needed for Subscribe
378− , eventEnvDelayedMerges :: !(IORef (IntMap [DelayedMerge]))
379− }
380−
381−{-# INLINE runEventM #-}
382−runEventM :: EventM x a -> EventEnv x -> IO a
383−runEventM = runReaderT . unEventM
384−
385−class MonadIO m => Defer a m where
386− getDeferralQueue :: m (IORef [a])
387−
388−{-# INLINE defer #-}
389−defer :: Defer a m => a -> m ()
390−defer a = do
391− q <- getDeferralQueue
392− liftIO $ modifyIORef' q (a:)
393−
394−instance Defer SomeAssignment (EventM x) where
395− {-# INLINE getDeferralQueue #-}
396− getDeferralQueue = EventM $ asks eventEnvAssignments
397−
398−instance Defer SomeHoldInit (EventM x) where
399− {-# INLINE getDeferralQueue #-}
400− getDeferralQueue = EventM $ asks eventEnvHoldInits
401−
402−instance Defer SomeHoldInit BehaviorM where
403− {-# INLINE getDeferralQueue #-}
404− getDeferralQueue = BehaviorM $ asks snd
405−
406−instance Defer SomeMergeUpdate (EventM x) where
407− {-# INLINE getDeferralQueue #-}
408− getDeferralQueue = EventM $ asks eventEnvMergeUpdates
409−
410−instance Defer SomeMergeInit (EventM x) where
411− {-# INLINE getDeferralQueue #-}
412− getDeferralQueue = EventM $ asks eventEnvMergeInits
413−
414−class HasCurrentHeight m where
415− getCurrentHeight :: m Height
416− scheduleMerge :: Height -> MergeSubscribed a -> m ()
417−
418−instance HasCurrentHeight (EventM x) where
419− {-# INLINE getCurrentHeight #-}
420− getCurrentHeight = EventM $ do
421− heightRef <- asks eventEnvCurrentHeight
422− liftIO $ readIORef heightRef
423− {-# INLINE scheduleMerge #-}
424− scheduleMerge height subscribed = EventM $ do
425− delayedRef <- asks eventEnvDelayedMerges
426− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [DelayedMerge subscribed]
427−
428−class HasSpiderTimeline x m | m -> x where
429− -- | Retrieve the current SpiderTimeline
430− askSpiderTimeline :: m (SpiderTimeline x)
431−
432−instance HasSpiderTimeline x (EventM x) where
433− {-# INLINE askSpiderTimeline #-}
434− askSpiderTimeline = EventM $ asks eventEnvSpiderTimeline
435−
436−instance HasSpiderTimeline x (SpiderHost x) where
437− {-# INLINE askSpiderTimeline #-}
438− askSpiderTimeline = SpiderHost ask
439−
440−putCurrentHeight :: Height -> EventM x ()
441−putCurrentHeight h = EventM $ do
442− heightRef <- asks eventEnvCurrentHeight
443− liftIO $ writeIORef heightRef $! h
444−
445−instance Defer SomeClear (EventM x) where
446− {-# INLINE getDeferralQueue #-}
447− getDeferralQueue = EventM $ asks eventEnvClears
448−
449−{-# INLINE scheduleClear #-}
450−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
451−scheduleClear r = defer $ SomeClear r
452−
453−instance Defer SomeRootClear (EventM x) where
454− {-# INLINE getDeferralQueue #-}
455− getDeferralQueue = EventM $ asks eventEnvRootClears
456−
457−{-# INLINE scheduleRootClear #-}
458−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
459−scheduleRootClear r = defer $ SomeRootClear r
460−
461−instance Defer SomeResetCoincidence (EventM x) where
462− {-# INLINE getDeferralQueue #-}
463− getDeferralQueue = EventM $ asks eventEnvResetCoincidences
464−
465−-- Note: hold cannot examine its event until after the phase is over
466−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> EventM x (Hold p a) #-}
467−{-# SPECIALIZE hold :: R.Patch p => a -> Event (p a) -> ComputeM (Hold p a) #-}
468−hold :: (R.Patch p, Defer SomeHoldInit m) => a -> Event (p a) -> m (Hold p a)
469−hold v0 e = do
470− valRef <- liftIO $ newIORef v0
471− invsRef <- liftIO $ newIORef []
472− parentRef <- liftIO $ newIORef $ Left e
473−#ifdef DEBUG_NODEIDS
474− nodeId <- liftIO newNodeId
475−#endif
476− let h = Hold
477− { holdValue = valRef
478− , holdInvalidators = invsRef
479− , holdParent = parentRef
480−#ifdef DEBUG_NODEIDS
481− , holdNodeId = nodeId
482−#endif
483− }
484− defer $ SomeHoldInit h
485− return h
486−
487−{-# INLINE getHoldEventSubscription #-}
488−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, HasSpiderTimeline x m) => Hold p a -> m (EventSubscription (p a))
489−getHoldEventSubscription h = do
490− ep <- liftIO $ readIORef $ holdParent h
491− case ep of
492− Right subd -> return subd
493− Left e -> do
494− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
495− subscription@(EventSubscription _ subd) <- subscribe e =<< liftIO (newSubscriberHold h)
496− liftIO $ writeIORef subscriptionRef $! subscription
497− occ <- liftIO $ readEventSubscribed subd
498− case occ of
499− Nothing -> return ()
500− Just o -> do
501− old <- liftIO $ readIORef $ holdValue h
502− case R.apply o old of
503− Nothing -> return ()
504− Just new -> do
505− -- Need to evaluate these so that we don't retain the Hold itself
506− v <- liftIO $ evaluate $ holdValue h
507− i <- liftIO $ evaluate $ holdInvalidators h
508− defer $ SomeAssignment v i new
509− liftIO $ writeIORef (holdParent h) $ Right subscription
510− return subscription
511−
512−type BehaviorEnv = (Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]), IORef [SomeHoldInit])
513−
514−--type role BehaviorM representational
515−-- BehaviorM can sample behaviors
516−newtype BehaviorM a = BehaviorM { unBehaviorM :: ReaderT BehaviorEnv IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
517−
518−instance Monad BehaviorM where
519− {-# INLINE (>>=) #-}
520− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
521− {-# INLINE (>>) #-}
522− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
523− {-# INLINE return #-}
524− return x = BehaviorM $ return x
525− {-# INLINE fail #-}
526− fail s = BehaviorM $ fail s
527−
528−data BehaviorSubscribed a
529− = forall p. BehaviorSubscribedHold (Hold p a)
530− | BehaviorSubscribedPull (PullSubscribed a)
531−
532−data SomeBehaviorSubscribed = forall a. SomeBehaviorSubscribed (BehaviorSubscribed a)
533−
534−--type role PullSubscribed representational
535−data PullSubscribed a
536− = PullSubscribed { pullSubscribedValue :: !a
537− , pullSubscribedInvalidators :: !(IORef [Weak Invalidator])
538− , pullSubscribedOwnInvalidator :: !Invalidator
539− , pullSubscribedParents :: ![SomeBehaviorSubscribed] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
540− }
541−
542−--type role Pull representational
543−data Pull a
544− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed a)))
545− , pullCompute :: !(BehaviorM a)
546−#ifdef DEBUG_NODEIDS
547− , pullNodeId :: Int
548−#endif
549− }
550−
551−data Invalidator
552− = forall a. InvalidatorPull (Pull a)
553− | forall a. InvalidatorSwitch (SwitchSubscribed a)
554−
555−data RootSubscribed a = forall k. GCompare k => RootSubscribed
556− { rootSubscribedKey :: !(k a)
557− , rootSubscribedCachedSubscribed :: !(IORef (DMap k RootSubscribed)) -- From the original Root
558− , rootSubscribedSubscribers :: !(SubscriberList a)
559− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
560− , rootSubscribedUninit :: IO ()
561− , 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?
562−#ifdef DEBUG_NODEIDS
563− , rootSubscribedNodeId :: Int
564−#endif
565− }
566−
567−data Root (k :: * -> *)
568− = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
569− , rootSubscribed :: !(IORef (DMap k RootSubscribed))
570− , rootInit :: !(forall a. k a -> RootTrigger a -> IO (IO ()))
571− }
572−
573−data SomeHoldInit = forall p a. R.Patch p => SomeHoldInit !(Hold p a)
574−
575−data SomeMergeUpdate = forall k. GCompare k => SomeMergeUpdate (R.PatchDMap (DMap k Event)) (MergeSubscribed k)
576−
577−data SomeMergeInit = forall k. GCompare k => SomeMergeInit !(MergeSubscribed k) !(Event (R.PatchDMap (DMap k Event)))
578−
579−data SomeEventSubscription = forall a. SomeEventSubscription (EventSubscription a)
580−
581−-- EventM can do everything BehaviorM can, plus create holds
582−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
583−
584−data PushSubscribed a b
585− = PushSubscribed { pushSubscribedCachedSubscribed :: !(IORef (Maybe (PushSubscribed a b)))
586− , 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)
587− , pushSubscribedHeight :: !(IORef Height)
588− , pushSubscribedSubscribers :: !(SubscriberList b)
589− , pushSubscribedSelf :: !(Subscriber a) -- Hold this in memory to ensure our WeakReferences don't die --TODO: Probably unneeded now
590− , pushSubscribedWeakSelf :: !(IORef (Weak (PushSubscribed a b)))
591− , pushSubscribedParent :: !(EventSubscription a)
592−#ifdef DEBUG_NODEIDS
593− , pushSubscribedNodeId :: Int
594−#endif
595− }
596−
597−newtype ComputeM a = ComputeM (ReaderT (IORef [SomeHoldInit]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
598−
599−instance Defer SomeHoldInit ComputeM where
600− getDeferralQueue = ComputeM ask
601−
602−{-# SPECIALIZE runComputeM :: ComputeM a -> EventM x a #-}
603−runComputeM :: Defer SomeHoldInit m => ComputeM a -> m a
604−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
605−
606−data Push a b
607− = 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?)
608− , pushParent :: !(Event a)
609− , pushSubscribed :: !(IORef (Maybe (PushSubscribed a b))) --TODO: Can we replace this with an unsafePerformIO thunk?
610− }
611−
612−data MergeSubscribedParent k a = MergeSubscribedParent !(EventSubscription a) !(Subscriber a)
613−
614−mergeSubscribedParentSubscription :: MergeSubscribedParent k a -> EventSubscription a
615−mergeSubscribedParentSubscription (MergeSubscribedParent es _) = es
616−
617−data HeightBag = HeightBag
618− { _heightBag_size :: {-# UNPACK #-} !Int
619− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
620− }
621− deriving (Show, Read, Eq, Ord)
622−
623−heightBagEmpty :: HeightBag
624−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
625−
626−heightBagSize :: HeightBag -> Int
627−heightBagSize = _heightBag_size
628−
629−heightBagFromList :: [Height] -> HeightBag
630−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
631−
632−heightBagAdd :: Height -> HeightBag -> HeightBag
633−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
634−
635−heightBagRemove :: Height -> HeightBag -> HeightBag
636−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
637− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
638− Just old -> HeightBag (pred s) $ case old of
639− 0 -> IntMap.delete h c
640− _ -> IntMap.insert h (pred old) c
641−
642−heightBagMax :: HeightBag -> Height
643−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
644− Just ((h, _), _) -> Height h
645− Nothing -> zeroHeight
646−
647−heightBagVerify :: HeightBag -> HeightBag
648−#ifdef DEBUG
649−heightBagVerify b@(HeightBag s c) = if
650− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
651− -> error $ "heightBagVerify: size doesn't match: " <> show b
652− | unHeight invalidHeight `IntMap.member` c
653− -> error $ "heightBagVerify: contains invalid height: " <> show b
654− | otherwise -> b
655−#else
656−heightBagVerify = id
657−#endif
658−
659−data MergeSubscribed k
660− = MergeSubscribed { mergeSubscribedCachedSubscribed :: !(IORef (Maybe (MergeSubscribed k))) -- From the original merge
661− , mergeSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
662− , mergeSubscribedAccum :: !(IORef (DMap k Identity)) -- This will accumulate occurrences until our height is reached, at which point it will be transferred to mergeSubscribedOccurrence
663− , mergeSubscribedHeight :: !(IORef Height)
664− , mergeSubscribedHeightBag :: !(IORef HeightBag)
665− , mergeSubscribedSubscribers :: !(SubscriberList (DMap k Identity))
666− , mergeSubscribedChange :: !(IORef (Subscriber (R.PatchDMap (DMap k Event)), EventSubscribed (R.PatchDMap (DMap k Event))))
667− , mergeSubscribedParents :: !(IORef (DMap k (MergeSubscribedParent k)))
668− , mergeSubscribedWeakSelf :: !(IORef (Weak (MergeSubscribed k)))
669−#ifdef DEBUG_NODEIDS
670− , mergeSubscribedNodeId :: Int
671−#endif
672− }
673−
674−--TODO: DMap sucks; we should really write something better (with a functor for the value as well as the key)
675−data Merge k
676− = Merge { mergeParents :: !(Dynamic R.PatchDMap (DMap k Event))
677− , mergeSubscribed :: !(IORef (Maybe (MergeSubscribed k)))
678− }
679−
680−data FanSubscribedChildren k a = FanSubscribedChildren
681− { _fanSubscribedChildren_list :: !(SubscriberList a)
682− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed k)
683− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed k)))
684− }
685−
686−data FanSubscribed k
687− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed k)))
688− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren k))) -- This DMap should never be empty
689− , fanSubscribedParent :: !(EventSubscription (DMap k Identity))
690−#ifdef DEBUG_NODEIDS
691− , fanSubscribedNodeId :: Int
692−#endif
693− }
694−
695−data Fan k
696− = Fan { fanParent :: !(Event (DMap k Identity))
697− , fanSubscribed :: !(IORef (Maybe (FanSubscribed k)))
698− }
699−
700−data SwitchSubscribed a
701− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
702− , switchSubscribedOccurrence :: !(IORef (Maybe a))
703− , switchSubscribedHeight :: !(IORef Height)
704− , switchSubscribedSubscribers :: !(SubscriberList a)
705− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !Invalidator
706− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak Invalidator))
707− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed])
708− , switchSubscribedParent :: !(Behavior (Event a))
709− , switchSubscribedCurrentParent :: !(IORef (EventSubscription a))
710− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed a)))
711−#ifdef DEBUG_NODEIDS
712− , switchSubscribedNodeId :: Int
713−#endif
714− }
715−
716−data Switch a
717− = Switch { switchParent :: !(Behavior (Event a))
718− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed a)))
719− }
720−
721−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
722−data CoincidenceSubscribed a
723− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
724− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
725− , coincidenceSubscribedSubscribers :: !(SubscriberList a)
726− , coincidenceSubscribedHeight :: !(IORef Height)
727− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber (Event a))
728− , coincidenceSubscribedOuterParent :: !(EventSubscription (Event a))
729− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed a)))
730− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed a)))
731−#ifdef DEBUG_NODEIDS
732− , coincidenceSubscribedNodeId :: Int
733−#endif
734− }
735−
736−data Coincidence a
737− = Coincidence { coincidenceParent :: !(Event (Event a))
738− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed a)))
739− }
740−
741−data Subscriber a
742− = forall b. SubscriberPush (a -> ComputeM (Maybe b)) (PushSubscribed a b) --TODO: Why does this have the pushCompute in it?
743− | forall k. GCompare k => SubscriberMerge !(k a) (MergeSubscribed k) --TODO: Can we inline the GCompare?
744− | forall k. (a ~ R.PatchDMap (DMap k Event), GCompare k) => SubscriberMergeChange (MergeSubscribed k)
745− | forall k. (GCompare k, a ~ DMap k Identity) => SubscriberFan (FanSubscribed k)
746− | forall p b. (a ~ p b, R.Patch p) => SubscriberHold !(Hold p b)
747− | forall b. (a ~ Identity b) => SubscriberHoldIdentity !(Hold Identity b)
748− | SubscriberSwitch (SwitchSubscribed a)
749− | forall b. a ~ Event b => SubscriberCoincidenceOuter (CoincidenceSubscribed b)
750− | SubscriberCoincidenceInner (CoincidenceSubscribed a)
751− | SubscriberHandle
752−
753−-- These function are constructor functions that are marked NOINLINE so they are
754−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
755−-- so any weak references that are attached to the constructors will have their
756−-- finalizer run. Using the opaque constructor, does not see the
757−-- constructor application, so it behaves like an IORef and cannot be fused away.
758−--
759−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
760−-- the weak pointer to it in some cases.
761−
762−--TODO: I think all of these noinlines are now unnecessary
763−{-# NOINLINE newSubscriberPush #-}
764−newSubscriberPush :: (a -> ComputeM (Maybe b)) -> PushSubscribed a b -> IO (Subscriber a)
765−newSubscriberPush compute subd = return $! SubscriberPush compute subd
766−
767−{-# RULES
768− "newSubscriberHold/Identity" forall h. newSubscriberHold h = newSubscriberHoldIdentity h
769− #-}
770−{-# NOINLINE newSubscriberHold #-}
771−newSubscriberHold :: R.Patch p => Hold p a -> IO (Subscriber (p a))
772−newSubscriberHold h = return $! SubscriberHold h
773−
774−{-# NOINLINE newSubscriberHoldIdentity #-}
775−newSubscriberHoldIdentity :: Hold Identity a -> IO (Subscriber (Identity a))
776−newSubscriberHoldIdentity h = return $! SubscriberHoldIdentity h
777−
778−{-# NOINLINE newSubscriberMerge #-}
779−newSubscriberMerge :: GCompare k => k a -> MergeSubscribed k -> IO (Subscriber a)
780−newSubscriberMerge k subd = return $! SubscriberMerge k subd
781−
782−{-# NOINLINE newSubscriberMergeChange #-}
783−newSubscriberMergeChange :: GCompare k => MergeSubscribed k -> IO (Subscriber (R.PatchDMap (DMap k Event)))
784−newSubscriberMergeChange subd = return $! SubscriberMergeChange subd
785−
786−{-# NOINLINE newSubscriberFan #-}
787−newSubscriberFan :: GCompare k => FanSubscribed k -> IO (Subscriber (DMap k Identity))
788−newSubscriberFan subd = return $! SubscriberFan subd
789−
790−{-# NOINLINE newSubscriberSwitch #-}
791−newSubscriberSwitch :: SwitchSubscribed a -> IO (Subscriber a)
792−newSubscriberSwitch subd = return $! SubscriberSwitch subd
793−
794−{-# NOINLINE newSubscriberCoincidenceOuter #-}
795−newSubscriberCoincidenceOuter :: CoincidenceSubscribed b -> IO (Subscriber (Event b))
796−newSubscriberCoincidenceOuter subd = return $! SubscriberCoincidenceOuter subd
797−
798−{-# NOINLINE newSubscriberCoincidenceInner #-}
799−newSubscriberCoincidenceInner :: CoincidenceSubscribed a -> IO (Subscriber a)
800−newSubscriberCoincidenceInner subd = return $! SubscriberCoincidenceInner subd
801−
802−{-# NOINLINE newInvalidatorSwitch #-}
803−newInvalidatorSwitch :: SwitchSubscribed a -> IO Invalidator
804−newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
805−
806−{-# NOINLINE newInvalidatorPull #-}
807−newInvalidatorPull :: Pull a -> IO Invalidator
808−newInvalidatorPull p = return $! InvalidatorPull p
809−
810−data Dynamic p a
811− = DynamicHold !(Hold p a)
812− | p ~ Identity => DynamicHoldIdentity !(Hold Identity a)
813− | DynamicConst !a
814− | DynamicDyn !(Dyn p a)
815− | p ~ Identity => DynamicDynIdentity !(Dyn Identity a)
816−
817−{-# INLINE current #-}
818−current :: R.Patch p => Dynamic p a -> Behavior a
819−current = \case
820− DynamicHold h -> BehaviorHold h
821− DynamicHoldIdentity h -> BehaviorHoldIdentity h
822− DynamicConst a -> BehaviorConst a
823− DynamicDyn d -> BehaviorDyn d
824− DynamicDynIdentity d -> BehaviorDynIdentity d
825−
826−--TODO: If you only need updated, not current, can we avoid actually constructing the Hold?
827−{-# INLINE updated #-}
828−updated :: R.Patch p => Dynamic p a -> Event (p a)
829−updated = \case
830− DynamicHold h -> EventHold h
831− DynamicHoldIdentity h -> EventHoldIdentity h
832− DynamicConst _ -> EventNever
833− DynamicDyn d -> EventDyn d
834− DynamicDynIdentity d -> EventDynIdentity d
835−
836−newMapDyn :: (a -> b) -> Dynamic Identity a -> Dynamic Identity b
837−newMapDyn f d = DynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ current d) (Identity . f . runIdentity <$> updated d)
838−
839−instance Functor (Dynamic Identity) where
840− fmap = newMapDyn
841−
842−instance Applicative (Dynamic Identity) where
843− pure = DynamicConst
844− (<*>) = zipDynWith ($)
845− _ *> b = b
846− a <* _ = a
847−
848−instance R.FunctorMaybe Event where
849− fmapMaybe f = push $ return . f
850−
851−instance Align Event where
852− nil = EventNever
853− align ea eb = R.fmapMaybe R.dmapToThese $ merge $ DynamicConst $ DMap.fromList [R.LeftTag :=> ea, R.RightTag :=> eb]
854−
855−--TODO: Avoid the duplication between this and R.zipDynWith
856−zipDynWith :: (a -> b -> c) -> Dynamic Identity a -> Dynamic Identity b -> Dynamic Identity c
857−zipDynWith f da db =
858− let eab = align (updated da) (updated db)
859− ec = flip push eab $ \o -> do
860− (a, b) <- case o of
861− This (Identity a) -> do
862− b <- readBehaviorUntracked $ current db
863− return (a, b)
864− That (Identity b) -> do
865− a <- readBehaviorUntracked $ current da
866− return (a, b)
867− These (Identity a) (Identity b) -> return (a, b)
868− return $ Just $ Identity $ f a b
869− in DynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (current da) <*> readBehaviorUntracked (current db)) ec
870−
871−instance Monad (Dynamic Identity) where
872− {-# INLINE return #-}
873− return = pure
874− {-# INLINE (>>=) #-}
875− x >>= f = DynamicDynIdentity $ newJoinDyn $ newMapDyn f x --TODO: (>>), fail
876− {-# INLINE (>>) #-}
877− _ >> y = y
878− {-# INLINE fail #-}
879− fail _ = error "Dynamic does not support 'fail'"
880−
881−newtype Dyn p a = Dyn { unDyn :: IORef (Either (BehaviorM a, Event (p a)) (Hold p a)) }
882−
883−unsafeDyn :: BehaviorM a -> Event (p a) -> Dyn p a
884−unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
885− where x = (readV0, v')
886−
887−newJoinDyn :: Dynamic Identity (Dynamic Identity a) -> Dyn Identity a
888−newJoinDyn d =
889− let readV0 = readBehaviorTracked . current =<< readBehaviorTracked (current d)
890− eOuter = push (fmap (Just . Identity) . readBehaviorUntracked . current . runIdentity) $ updated d
891− eInner = switch $ updated <$> current d
892− eBoth = coincidence $ updated . runIdentity <$> updated d
893− v' = unSpiderEvent $ R.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
894− in unsafeDyn readV0 v'
895−
896−--type role Behavior representational
897−data Behavior a
898− = forall p. BehaviorHold !(Hold p a)
899− | BehaviorHoldIdentity !(Hold Identity a)
900− | BehaviorConst !a
901− | BehaviorPull !(Pull a)
902− | BehaviorDynIdentity !(Dyn Identity a)
903− | forall p. R.Patch p => BehaviorDyn !(Dyn p a)
904−
905−-- ResultM can read behaviors and events
906−type ResultM = EventM
907−
908−{-# NOINLINE unsafeNewIORef #-}
909−unsafeNewIORef :: a -> b -> IORef b
910−unsafeNewIORef _ b = unsafePerformIO $ newIORef b
911−
912−instance Functor Event where
913− fmap f = push $ return . Just . f
914−
915−instance Functor Behavior where
916− fmap f = pull . fmap f . readBehaviorTracked
917−
918−{-# NOINLINE push #-} --TODO: If this is helpful, we can get rid of the unsafeNewIORef and use unsafePerformIO directly
919−push :: forall a b. (a -> ComputeM (Maybe b)) -> Event a -> Event b
920−push f e = EventPush $ Push
921− { pushCompute = f
922− , pushParent = e
923− , pushSubscribed = unsafeNewIORef (f, e) Nothing --TODO: Does the use of the tuple here create unnecessary overhead?
924− }
925−-- DISABLED: {- RULES "push/push" forall f g e. push f (push g e) = push (maybe (return Nothing) f <=< g) e #-}
926−
927−{-# NOINLINE pull #-}
928−pull :: BehaviorM a -> Behavior a
929−pull a = BehaviorPull $ Pull
930− { pullCompute = a
931− , pullValue = unsafeNewIORef a Nothing
932−#ifdef DEBUG_NODEIDS
933− , pullNodeId = unsafeNodeId a
934−#endif
935− }
936−
937−{-# NOINLINE switch #-}
938−switch :: Behavior (Event a) -> Event a
939−switch a = EventSwitch $ Switch
940− { switchParent = a
941− , switchSubscribed = unsafeNewIORef a Nothing
942− }
943−
944−coincidence :: Event (Event a) -> Event a
945−coincidence a = EventCoincidence $ Coincidence
946− { coincidenceParent = a
947− , coincidenceSubscribed = unsafeNewIORef a Nothing
948− }
949−
950−propagateAndUpdateSubscribersRef :: SubscriberList a -> a -> EventM x ()
951−propagateAndUpdateSubscribersRef subscribersRef a = do
952− propagate a subscribersRef
953−
954−-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
955−run :: [DSum RootTrigger Identity] -> ResultM x b -> SpiderHost x b
956−run roots after = do
957− tracePropagate $ "Running an event frame with " <> show (length roots) <> " events"
958− spiderEnv <- SpiderHost ask
959− result <- SpiderHost $ lift $ withMVar (_spiderEnv_lock spiderEnv) $ \_ -> flip runReaderT spiderEnv $ unSpiderHost $ runFrame $ do
960− rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
961− occBefore <- liftIO $ do
962− occBefore <- readIORef occRef
963− writeIORef occRef $! DMap.insert k a occBefore
964− return occBefore
965− if DMap.null occBefore
966− then do scheduleRootClear occRef
967− return $ Just r
968− else return Nothing
969− forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
970− propagateAndUpdateSubscribersRef subscribersRef a
971− delayedRef <- EventM $ asks eventEnvDelayedMerges
972− let go = do
973− delayed <- liftIO $ readIORef delayedRef
974− case IntMap.minViewWithKey delayed of
975− Nothing -> return ()
976− Just ((currentHeight, cur), future) -> do
977− tracePropagate $ "Running height " ++ show currentHeight
978− putCurrentHeight $ Height currentHeight
979− liftIO $ writeIORef delayedRef $! future
980− forM_ cur $ \d -> case d of
981− DelayedMerge subscribed -> do
982− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
983− case height `compare` Height currentHeight of
984− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
985− GT -> scheduleMerge height subscribed -- The height has been increased (by a coincidence event; TODO: is this the only way?)
986− EQ -> do
987− m <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
988− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! DMap.empty
989− --TODO: Assert that m is not empty
990− liftIO $ writeIORef (mergeSubscribedOccurrence subscribed) $ Just m
991− scheduleClear $ mergeSubscribedOccurrence subscribed
992− propagateAndUpdateSubscribersRef (mergeSubscribedSubscribers subscribed) m
993− go
994− go
995− putCurrentHeight maxBound
996− after
997− tracePropagate "Done running an event frame"
998− return result
999−
1000−data SomeClear = forall a. SomeClear (IORef (Maybe a))
1001−
1002−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1003−
1004−data SomeAssignment = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak Invalidator]) a
1005−
1006−data DelayedMerge = forall k. DelayedMerge (MergeSubscribed k)
1007−
1008−debugFinalize :: Bool
1009−debugFinalize = False
1010−
1011−mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1012−mkWeakPtrWithDebug x debugNote = do
1013− x' <- evaluate x
1014− mkWeakPtr x' $
1015− if debugFinalize
1016− then Just $ putStrLn $ "finalizing: " ++ debugNote
1017− else Nothing
1018−
1019−type WeakList a = [Weak a]
1020−
1021−{-# INLINE withIncreasedDepth #-}
1022−#ifdef DEBUG
1023−withIncreasedDepth :: CanTrace x m => m a -> m a
1024−withIncreasedDepth a = do
1025− spiderEnv <- askSpiderTimeline
1026− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) succ
1027− result <- a
1028− liftIO $ modifyIORef' (_spiderEnv_depth spiderEnv) pred
1029− return result
1030−#else
1031−withIncreasedDepth :: m a -> m a
1032−withIncreasedDepth = id
1033−#endif
1034−
1035−type CanTrace x m = (HasSpiderTimeline x m, MonadIO m)
1036−
1037−{-# INLINE tracePropagate #-}
1038−tracePropagate :: (CanTrace x m) => String -> m ()
1039−tracePropagate = traceWhen debugPropagate
1040−
1041−{-# INLINE traceSubscribe #-}
1042−traceSubscribe :: String -> IO ()
1043−traceSubscribe = when debugSubscribe . liftIO . putStrLn
1044−
1045−{-# INLINE traceInvalidate #-}
1046−traceInvalidate :: String -> IO ()
1047−traceInvalidate = when debugInvalidate . liftIO . putStrLn
1048−
1049−{-# INLINE traceWhen #-}
1050−traceWhen :: (CanTrace x m) => Bool -> String -> m ()
1051−traceWhen b message = traceMWhen b $ return message
1052−
1053−{-# INLINE traceMWhen #-}
1054−traceMWhen :: (CanTrace x m) => Bool -> m String -> m ()
1055−traceMWhen b getMessage = when b $ do
1056− message <- getMessage
1057−#ifdef DEBUG
1058− spiderEnv <- askSpiderTimeline
1059− d <- liftIO $ readIORef $ _spiderEnv_depth spiderEnv
1060−#else
1061− let d = 0
1062−#endif
1063− liftIO $ putStrLn $ replicate d ' ' <> message
1064−
1065−whoCreatedIORef :: IORef a -> IO [String]
1066−whoCreatedIORef (IORef a) = whoCreated $! a
1067−
1068−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1069−groupByHead = \case
1070− [] -> []
1071− (x :| xs) : t -> case groupByHead t of
1072− [] -> [(x, xs :| [])]
1073− l@((y, yss) : t')
1074− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
1075− | otherwise -> (x, xs :| []) : l
1076−
1077−listsToForest :: Eq a => [[a]] -> Forest a
1078−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1079−
1080−-- | Propagate everything at the current height
1081−propagate :: a -> SubscriberList a -> EventM x ()
1082−propagate a subscribers = withIncreasedDepth $ do
1083− -- 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
1084− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
1085− WeakBag.traverse subscribers $ \s -> case s of
1086− SubscriberPush compute subscribed -> {-# SCC "traversePush" #-} do
1087− tracePropagate $ "SubscriberPush" <> showNodeId subscribed
1088− occ <- runComputeM $ compute a
1089− case occ of
1090− Nothing -> return () -- No need to write a Nothing back into the Ref
1091− Just o -> do
1092− liftIO $ writeIORef (pushSubscribedOccurrence subscribed) occ
1093− scheduleClear $ pushSubscribedOccurrence subscribed
1094− propagate o $ pushSubscribedSubscribers subscribed
1095− SubscriberMerge k subscribed -> {-# SCC "traverseMerge" #-} do
1096− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
1097− oldM <- liftIO $ readIORef $ mergeSubscribedAccum subscribed
1098− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge" <> showNodeId subscribed) k (Identity a) oldM
1099− tracePropagate $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1100− liftIO $ writeIORef (mergeSubscribedAccum subscribed) $! newM
1101− when (DMap.null oldM) $ do -- Only schedule the firing once
1102− height <- liftIO $ readIORef $ mergeSubscribedHeight subscribed
1103− --TODO: assertions about height
1104− currentHeight <- getCurrentHeight
1105− when (height <= currentHeight) $ do
1106− myStack <- liftIO $ whoCreatedIORef $ mergeSubscribedCachedSubscribed subscribed
1107− if height /= invalidHeight
1108− then error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1109− else liftIO $ do
1110− nodesInvolvedInCycle <- walkInvalidHeightParents $ EventSubscribedMerge subscribed
1111− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1112− error $ "Causality loop found:\n" <> drawForest (listsToForest stacks)
1113− scheduleMerge height subscribed
1114− SubscriberMergeChange subscribed -> {-# SCC "traverseMergeChange" #-} do
1115− tracePropagate $ "SubscriberMerge" <> showNodeId subscribed
1116− defer $ SomeMergeUpdate a subscribed
1117− SubscriberFan subscribed -> {-# SCC "traverseFan" #-} do
1118− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
1119− tracePropagate $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
1120− --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
1121− forM_ (DMap.toList a) $ \(k :=> Identity v) -> {-# SCC "SubscriberFan" #-} case DMap.lookup k subs of
1122− Nothing -> do
1123− tracePropagate "No subscriber for key"
1124− return ()
1125− Just subsubs -> do
1126− propagate v $ _fanSubscribedChildren_list subsubs
1127− return ()
1128− SubscriberHold h -> {-# SCC "traverseHold" #-} propagateSubscriberHold h a
1129− SubscriberHoldIdentity h -> {-# SCC "traverseHoldIdentity" #-} propagateSubscriberHold h a
1130− SubscriberSwitch subscribed -> {-# SCC "traverseSwitch" #-} do
1131− tracePropagate $ "SubscriberSwitch" <> showNodeId subscribed
1132− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
1133− scheduleClear $ switchSubscribedOccurrence subscribed
1134− propagate a $ switchSubscribedSubscribers subscribed
1135− SubscriberCoincidenceOuter subscribed -> {-# SCC "traverseCoincidenceOuter" #-} do
1136− tracePropagate $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
1137− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
1138− tracePropagate $ " outerHeight = " <> show outerHeight
1139− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
1140− tracePropagate $ " isJust occ = " <> show (isJust occ)
1141− tracePropagate $ " innerHeight = " <> show innerHeight
1142− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
1143− scheduleClear $ coincidenceSubscribedInnerParent subscribed
1144− case occ of
1145− Nothing -> do
1146− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
1147− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
1148− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
1149− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
1150− Just o -> do -- Since it's already firing, no need to adjust height
1151− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
1152− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1153− propagate o $ coincidenceSubscribedSubscribers subscribed
1154− SubscriberCoincidenceInner subscribed -> {-# SCC "traverseCoincidenceInner" #-} do
1155− tracePropagate $ "SubscriberCoincidenceInner" <> showNodeId subscribed
1156− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
1157− case occ of
1158− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
1159− Nothing -> do
1160− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
1161− scheduleClear $ coincidenceSubscribedOccurrence subscribed
1162− propagate a $ coincidenceSubscribedSubscribers subscribed
1163− SubscriberHandle -> {-# SCC "traverseHandle" #-} tracePropagate "SubscriberHandle"
1164−
1165−{-# INLINE propagateSubscriberHold #-}
1166−propagateSubscriberHold :: R.Patch p => Hold p a -> p a -> EventM x ()
1167−propagateSubscriberHold h a = do
1168− {-# SCC "trace" #-} traceMWhen debugPropagate $ liftIO $ do
1169− invalidators <- liftIO $ readIORef $ holdInvalidators h
1170− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1171− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1172− case {-# SCC "apply" #-} R.apply a v of
1173− Nothing -> return ()
1174− Just v' -> do
1175− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1176− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1177− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1178− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1179−
1180−data SomeResetCoincidence = forall a. SomeResetCoincidence !(EventSubscription a) !(Maybe (CoincidenceSubscribed a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1181−
1182−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> BehaviorM a #-}
1183−{-# SPECIALIZE readBehaviorUntracked :: Behavior a -> EventM x a #-}
1184−readBehaviorUntracked :: Defer SomeHoldInit m => Behavior a -> m a
1185−readBehaviorUntracked b = do
1186− holdInits <- getDeferralQueue
1187− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
1188−
1189−runBehaviorM :: BehaviorM a -> Maybe (Weak Invalidator, IORef [SomeBehaviorSubscribed]) -> IORef [SomeHoldInit] -> IO a
1190−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1191−
1192−askInvalidator :: BehaviorM (Maybe (Weak Invalidator))
1193−askInvalidator = do
1194− (!m, _) <- BehaviorM ask
1195− case m of
1196− Nothing -> return Nothing
1197− Just (!wi, _) -> return $ Just wi
1198−
1199−askParentsRef :: BehaviorM (Maybe (IORef [SomeBehaviorSubscribed]))
1200−askParentsRef = do
1201− (!m, _) <- BehaviorM ask
1202− case m of
1203− Nothing -> return Nothing
1204− Just (_, !p) -> return $ Just p
1205−
1206−askBehaviorHoldInits :: BehaviorM (IORef [SomeHoldInit])
1207−askBehaviorHoldInits = do
1208− (_, !his) <- BehaviorM ask
1209− return his
1210−
1211−readBehaviorTracked :: Behavior a -> BehaviorM a
1212−readBehaviorTracked b = case b of
1213− BehaviorHold h -> readHoldTracked h
1214− BehaviorHoldIdentity h -> readHoldTracked h
1215− BehaviorConst a -> return a
1216− BehaviorPull p -> do
1217− val <- liftIO $ readIORef $ pullValue p
1218− case val of
1219− Just subscribed -> do
1220− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1221− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
1222− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
1223− return $ pullSubscribedValue subscribed
1224− Nothing -> do
1225− i <- liftIO $ newInvalidatorPull p
1226− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
1227− parentsRef <- liftIO $ newIORef []
1228− holdInits <- askBehaviorHoldInits
1229− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
1230− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
1231− parents <- liftIO $ readIORef parentsRef
1232− let subscribed = PullSubscribed
1233− { pullSubscribedValue = a
1234− , pullSubscribedInvalidators = invsRef
1235− , pullSubscribedOwnInvalidator = i
1236− , pullSubscribedParents = parents
1237− }
1238− liftIO $ writeIORef (pullValue p) $ Just subscribed
1239− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
1240− return a
1241− BehaviorDyn d -> readHoldTracked =<< getDynHold d
1242− BehaviorDynIdentity d -> readHoldTracked =<< getDynHold d
1243−
1244−{-# INLINE readHoldTracked #-}
1245−readHoldTracked :: Hold p a -> BehaviorM a
1246−readHoldTracked h = do
1247− result <- liftIO $ readIORef $ holdValue h
1248− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
1249− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
1250− return result
1251−
1252−{-# INLINE getDynHold #-}
1253−getDynHold :: (Defer SomeHoldInit m, R.Patch p) => Dyn p a -> m (Hold p a)
1254−getDynHold d = do
1255− mh <- liftIO $ readIORef $ unDyn d
1256− case mh of
1257− Right h -> return h
1258− Left (readV0, v') -> do
1259− holdInits <- getDeferralQueue
1260− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1261− h <- hold v0 v'
1262− liftIO $ writeIORef (unDyn d) $ Right h
1263− return h
1264−
1265−readRootSubscribed :: RootSubscribed a -> IO (Maybe a)
1266−readRootSubscribed = rootSubscribedOccurrence
1267−
1268−readPushSubscribed :: PushSubscribed b a -> IO (Maybe a)
1269−readPushSubscribed subscribed = do
1270− result <- readIORef $ pushSubscribedOccurrence subscribed -- Since ResultM is always called after the final height is reached, this will always be valid
1271− touch $ pushSubscribedSelf subscribed
1272− return result
1273−
1274−readMergeSubscribed :: MergeSubscribed k -> IO (Maybe (DMap k Identity))
1275−readMergeSubscribed subscribed = do
1276− result <- readIORef $ mergeSubscribedOccurrence subscribed
1277− touch $ mergeSubscribedParents subscribed
1278− return result
1279−
1280−readFanSubscribed :: GCompare k => k a -> FanSubscribed k -> IO (Maybe a)
1281−readFanSubscribed k fs = do
1282− parentOcc <- readEventSubscribed $ _eventSubscription_subscribed $ fanSubscribedParent fs
1283− return $ coerce $ DMap.lookup k =<< parentOcc
1284−
1285−readSwitchSubscribed :: SwitchSubscribed a -> IO (Maybe a)
1286−readSwitchSubscribed subscribed = do
1287− result <- readIORef $ switchSubscribedOccurrence subscribed
1288− touch $ switchSubscribedOwnInvalidator subscribed
1289− return result
1290−
1291−readCoincidenceSubscribed :: CoincidenceSubscribed a -> IO (Maybe a)
1292−readCoincidenceSubscribed subscribed = do
1293− result <- readIORef $ coincidenceSubscribedOccurrence subscribed
1294− touch $ coincidenceSubscribedOuter subscribed
1295− --TODO: do we need to touch the inner subscriber?
1296− return result
1297−
1298−-- Always refers to 0
1299−{-# NOINLINE zeroRef #-}
1300−zeroRef :: IORef Height
1301−zeroRef = unsafePerformIO $ newIORef zeroHeight
1302−
1303−{-# INLINE subscribeHoldEvent #-}
1304−subscribeHoldEvent :: (CanSubscribe x m, R.Patch p) => Hold p a -> Subscriber (p a) -> m (EventSubscription (p a))
1305−subscribeHoldEvent h sub = do
1306− EventSubscription _ subd <- getHoldEventSubscription h
1307− sln <- liftIO $ subscribeEventSubscribed subd sub
1308− return $ EventSubscription sln subd
1309−
1310−{-# INLINE debugSubscribe #-}
1311−debugSubscribe :: Bool
1312−debugSubscribe = False
1313−
1314−
1315−type CanSubscribe x m =
1316− ( HasSpiderTimeline x m
1317− , HasCurrentHeight m
1318− , Defer SomeHoldInit m, Defer SomeMergeInit m -- The things we need to lazily initialize state
1319− , Defer SomeClear m -- Wiping out event firings
1320− , Defer SomeAssignment m -- Updating state
1321− , Defer SomeResetCoincidence m
1322− )
1323−
1324−getRootSubscribed :: GCompare k => k a -> Root k -> Subscriber a -> IO (SubscriberListTicket a, RootSubscribed a)
1325−getRootSubscribed k r sub = do
1326− mSubscribed <- readIORef $ rootSubscribed r
1327− case DMap.lookup k mSubscribed of
1328− Just subscribed -> {-# SCC "hitRoot" #-} do
1329− sln <- subscribeRootSubscribed subscribed sub
1330− return (sln, subscribed)
1331− Nothing -> {-# SCC "missRoot" #-} do
1332− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1333− let !cached = rootSubscribed r
1334− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1335− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1336− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1337− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1338− writeIORef uninitRef $! uninit
1339− let subscribed = RootSubscribed
1340− { rootSubscribedKey = k
1341− , rootSubscribedCachedSubscribed = cached
1342− , rootSubscribedOccurrence = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1343− , rootSubscribedSubscribers = subs
1344− , rootSubscribedUninit = uninit
1345− , rootSubscribedWeakSelf = weakSelf
1346−#ifdef DEBUG_NODEIDS
1347− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1348−#endif
1349− }
1350− writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1351− 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
1352− return (sln, subscribed)
1353−
1354−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1355−cleanupRootSubscribed :: RootSubscribed a -> IO ()
1356−cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1357− rootSubscribedUninit self
1358− modifyIORef' cached $ DMap.delete k
1359−
1360−{-# INLINE subscribeRootSubscribed #-}
1361−subscribeRootSubscribed :: RootSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1362−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1363−
1364−-- When getPushSubscribed returns, the PushSubscribed returned will have a fully filled-in
1365−{-# SPECIALIZE getPushSubscribed :: Push a b -> Subscriber b -> EventM x (SubscriberListTicket b, PushSubscribed a b) #-}
1366−getPushSubscribed :: CanSubscribe x m => Push a b -> Subscriber b -> m (SubscriberListTicket b, PushSubscribed a b)
1367−getPushSubscribed p sub = do
1368− mSubscribed <- liftIO $ readIORef $ pushSubscribed p
1369− case mSubscribed of
1370− Just subscribed -> {-# SCC "hitPush" #-} do
1371− sln <- liftIO $ subscribePushSubscribed subscribed sub
1372− return (sln, subscribed)
1373− Nothing -> {-# SCC "missPush" #-} do -- Not yet subscribed
1374− subscribedRef <- liftIO $ newIORef $ error "getPushSubscribed: subscribed not yet intialized"
1375− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1376− s <- liftIO $ newSubscriberPush (pushCompute p) subscribedUnsafe
1377− subscription@(EventSubscription _ subd) <- subscribe (pushParent p) s
1378− parentOcc <- liftIO $ readEventSubscribed subd
1379− occ <- runComputeM $ join <$> mapM (pushCompute p) parentOcc
1380− occRef <- liftIO $ newIORef occ
1381− when (isJust occ) $ scheduleClear occRef
1382− weakSelf <- liftIO $ newIORef $ error "getPushSubscribed: weakSelf not initialized"
1383− let !cached = pushSubscribed p
1384− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupPushSubscribed
1385− let subscribed = PushSubscribed
1386− { pushSubscribedCachedSubscribed = cached
1387− , pushSubscribedOccurrence = occRef
1388− , pushSubscribedHeight = eventSubscribedHeightRef subd -- Since pushes have the same height as their parents, share the ref
1389− , pushSubscribedSubscribers = subs
1390− , pushSubscribedSelf = s
1391− , pushSubscribedWeakSelf = weakSelf
1392− , pushSubscribedParent = subscription
1393−#ifdef DEBUG_NODEIDS
1394− , pushSubscribedNodeId = unsafeNodeId p
1395−#endif
1396− }
1397− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
1398− liftIO $ writeIORef subscribedRef $! subscribed
1399− liftIO $ writeIORef (pushSubscribed p) $ Just subscribed
1400− return (sln, subscribed)
1401−
1402−cleanupPushSubscribed :: PushSubscribed a b -> IO ()
1403−cleanupPushSubscribed self = do
1404− unsubscribe $ pushSubscribedParent self
1405− writeIORef (pushSubscribedCachedSubscribed self) $ Nothing
1406−
1407−{-# INLINE subscribePushSubscribed #-}
1408−subscribePushSubscribed :: PushSubscribed a b -> Subscriber b -> IO (SubscriberListTicket b)
1409−subscribePushSubscribed subscribed sub = WeakBag.insert sub (pushSubscribedSubscribers subscribed) (pushSubscribedWeakSelf subscribed) cleanupPushSubscribed
1410−
1411−{-# SPECIALIZE getMergeSubscribed :: GCompare k => Merge k -> Subscriber (DMap k Identity) -> EventM x (SubscriberListTicket (DMap k Identity), MergeSubscribed k) #-}
1412−getMergeSubscribed :: forall x k m. (CanSubscribe x m, GCompare k) => Merge k -> Subscriber (DMap k Identity) -> m (SubscriberListTicket (DMap k Identity), MergeSubscribed k)
1413−getMergeSubscribed m sub = do
1414− mSubscribed <- liftIO $ readIORef $ mergeSubscribed m
1415− case mSubscribed of
1416− Just subscribed -> {-# SCC "hitMerge" #-} do
1417− sln <- liftIO $ subscribeMergeSubscribed subscribed sub
1418− return (sln, subscribed)
1419− Nothing -> {-# SCC "missMerge" #-} do
1420− subscribedRef <- liftIO $ newIORef $ error "getMergeSubscribed: subscribedRef not yet initialized"
1421− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1422− initialParents <- readBehaviorUntracked $ current $ mergeParents m
1423− subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent k))] <- forM (DMap.toList initialParents) $ {-# SCC "getMergeSubscribed.a" #-} \(k :=> e) -> do
1424− s <- {-# SCC "getMergeSubscribed.a.1" #-} liftIO $ newSubscriberMerge k subscribedUnsafe
1425− subscription@(EventSubscription _ parentSubd) <- {-# SCC "getMergeSubscribed.a.2" #-} subscribe e s
1426− parentOcc <- {-# SCC "getMergeSubscribed.a.3" #-} liftIO $ readEventSubscribed parentSubd
1427− height <- {-# SCC "getMergeSubscribed.a.4" #-} liftIO $ getEventSubscribedHeight parentSubd
1428− {-# SCC "getMergeSubscribed.a.5" #-} return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription s)
1429− let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1430− heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1431− myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1432− myHeight = if invalidHeight `elem` heights
1433− then invalidHeight
1434− else succHeight $ heightBagMax myHeightBag
1435− currentHeight <- getCurrentHeight
1436− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1437− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1438− else (Nothing, dm)
1439− unless (DMap.null accum) $ do
1440− scheduleMerge myHeight subscribedUnsafe
1441− occRef <- liftIO $ newIORef occ
1442− when (isJust occ) $ scheduleClear occRef
1443− accumRef <- liftIO $ newIORef accum
1444− heightRef <- liftIO $ newIORef myHeight
1445− heightMapRef <- liftIO $ newIORef myHeightBag
1446− weakSelf <- liftIO $ newIORef $ error "getMergeSubscribed: weakSelf not yet initialized"
1447− (subs, sln) <- liftIO $ WeakBag.singleton sub weakSelf cleanupMergeSubscribed
1448− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1449− parentsRef <- liftIO $ newIORef $ DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1450− let !subscribed = MergeSubscribed
1451− { mergeSubscribedCachedSubscribed = mergeSubscribed m
1452− , mergeSubscribedOccurrence = occRef
1453− , mergeSubscribedAccum = accumRef
1454− , mergeSubscribedHeight = heightRef
1455− , mergeSubscribedHeightBag = heightMapRef
1456− , mergeSubscribedSubscribers = subs
1457− , mergeSubscribedChange = changeSubdRef
1458− , mergeSubscribedParents = parentsRef
1459− , mergeSubscribedWeakSelf = weakSelf
1460−#ifdef DEBUG_NODEIDS
1461− , mergeSubscribedNodeId = unsafeNodeId m
1462−#endif
1463− }
1464− defer $ SomeMergeInit subscribed $ updated $ mergeParents m
1465− liftIO $ writeIORef subscribedRef $! subscribed
1466− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "MergeSubscribed"
1467− liftIO $ writeIORef (mergeSubscribed m) $ Just subscribed
1468− return (sln, subscribed)
1469−
1470−cleanupMergeSubscribed :: MergeSubscribed k -> IO ()
1471−cleanupMergeSubscribed subscribed = do
1472− parents <- readIORef $ mergeSubscribedParents subscribed
1473− forM_ (DMap.toList parents) $ \(_ :=> msp) -> unsubscribe $ mergeSubscribedParentSubscription msp
1474− -- Not necessary, because this whole MergeSubscribed is dead: writeIORef (mergeSubscribedParents subscribed) DMap.empty
1475− writeIORef (mergeSubscribedCachedSubscribed subscribed) $ Nothing -- Get rid of the cached subscribed
1476−
1477−{-# INLINE subscribeMergeSubscribed #-}
1478−subscribeMergeSubscribed :: MergeSubscribed k -> Subscriber (DMap k Identity) -> IO (SubscriberListTicket (DMap k Identity))
1479−subscribeMergeSubscribed subscribed sub = WeakBag.insert sub (mergeSubscribedSubscribers subscribed) (mergeSubscribedWeakSelf subscribed) cleanupMergeSubscribed
1480−
1481−{-# SPECIALIZE getFanSubscribed :: GCompare k => k a -> Fan k -> Subscriber a -> EventM x (SubscriberListTicket a, FanSubscribed k) #-}
1482−getFanSubscribed :: (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderTimeline x m, GCompare k) => k a -> Fan k -> Subscriber a -> m (SubscriberListTicket a, FanSubscribed k)
1483−getFanSubscribed k f sub = do
1484− mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1485− case mSubscribed of
1486− Just subscribed -> {-# SCC "hitFan" #-} do
1487− sln <- liftIO $ subscribeFanSubscribed k subscribed sub
1488− return (sln, subscribed)
1489− Nothing -> {-# SCC "missFan" #-} do
1490− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1491− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1492− s <- liftIO $ newSubscriberFan subscribedUnsafe
1493− subscription <- subscribe (fanParent f) s
1494− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1495− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1496− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1497− let subscribed = FanSubscribed
1498− { fanSubscribedCachedSubscribed = fanSubscribed f
1499− , fanSubscribedParent = subscription
1500− , fanSubscribedSubscribers = subscribersRef
1501−#ifdef DEBUG_NODEIDS
1502− , fanSubscribedNodeId = unsafeNodeId f
1503−#endif
1504− }
1505− let !self = (k, subscribed)
1506− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1507− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1508− liftIO $ writeIORef subscribedRef $! subscribed
1509− liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1510− return (slnForSub, subscribed)
1511−
1512−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed k) -> IO ()
1513−cleanupFanSubscribed (k, subscribed) = do
1514− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1515− let reducedSubscribers = DMap.delete k subscribers
1516− if DMap.null reducedSubscribers
1517− then do
1518− unsubscribe $ fanSubscribedParent subscribed
1519− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1520− writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1521− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1522−
1523−{-# INLINE subscribeFanSubscribed #-}
1524−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed k -> Subscriber a -> IO (SubscriberListTicket a)
1525−subscribeFanSubscribed k subscribed sub = do
1526− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1527− case DMap.lookup k subscribers of
1528− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1529− let !self = (k, subscribed)
1530− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1531− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1532− 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
1533− return sln
1534− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1535−
1536−{-# SPECIALIZE getSwitchSubscribed :: Switch a -> Subscriber a -> EventM x (SubscriberListTicket a, SwitchSubscribed a) #-}
1537−getSwitchSubscribed :: (Defer SomeHoldInit m, Defer SomeAssignment m, Defer SomeMergeInit m, Defer SomeClear m, Defer SomeResetCoincidence m, HasCurrentHeight m, HasSpiderTimeline x m) => Switch a -> Subscriber a -> m (SubscriberListTicket a, SwitchSubscribed a)
1538−getSwitchSubscribed s sub = do
1539− mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1540− case mSubscribed of
1541− Just subscribed -> {-# SCC "hitSwitch" #-} do
1542− sln <- liftIO $ subscribeSwitchSubscribed subscribed sub
1543− return (sln, subscribed)
1544− Nothing -> {-# SCC "missSwitch" #-} do
1545− subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1546− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1547− i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1548− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1549− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1550− wiRef <- liftIO $ newIORef wi
1551− parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1552− holdInits <- getDeferralQueue
1553− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1554− subscription@(EventSubscription _ subd) <- subscribe e mySub
1555− subscriptionRef <- liftIO $ newIORef subscription
1556− parentOcc <- liftIO $ readEventSubscribed subd
1557− occRef <- liftIO $ newIORef parentOcc
1558− when (isJust parentOcc) $ scheduleClear occRef
1559− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1560− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1561− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1562− let !subscribed = SwitchSubscribed
1563− { switchSubscribedCachedSubscribed = switchSubscribed s
1564− , switchSubscribedOccurrence = occRef
1565− , switchSubscribedHeight = heightRef
1566− , switchSubscribedSubscribers = subs
1567− , switchSubscribedOwnInvalidator = i
1568− , switchSubscribedOwnWeakInvalidator = wiRef
1569− , switchSubscribedBehaviorParents = parentsRef
1570− , switchSubscribedParent = switchParent s
1571− , switchSubscribedCurrentParent = subscriptionRef
1572− , switchSubscribedWeakSelf = weakSelf
1573−#ifdef DEBUG_NODEIDS
1574− , switchSubscribedNodeId = unsafeNodeId s
1575−#endif
1576− }
1577− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1578− liftIO $ writeIORef subscribedRef $! subscribed
1579− liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1580− return (slnForSub, subscribed)
1581−
1582−cleanupSwitchSubscribed :: SwitchSubscribed a -> IO ()
1583−cleanupSwitchSubscribed subscribed = do
1584− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1585− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1586− writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1587−
1588−{-# INLINE subscribeSwitchSubscribed #-}
1589−subscribeSwitchSubscribed :: SwitchSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1590−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1591−
1592−{-# SPECIALIZE getCoincidenceSubscribed :: Coincidence a -> Subscriber a -> EventM x (SubscriberListTicket a, CoincidenceSubscribed a) #-}
1593−getCoincidenceSubscribed :: forall x a m. (Defer SomeAssignment m, Defer SomeHoldInit m, Defer SomeMergeInit m, Defer SomeClear m, HasCurrentHeight m, Defer SomeResetCoincidence m, HasSpiderTimeline x m) => Coincidence a -> Subscriber a -> m (SubscriberListTicket a, CoincidenceSubscribed a)
1594−getCoincidenceSubscribed c sub = do
1595− mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1596− case mSubscribed of
1597− Just subscribed -> {-# SCC "hitCoincidence" #-} do
1598− sln <- liftIO $ subscribeCoincidenceSubscribed subscribed sub
1599− return (sln, subscribed)
1600− Nothing -> {-# SCC "missCoincidence" #-} do
1601− subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1602− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1603− subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1604− outerSubscription@(EventSubscription _ outerSubd) <- subscribe (coincidenceParent c) subOuter
1605− outerOcc <- liftIO $ readEventSubscribed outerSubd
1606− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1607− (occ, height, mInnerSubd) <- case outerOcc of
1608− Nothing -> return (Nothing, outerHeight, Nothing)
1609− Just o -> do
1610− (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
1611− return (occ, height, Just innerSubd)
1612− occRef <- liftIO $ newIORef occ
1613− when (isJust occ) $ scheduleClear occRef
1614− heightRef <- liftIO $ newIORef height
1615− innerSubdRef <- liftIO $ newIORef mInnerSubd
1616− scheduleClear innerSubdRef
1617− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1618− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1619− let subscribed = CoincidenceSubscribed
1620− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1621− , coincidenceSubscribedOccurrence = occRef
1622− , coincidenceSubscribedHeight = heightRef
1623− , coincidenceSubscribedSubscribers = subs
1624− , coincidenceSubscribedOuter = subOuter
1625− , coincidenceSubscribedOuterParent = outerSubscription
1626− , coincidenceSubscribedInnerParent = innerSubdRef
1627− , coincidenceSubscribedWeakSelf = weakSelf
1628−#ifdef DEBUG_NODEIDS
1629− , coincidenceSubscribedNodeId = unsafeNodeId c
1630−#endif
1631− }
1632− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1633− liftIO $ writeIORef subscribedRef $! subscribed
1634− liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1635− return (slnForSub, subscribed)
1636−
1637−cleanupCoincidenceSubscribed :: CoincidenceSubscribed a -> IO ()
1638−cleanupCoincidenceSubscribed subscribed = do
1639− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1640− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1641−
1642−{-# INLINE subscribeCoincidenceSubscribed #-}
1643−subscribeCoincidenceSubscribed :: CoincidenceSubscribed a -> Subscriber a -> IO (SubscriberListTicket a)
1644−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1645−
1646−{-# INLINE merge #-}
1647−merge :: GCompare k => Dynamic R.PatchDMap (DMap k Event) -> Event (DMap k Identity)
1648−merge m = EventMerge $ Merge
1649− { mergeParents = m
1650− , mergeSubscribed = unsafeNewIORef m Nothing
1651− }
1652−
1653−newtype EventSelector k = EventSelector { select :: forall a. k a -> Event a }
1654−
1655−fan :: GCompare k => Event (DMap k Identity) -> EventSelector k
1656−fan e =
1657− let f = Fan
1658− { fanParent = e
1659− , fanSubscribed = unsafeNewIORef e Nothing
1660− }
1661− in EventSelector $ \k -> EventFan k f
1662−
1663−runHoldInits :: IORef [SomeHoldInit] -> IORef [SomeMergeInit] -> EventM x ()
1664−runHoldInits holdInitRef mergeInitRef = do
1665− holdInits <- liftIO $ readIORef holdInitRef
1666− mergeInits <- liftIO $ readIORef mergeInitRef
1667− unless (null holdInits && null mergeInits) $ do
1668− liftIO $ writeIORef holdInitRef []
1669− liftIO $ writeIORef mergeInitRef []
1670− mapM_ initHold holdInits
1671− mapM_ initMerge mergeInits
1672− runHoldInits holdInitRef mergeInitRef
1673−
1674−initHold :: SomeHoldInit -> EventM x ()
1675−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1676−
1677−initMerge :: SomeMergeInit -> EventM x ()
1678−initMerge (SomeMergeInit subscribed changed) = do
1679− changeSub <- liftIO $ newSubscriberMergeChange subscribed
1680− EventSubscription _ changeSubd <- subscribe changed changeSub
1681− change <- liftIO $ readEventSubscribed changeSubd
1682− forM_ change $ \c -> defer $ SomeMergeUpdate c subscribed
1683− liftIO $ writeIORef (mergeSubscribedChange subscribed) (changeSub, changeSubd)
1684−
1685−-- | Run an event action outside of a frame
1686−runFrame :: EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1687−runFrame a = SpiderHost $ ask >>= \spiderEnv -> lift $ do
1688− -- 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
1689− pendingCleanups <- atomicModifyIORef (_spiderEnv_cleanups spiderEnv) $ \x -> ([], x)
1690− {-# SCC "cleanups" #-} sequence_ pendingCleanups
1691− toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1692− holdInitRef <- newIORef []
1693− mergeUpdateRef <- newIORef []
1694− mergeInitRef <- newIORef []
1695− heightRef <- newIORef zeroHeight
1696− toClearRef <- newIORef []
1697− toClearRootRef <- newIORef []
1698− coincidenceInfosRef <- newIORef []
1699− delayedRef <- liftIO $ newIORef IntMap.empty
1700− let env = EventEnv spiderEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1701− let go = do
1702− result <- a
1703− 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
1704− return result
1705− result <- runEventM go env
1706− toClear <- readIORef toClearRef
1707− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1708− toClearRoot <- readIORef toClearRootRef
1709− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1710− toAssign <- readIORef toAssignRef
1711− toReconnectRef <- newIORef []
1712− coincidenceInfos <- readIORef coincidenceInfosRef
1713− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1714− writeIORef vRef v
1715− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1716− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1717− mergeUpdates <- readIORef mergeUpdateRef
1718− writeIORef mergeUpdateRef []
1719− let updateMerge :: SomeMergeUpdate -> IO [SomeEventSubscription]
1720− updateMerge (SomeMergeUpdate (R.PatchDMap p :: R.PatchDMap (DMap k Event)) m) = do
1721− oldParents <- readIORef $ mergeSubscribedParents m
1722− let f (subscriptionsToKill, ps) (k :=> Compose me) = do
1723− (mOldSubd, newPs) <- case me of
1724− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1725− Just e -> do
1726− s <- liftIO $ newSubscriberMerge k m
1727− subscription@(EventSubscription _ subd) <- subscribe e s
1728− let newParent = MergeSubscribedParent subscription s
1729− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1730− liftIO $ addMergeHeight newParentHeight m
1731− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1732− forM_ mOldSubd $ \oldSubd -> do
1733− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ mergeSubscribedParentSubscription oldSubd
1734− liftIO $ removeMergeHeight oldHeight m
1735− return (maybeToList (SomeEventSubscription . mergeSubscribedParentSubscription <$> mOldSubd) ++ subscriptionsToKill, newPs)
1736− (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
1737− writeIORef (mergeSubscribedParents m) $! newParents
1738− return subscriptionsToKill
1739− when debugPropagate $ putStrLn "Updating merges"
1740− mergeSubscriptionsToKill <- concat <$> mapM updateMerge mergeUpdates
1741− when debugPropagate $ putStrLn "Updating merges done"
1742− toReconnect <- readIORef toReconnectRef
1743− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1744− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1745− wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1746− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1747− finalize wi
1748− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1749− wi' <- mkWeakPtrWithDebug i "wi'"
1750− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1751− writeIORef (switchSubscribedBehaviorParents subscribed) []
1752− writeIORef holdInitRef [] --TODO: Should we reuse this?
1753− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) holdInitRef
1754− 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
1755− --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1756− sub <- newSubscriberSwitch subscribed
1757− 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
1758− {-
1759− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1760− liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1761− EventRoot _ -> "EventRoot"
1762− EventNever -> "EventNever"
1763− _ -> "something else"
1764− -}
1765− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1766− return $ SomeEventSubscription oldSubscription
1767− forM_ mergeSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1768− forM_ switchSubscriptionsToKill $ \(SomeEventSubscription oldSubscription) -> liftIO $ unsubscribe oldSubscription
1769− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1770− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1771− parentHeight <- getEventSubscribedHeight subd'
1772− myHeight <- readIORef $ switchSubscribedHeight subscribed
1773− when (parentHeight /= myHeight) $ do
1774− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1775− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1776− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> do
1777− 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
1778− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1779− unsubscribe subscription
1780− mapM_ invalidateCoincidenceHeight mcs
1781− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1782− forM_ mergeUpdates $ \(SomeMergeUpdate _ m) -> revalidateMergeHeight m
1783− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1784− height <- calculateSwitchHeight subscribed
1785− updateSwitchHeight height subscribed
1786− return result
1787−
1788−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1789−
1790−{-# INLINE zeroHeight #-}
1791−zeroHeight :: Height
1792−zeroHeight = Height 0
1793−
1794−{-# INLINE invalidHeight #-}
1795−invalidHeight :: Height
1796−invalidHeight = Height (-1000)
1797−
1798−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1799−{-# INLINE invalidHeightBeingTraversed #-}
1800−invalidHeightBeingTraversed :: Height
1801−invalidHeightBeingTraversed = Height (-1001)
1802−
1803−{-# INLINE succHeight #-}
1804−succHeight :: Height -> Height
1805−succHeight h@(Height a) =
1806− if h == invalidHeight
1807− then invalidHeight
1808− else Height $ succ a
1809−
1810−invalidateSubscriberHeight :: Height -> Subscriber a -> IO ()
1811−invalidateSubscriberHeight old s = case s of
1812− SubscriberPush _ subscribed -> do
1813− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1814− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ invalidateSubscriberHeight old
1815− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1816− SubscriberMerge _ subscribed -> do
1817− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1818− removeMergeHeight old subscribed
1819− invalidateMergeHeight subscribed
1820− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1821− SubscriberMergeChange _ -> return () -- Doesn't affect the height of the merge, because its changes are delayed
1822− SubscriberFan subscribed -> do
1823− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1824− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1825− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
1826− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1827− SubscriberHold _ -> return ()
1828− SubscriberHoldIdentity _ -> return ()
1829− SubscriberSwitch subscribed -> do
1830− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1831− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1832− when (oldHeight /= invalidHeight) $ do
1833− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1834− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1835− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1836− SubscriberCoincidenceOuter subscribed -> do
1837− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1838− invalidateCoincidenceHeight subscribed
1839− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1840− SubscriberCoincidenceInner subscribed -> do
1841− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1842− invalidateCoincidenceHeight subscribed
1843− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1844− SubscriberHandle -> return ()
1845−
1846−removeMergeHeight :: Height -> MergeSubscribed k -> IO ()
1847−removeMergeHeight oldParentHeight subscribed = do
1848− when debugInvalidateHeight $ putStrLn $ "removeMergeHeight: " <> show oldParentHeight <> " Merge" <> showNodeId subscribed
1849− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1850− let newHeights = heightBagRemove oldParentHeight oldHeights
1851− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1852−
1853−invalidateMergeHeight :: MergeSubscribed k -> IO ()
1854−invalidateMergeHeight subscribed = do
1855− oldHeight <- readIORef $ mergeSubscribedHeight subscribed
1856− -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1857− when (oldHeight /= invalidHeight) $ do
1858− writeIORef (mergeSubscribedHeight subscribed) $! invalidHeight
1859− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1860−
1861−addMergeHeight :: Height -> MergeSubscribed k -> IO ()
1862−addMergeHeight newParentHeight subscribed = do
1863− when debugInvalidateHeight $ putStrLn $ "addMergeHeight: " <> show newParentHeight <> " Merge" <> showNodeId subscribed
1864− oldHeights <- readIORef $ mergeSubscribedHeightBag subscribed
1865− let newHeights = heightBagAdd newParentHeight oldHeights
1866− writeIORef (mergeSubscribedHeightBag subscribed) $! newHeights
1867−
1868−revalidateMergeHeight :: MergeSubscribed k -> IO ()
1869−revalidateMergeHeight subscribed = do
1870− currentHeight <- readIORef $ mergeSubscribedHeight subscribed
1871− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1872− heights <- readIORef $ mergeSubscribedHeightBag subscribed
1873− parents <- readIORef $ mergeSubscribedParents subscribed
1874− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1875− case heightBagSize heights `compare` DMap.size parents of
1876− LT -> return ()
1877− EQ -> do
1878− let height = succHeight $ heightBagMax heights
1879− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1880− writeIORef (mergeSubscribedHeight subscribed) $! height
1881− WeakBag.traverse (mergeSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1882− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge" <> showNodeId subscribed
1883−
1884−invalidateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1885−invalidateCoincidenceHeight subscribed = do
1886− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1887− when (oldHeight /= invalidHeight) $ do
1888− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1889− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1890−
1891−recalculateSubscriberHeight :: Height -> Subscriber a -> IO ()
1892−recalculateSubscriberHeight new s = case s of
1893− SubscriberPush _ subscribed -> do
1894− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed
1895− WeakBag.traverse (pushSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1896− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberPush" <> showNodeId subscribed <> " done"
1897− SubscriberMerge _ subscribed -> do
1898− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed
1899− addMergeHeight new subscribed
1900− revalidateMergeHeight subscribed
1901− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberMerge" <> showNodeId subscribed <> " done"
1902− SubscriberMergeChange _ -> return ()
1903− SubscriberFan subscribed -> do
1904− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
1905− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1906− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
1907− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
1908− SubscriberHold _ -> return ()
1909− SubscriberHoldIdentity _ -> return ()
1910− SubscriberSwitch subscribed -> do
1911− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
1912− updateSwitchHeight new subscribed
1913− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
1914− SubscriberCoincidenceOuter subscribed -> do
1915− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
1916− recalculateCoincidenceHeight subscribed
1917− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
1918− SubscriberCoincidenceInner subscribed -> do
1919− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
1920− recalculateCoincidenceHeight subscribed
1921− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
1922− SubscriberHandle -> return ()
1923−
1924−updateSwitchHeight :: Height -> SwitchSubscribed a -> IO ()
1925−updateSwitchHeight new subscribed = do
1926− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1927− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1928− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1929− writeIORef (switchSubscribedHeight subscribed) $! new
1930− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1931−
1932−recalculateCoincidenceHeight :: CoincidenceSubscribed a -> IO ()
1933−recalculateCoincidenceHeight subscribed = do
1934− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1935− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1936− height <- calculateCoincidenceHeight subscribed
1937− when (height /= invalidHeight) $ do
1938− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1939− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1940−
1941−calculateSwitchHeight :: SwitchSubscribed a -> IO Height
1942−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1943−
1944−calculateCoincidenceHeight :: CoincidenceSubscribed a -> IO Height
1945−calculateCoincidenceHeight subscribed = do
1946− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1947− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1948− return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1949−
1950−data SomeSwitchSubscribed = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed a)
1951−
1952−invalidate :: IORef [SomeSwitchSubscribed] -> WeakList Invalidator -> IO (WeakList Invalidator)
1953−invalidate toReconnectRef wis = do
1954− forM_ wis $ \wi -> do
1955− mi <- deRefWeak wi
1956− case mi of
1957− Nothing -> do
1958− traceInvalidate "invalidate Dead"
1959− return () --TODO: Should we clean this up here?
1960− Just i -> do
1961− finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1962− case i of
1963− InvalidatorPull p -> do
1964− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1965− mVal <- readIORef $ pullValue p
1966− forM_ mVal $ \val -> do
1967− writeIORef (pullValue p) Nothing
1968− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1969− InvalidatorSwitch subscribed -> do
1970− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1971− modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1972− 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
1973−
1974−--------------------------------------------------------------------------------
1975−-- Reflex integration
1976−--------------------------------------------------------------------------------
1977−
1978−-- | The default, global Spider environment
1979−type Spider = SpiderTimeline Global
1980−
1981−-- | Designates the default, global Spider timeline
1982−data Global
1983−
1984−{-# NOINLINE globalSpiderTimeline #-}
1985−globalSpiderTimeline :: SpiderTimeline Global
1986−globalSpiderTimeline = unsafePerformIO unsafeNewSpiderTimeline
1987−
1988−-- | Stores all global data relevant to a particular Spider timeline; only one
1989−-- value should exist for each type @x@
1990−data SpiderTimeline x = SpiderTimeline
1991− { _spiderEnv_toUnsubscribe :: !(IORef [SomeEventSubscription])
1992− , _spiderEnv_lock :: !(MVar ())
1993− , _spiderEnv_cleanups :: !(IORef [IO ()])
1994−#ifdef DEBUG
1995− , _spiderEnv_depth :: !(IORef Int)
1996−#endif
1997− }
1998−type role SpiderTimeline nominal
1999−
2000−instance GEq SpiderTimeline where
2001− a `geq` b = if _spiderEnv_toUnsubscribe a == _spiderEnv_toUnsubscribe b
2002− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimeline can't have two different 'x' arguments
2003− else Nothing
2004−
2005−unsafeNewSpiderTimeline :: forall x. IO (SpiderTimeline x)
2006−unsafeNewSpiderTimeline = do
2007− toUnsubscribe <- newIORef []
2008− lock <- newMVar ()
2009− cleanups <- newIORef []
2010−#ifdef DEBUG
2011− depthRef <- newIORef 0
2012−#endif
2013− return $ SpiderTimeline
2014− { _spiderEnv_toUnsubscribe = toUnsubscribe
2015− , _spiderEnv_lock = lock
2016− , _spiderEnv_cleanups = cleanups
2017−#ifdef DEBUG
2018− , _spiderEnv_depth = depthRef
2019−#endif
2020− }
2021−
2022−-- | Create a new SpiderTimeline
2023−newSpiderTimeline :: IO (Some SpiderTimeline)
2024−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2025−
2026−-- | Pass a new timeline to the given function.
2027−withSpiderTimeline :: (forall x. SpiderTimeline x -> IO r) -> IO r
2028−withSpiderTimeline k = unsafeNewSpiderTimeline >>= k
2029−
2030−newtype SpiderPullM x a = SpiderPullM (BehaviorM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2031−
2032−newtype SpiderPushM x a = SpiderPushM (ComputeM a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2033−
2034−instance R.Reflex (SpiderTimeline x) where
2035− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior a }
2036− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event a }
2037− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic Identity a } deriving (Functor, Applicative, Monad)
2038− newtype Incremental (SpiderTimeline x) p a = SpiderIncremental { unSpiderIncremental :: Dynamic p a }
2039− type PullM (SpiderTimeline x) = SpiderPullM x
2040− type PushM (SpiderTimeline x) = SpiderPushM x
2041− {-# INLINABLE never #-}
2042− never = SpiderEvent EventNever
2043− {-# INLINABLE constant #-}
2044− constant = SpiderBehavior . BehaviorConst
2045− {-# INLINABLE push #-}
2046− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2047− {-# INLINABLE pull #-}
2048− pull = SpiderBehavior . pull . coerce
2049− {-# INLINABLE merge #-}
2050− merge = SpiderEvent . merge . DynamicConst . (unsafeCoerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k Event)
2051− {-# INLINABLE fan #-}
2052− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2053− {-# INLINABLE switch #-}
2054− switch = SpiderEvent . switch . (unsafeCoerce :: Behavior (R.Event (SpiderTimeline x) a) -> Behavior (Event a)) . unSpiderBehavior
2055− {-# INLINABLE coincidence #-}
2056− coincidence = SpiderEvent . coincidence . (unsafeCoerce :: Event (R.Event (SpiderTimeline x) a) -> Event (Event a)) . unSpiderEvent
2057− {-# INLINABLE current #-}
2058− current = SpiderBehavior . current . unSpiderDynamic
2059− {-# INLINABLE updated #-}
2060− updated = SpiderEvent . fmap runIdentity . updated . unSpiderDynamic
2061− {-# INLINABLE unsafeBuildDynamic #-}
2062− unsafeBuildDynamic readV0 v' = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (coerce readV0) $ addIdentity $ unSpiderEvent v'
2063− {-# INLINABLE unsafeBuildIncremental #-}
2064− unsafeBuildIncremental readV0 dv = SpiderIncremental $ DynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2065− {-# INLINABLE mergeIncremental #-}
2066− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic R.PatchDMap (DMap k (R.Event (SpiderTimeline x))) -> Dynamic R.PatchDMap (DMap k Event)) . unSpiderIncremental
2067− {-# INLINABLE currentIncremental #-}
2068− currentIncremental = SpiderBehavior . current . unSpiderIncremental
2069− {-# INLINABLE updatedIncremental #-}
2070− updatedIncremental = SpiderEvent . updated . unSpiderIncremental
2071− {-# INLINABLE incrementalToDynamic #-}
2072− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ DynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ current i) $ flip push (updated i) $ \p -> do
2073− c <- readBehaviorUntracked $ current i
2074− return $ Identity <$> R.apply p c --TODO: Avoid the redundant 'apply'
2075−
2076−instance R.MonadSample (SpiderTimeline x) (EventM x) where
2077− {-# INLINABLE sample #-}
2078− sample (SpiderBehavior b) = readBehaviorUntracked b
2079−
2080−instance R.MonadHold (SpiderTimeline x) (EventM x) where
2081− {-# INLINABLE hold #-}
2082− hold = holdSpiderEventM
2083− {-# INLINABLE holdDyn #-}
2084− holdDyn = holdDynSpiderEventM
2085− {-# INLINABLE holdIncremental #-}
2086− holdIncremental = holdIncrementalSpiderEventM
2087−
2088−holdSpiderEventM :: a -> R.Event (SpiderTimeline x) a -> EventM x (R.Behavior (SpiderTimeline x) a)
2089−holdSpiderEventM v0 e = fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2090−
2091−holdDynSpiderEventM :: a -> R.Event (SpiderTimeline x) a -> EventM x (R.Dynamic (SpiderTimeline x) a)
2092−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2093−
2094−holdIncrementalSpiderEventM :: R.Patch p => a -> R.Event (SpiderTimeline x) (p a) -> EventM x (R.Incremental (SpiderTimeline x) p a)
2095−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2096−
2097−instance R.MonadHold (SpiderTimeline x) (SpiderHost x) where
2098− {-# INLINABLE hold #-}
2099− hold v0 e = R.runHostFrame $ R.hold v0 e
2100− {-# INLINABLE holdDyn #-}
2101− holdDyn v0 e = R.runHostFrame $ R.holdDyn v0 e
2102− {-# INLINABLE holdIncremental #-}
2103− holdIncremental v0 e = R.runHostFrame $ R.holdIncremental v0 e
2104−
2105−instance R.MonadSample (SpiderTimeline x) (SpiderHost x) where
2106− {-# INLINABLE sample #-}
2107− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
2108−
2109−instance R.MonadSample (SpiderTimeline x) (SpiderPullM x) where
2110− {-# INLINABLE sample #-}
2111− sample = coerce . readBehaviorTracked . unSpiderBehavior
2112−
2113−instance R.MonadSample (SpiderTimeline x) (SpiderPushM x) where
2114− {-# INLINABLE sample #-}
2115− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
2116−
2117−instance R.MonadHold (SpiderTimeline x) (SpiderPushM x) where
2118− {-# INLINABLE hold #-}
2119− hold v0 e = R.current <$> R.holdDyn v0 e
2120− {-# INLINABLE holdDyn #-}
2121− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity e
2122− {-# INLINABLE holdIncremental #-}
2123− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . DynamicHold <$> hold v0 e
2124−
2125−data RootTrigger a = forall k. GCompare k => RootTrigger (SubscriberList a, IORef (DMap k Identity), k a)
2126−newtype SpiderEventHandle a = SpiderEventHandle { unSpiderEventHandle :: EventSubscription a }
2127−
2128−instance R.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2129− {-# INLINABLE subscribeEvent #-}
2130− subscribeEvent e = SpiderHostFrame $ do
2131− subscription <- subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2132− return $ SpiderEventHandle subscription
2133−
2134−instance R.ReflexHost (SpiderTimeline x) where
2135− type EventTrigger (SpiderTimeline x) = RootTrigger
2136− type EventHandle (SpiderTimeline x) = SpiderEventHandle
2137− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
2138−
2139−instance R.MonadReadEvent (SpiderTimeline x) (ReadPhase x) where
2140− {-# INLINABLE readEvent #-}
2141− readEvent h = ReadPhase $ fmap (fmap return) $ liftIO $ do
2142− let EventSubscription sln subscribed = unSpiderEventHandle h
2143− result <- readEventSubscribed subscribed
2144− touch sln
2145− return result
2146−
2147−instance MonadRef (EventM x) where
2148− type Ref (EventM x) = Ref IO
2149− {-# INLINABLE newRef #-}
2150− {-# INLINABLE readRef #-}
2151− {-# INLINABLE writeRef #-}
2152− newRef = liftIO . newRef
2153− readRef = liftIO . readRef
2154− writeRef r a = liftIO $ writeRef r a
2155−
2156−instance MonadAtomicRef (EventM x) where
2157− {-# INLINABLE atomicModifyRef #-}
2158− atomicModifyRef r f = liftIO $ atomicModifyRef r f
2159−
2160−-- | The monad for actions that manipulate a Spider timeline identified by @x@
2161−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimeline x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2162−
2163−instance Monad (SpiderHost x) where
2164− {-# INLINABLE (>>=) #-}
2165− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2166− {-# INLINABLE (>>) #-}
2167− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2168− {-# INLINABLE return #-}
2169− return x = SpiderHost $ return x
2170− {-# INLINABLE fail #-}
2171− fail s = SpiderHost $ fail s
2172−
2173−-- | Run an action affecting the global Spider timeline; this will be guarded by
2174−-- a mutex for that timeline
2175−runSpiderHost :: SpiderHost Global a -> IO a
2176−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimeline
2177−
2178−-- | Run an action affecting a given Spider timeline; this will be guarded by a
2179−-- mutex for that timeline
2180−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimeline x -> IO a
2181−runSpiderHostForTimeline (SpiderHost a) = runReaderT a
2182−
2183−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2184−
2185−instance Monad (SpiderHostFrame x) where
2186− {-# INLINABLE (>>=) #-}
2187− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2188− {-# INLINABLE (>>) #-}
2189− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2190− {-# INLINABLE return #-}
2191− return x = SpiderHostFrame $ return x
2192− {-# INLINABLE fail #-}
2193− fail s = SpiderHostFrame $ fail s
2194−
2195−instance R.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
2196− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
2197−
2198−addIdentity :: Event a -> Event (Identity a)
2199−--addIdentity = fmap Identity
2200−addIdentity = unsafeCoerce
2201−
2202−instance R.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
2203− {-# INLINABLE hold #-}
2204− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . BehaviorHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2205− {-# INLINABLE holdDyn #-}
2206− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . DynamicHoldIdentity) $ hold v0 $ addIdentity $ unSpiderEvent e
2207− {-# INLINABLE holdIncremental #-}
2208− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . DynamicHold) $ hold v0 $ unSpiderEvent e
2209−
2210−newEventWithTriggerIO :: forall a. (RootTrigger a -> IO (IO ())) -> IO (Event a)
2211−newEventWithTriggerIO f = do
2212− es <- newFanEventWithTriggerIO $ \Refl -> f
2213− return $ select es Refl
2214−
2215−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger a -> IO (IO ())) -> IO (EventSelector k)
2216−newFanEventWithTriggerIO f = do
2217− occRef <- newIORef DMap.empty
2218− subscribedRef <- newIORef DMap.empty
2219− let !r = Root
2220− { rootOccurrence = occRef
2221− , rootSubscribed = subscribedRef
2222− , rootInit = f
2223− }
2224− return $ EventSelector $ \k -> EventRoot k r
2225−
2226−instance R.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
2227− newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
2228− newFanEventWithTrigger f = SpiderHost $ lift $ do
2229− es <- newFanEventWithTriggerIO f
2230− return $ R.EventSelector $ SpiderEvent . select es
2231−
2232−instance R.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
2233− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
2234− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
2235− es <- newFanEventWithTriggerIO f
2236− return $ R.EventSelector $ SpiderEvent . select es
2237−
2238−instance R.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
2239− {-# INLINABLE subscribeEvent #-}
2240− subscribeEvent = subscribeEventSpiderHost
2241−
2242−subscribeEventSpiderHost :: R.Event (SpiderTimeline x) a -> SpiderHost x (R.EventHandle (SpiderTimeline x) a)
2243−subscribeEventSpiderHost e = do
2244− subscription <- runFrame $ subscribe (unSpiderEvent e) SubscriberHandle --TODO: Unsubscribe eventually (manually and/or with weak ref)
2245− return $ SpiderEventHandle subscription
2246−
2247−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2248−
2249−instance R.MonadSample (SpiderTimeline x) (ReadPhase x) where
2250− {-# INLINABLE sample #-}
2251− sample = ReadPhase . R.sample
2252−
2253−instance R.MonadHold (SpiderTimeline x) (ReadPhase x) where
2254− {-# INLINABLE hold #-}
2255− hold v0 e = ReadPhase $ R.hold v0 e
2256− {-# INLINABLE holdDyn #-}
2257− holdDyn v0 e = ReadPhase $ R.holdDyn v0 e
2258− {-# INLINABLE holdIncremental #-}
2259− holdIncremental v0 e = ReadPhase $ R.holdIncremental v0 e
2260−
2261−instance R.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
2262− type ReadPhase (SpiderHost x) = ReadPhase x
2263− fireEventsAndRead es (ReadPhase a) = run es a
2264− runHostFrame = runFrame . runSpiderHostFrame
2265−
2266−instance MonadRef (SpiderHost x) where
2267− type Ref (SpiderHost x) = Ref IO
2268− newRef = SpiderHost . newRef
2269− readRef = SpiderHost . readRef
2270− writeRef r = SpiderHost . writeRef r
2271−
2272−instance MonadAtomicRef (SpiderHost x) where
2273− atomicModifyRef r = SpiderHost . atomicModifyRef r
2274−
2275−instance MonadRef (SpiderHostFrame x) where
2276− type Ref (SpiderHostFrame x) = Ref IO
2277− newRef = SpiderHostFrame . newRef
2278− readRef = SpiderHostFrame . readRef
2279− writeRef r = SpiderHostFrame . writeRef r
2280−
2281−instance MonadAtomicRef (SpiderHostFrame x) where
2282− atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2283−
2284−--------------------------------------------------------------------------------
2285−-- Deprecated items
2286−--------------------------------------------------------------------------------
2287−
2288−{-# DEPRECATED SpiderEnv "Use 'SpiderTimeline' instead" #-}
2289−type SpiderEnv = SpiderTimeline
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−369
@@ -1,369 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE RecursiveDo #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE TypeSynonymInstances #-}
11−
12−module Reflex.Bench.Focused where
13−
14−import Reflex
15−import Reflex.TestPlan
16−
17−import Control.Applicative
18−import Control.Monad
19−import Control.Monad.Fix
20−import Control.Monad.Identity
21−import Data.Traversable (for, traverse)
22−
23−import qualified Data.Dependent.Map as DMap
24−import Data.Map (Map)
25−import qualified Data.Map as Map
26−import Data.Maybe
27−
28−import Data.Functor.Misc
29−
30−import Data.List
31−import Data.List.Split
32−import Data.Tuple
33−
34−import Data.Monoid
35−import Data.Word
36−
37−import Control.DeepSeq
38−
39−import Prelude hiding (concat, sum)
40−
41−
42−mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
43−mergeTree n es | length es <= n = sum' es
44− | otherwise = mergeTree n subTrees
45− where
46− merges = chunksOf n es
47− subTrees = map sum' merges
48− sum' = fmap sum . mconcat . fmap (fmap (:[]) )
49−
50−mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51−mergeListDyn = mconcat . fmap (fmap pure)
52−
53−mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
54−mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55− | otherwise = mergeTreeDyn n $ fmap sumDyns merges
56− where
57− merges = chunksOf n dyns
58− sumDyns = fmap sum . mergeListDyn
59−
60−
61−
62−-- N events all firing for N frames
63−continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
64−continuous n frames = for [1..n] $ \i -> plan $ (, i) <$> [1..frames]
65−
66−-- Fire N events once every P frames for total N frames (interspersed evenly)
67−occasional :: TestPlan t m => Word -> Word -> Word -> m [Event t Word]
68−occasional n period frames = traverse plan $ zipWith occs [1..] phases
69− where
70− phases = take (fromIntegral n) $ concat (repeat [1..period])
71− occs i phase = (, i) <$> [1, phase+1..frames]
72−
73−
74−
75−-- Event which never fires (but isn't 'never')
76−never' :: TestPlan t m => m (Event t Word)
77−never' = plan []
78−
79−
80−-- Event which fires once on first frame
81−event :: TestPlan t m => m (Event t Word)
82−event = plan [(1, 0)]
83−
84−-- Event which fires constantly over N frames
85−events :: TestPlan t m => Word -> m (Event t Word)
86−events n = plan $ (\i -> (i, i)) <$> [1..n]
87−
88−
89−-- N events all originating from one event
90−fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
91−fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
92−
93−
94−-- Make a fan using the value in the event converting it to a singleton Map
95−-- use a fan of size n (key is the value mod n)
96−fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
97−fanMerge n e = leftmost $ s <$> [0..n - 1] where
98− s k = select f (Const2 k)
99− f = fan (toMap <$> e)
100− toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
101−
102−-- Dumb version of the above using simple fmapMaybe to filter
103−fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
104−fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
105− s k = flip fmapMaybe e $ \j -> if k == j `mod` n
106− then Just k
107− else Nothing
108−
109−
110−
111−-- Create an Event with a Map which is dense to size N
112−denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
113−denseMap n = plan [(1, m)] where
114− m = Map.fromList $ zip [1..n] [1..n]
115−
116−iter :: (a -> a) -> Word -> a -> a
117−iter _ 0 a = a
118−iter f n a = iter f (n - 1) (f a)
119−
120−iterM :: Monad m => (a -> m a) -> Word -> a -> m a
121−iterM _ 0 a = return a
122−iterM f n a = iterM f (n - 1) =<< f a
123−
124−fmapChain :: (Functor f) => Word -> f Word -> f Word
125−fmapChain = iter (fmap (+1))
126−
127−mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
128−mapDynChain = iterM (return . fmap (+1))
129−
130−joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131−joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132−
133−combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134−combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135−
136−pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
137−pingPong e1 e2 = do
138− rec
139− d <- foldDyn (const swap) (e1, e2) e
140− let e = switch (fst <$> current d)
141− return e
142−
143−switchFactors :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
144−switchFactors 0 e = return e
145−switchFactors i e = do
146− b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
147− switchFactors (i - 1) (switch b)
148−
149−
150−switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
151−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
152−
153−
154−switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
155−switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
156−
157−switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
158−switchPromptlyChain = iterM $ \e ->
159− switchPromptlyDyn <$> holdDyn e (e <$ e)
160−
161−
162−coinChain :: Reflex t => Word -> Event t Word -> Event t Word
163−coinChain = iter $ \e -> coincidence (e <$ e)
164−
165−fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
166−fanMergeChain width = iter $ fanMerge width
167−
168−fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
169−fmapFanMergeChain width = iter $ fmapFanMerge width
170−
171−-- | Data type for a collection (a map) which is updated by providing differences
172−data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
173−
174−-- This will hopefully become a primitive (faster!)
175−switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
176−switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
177−
178−-- switchMergeMaybe :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
179−-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
180−
181−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))
182−switchMergeBehaviors mapChanges = pull . joinMap <$> holdMap mapChanges
183− where joinMap m = traverse sample =<< sample m
184−
185−-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
186−holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
187−holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
188−
189− where
190− modify k Nothing items = Map.delete k items
191− modify k (Just item) items = Map.insert k item items
192−
193−
194−-- | Hold an UpdatedMap as a behavior by applying differences to the initial value
195−holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
196−holdMap = (current <$>) . holdMapDyn
197−
198−
199−increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
200−increasingMerge xs = do
201− e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
202− return (UpdatedMap Map.empty e)
203− where
204− reset = Map.fromList $ zip [1..] (const Nothing <$> xs)
205−
206−decreasingMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
207−decreasingMerge xs = do
208− e <- planList (zipWith Map.singleton [1..] (const Nothing <$> xs) ++ [reset])
209− return (UpdatedMap initial e)
210− where
211− initial = Map.fromList $ zip [1..] xs
212− reset = Just <$> initial
213−
214−modifyMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
215−modifyMerge xs = do
216− e <- planList $ zipWith Map.singleton [1..] (Just <$> xs)
217− return (UpdatedMap initial e)
218− where
219− initial = Map.fromList $ zip [1..] xs
220−
221−
222−countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
223−countMany = traverse (fmap current . count)
224−
225−countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
226−countManyDyn = traverse count
227−
228−
229−-- Given a function which creates a sub-network (from a single event)
230−-- Create a plan which uses a switch to substitute it
231−switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
232−switches numFrames f = do
233− es <- events numFrames
234− switch <$> hold never (makeEvents es)
235−
236− where
237− makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
238−
239−
240−
241−-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
242−-- the other, which we're only interested in time for running frames
243−subscribing :: Word -> Word -> [(String, TestCase)]
244−subscribing n frames =
245− [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
246− , testSub "fanMerge" $ return . fanMerge n
247− , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
248− , testSub "fmapChain" $ return . fmapChain n
249− , testSub "switchChain" $ switchChain n
250− , testSub "switchPromptlyChain" $ switchPromptlyChain n
251− , testSub "switchFactors" $ switchFactors n
252− , testSub "coincidenceChain" $ return . coinChain n
253− ]
254−
255− where
256− 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)
257− testSub name test = testE name (switches frames test)
258−
259−-- Set of benchmark to test specifically merging dynamic collections
260−-- a pattern which occurs frequently in reflex-dom collections
261−merging :: Word -> [(String, TestCase)]
262−merging n =
263− [ testE "static events" $ mergeList <$> sparse
264− , testE "increasing events" $ switchMergeEvents =<< increasingMerge =<< sparse
265− , testE "decreasing events" $ switchMergeEvents =<< decreasingMerge =<< sparse
266− , testE "modify events" $ switchMergeEvents =<< modifyMerge =<< sparse
267−
268− , testB "static behaviors" $ pull . traverse sample <$> counters
269− , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
270− , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
271− , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
272−
273−
274− -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
275− -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
276− -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
277− ]
278−
279− where
280− sparse :: TestPlan t m => m [Event t Word]
281− sparse = fmap (fmap (+1)) <$> occasional n 8 n
282−
283− counters :: TestPlan t m => m [Behavior t Int]
284− counters = countMany =<< sparse
285−
286−
287−
288−fans :: Word -> [(String, TestCase)]
289−fans n =
290− [ testE "fanMapChain" $ fanMergeChain n 10 <$> e
291− , testE "fmapFanMapChain" $ fmapFanMergeChain n 10 <$> e
292−
293− , testE "fanMerge " $ fanMerge n <$> events n
294−
295− , testE "fmapFanMerge " $ fmapFanMerge n <$> events n
296− , testE "triggerMerge" $ leftmost <$> for [1..n] (\i -> plan [(i, i)])
297−
298− ]
299− where
300−
301− e :: TestPlan t m => m (Event t Word)
302− e = events 100
303−
304−
305−
306−
307−dynamics :: Word -> [(String, TestCase)]
308−dynamics n =
309− [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310− , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
311− , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312− , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313− , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
314− , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
315− ] where
316−
317− d :: TestPlan t m => m (Dynamic t Word)
318− d = count =<< events 10
319−
320− sparse, dense :: TestPlan t m => m [Dynamic t Int]
321− sparse = countManyDyn =<< occasional n 8 16
322− dense = countManyDyn =<< continuous n 2
323−
324−
325−firing :: Word -> [(String, TestCase)]
326−firing n =
327− [ testE "fmapChain" $ fmapChain n <$> e
328− , testE "switchChain2" $ switchChain2 n =<< e
329− , testE "switchPromptlyChain" $ switchPromptlyChain n =<< e
330− , testE "switchFactors" $ switchFactors n =<< e
331− , testE "coincidenceChain" $ coinChain n <$> e
332−
333− , testE "dense mergeTree" $ mergeTree 8 <$> dense
334− , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
335−
336− -- Triggers test failure in Spider
337− --, testE "runFrame" $ events n
338−
339− , testB "sum counters" $ do
340− counts <- counters
341− return $ pull $ sum <$> traverse sample counts
342−
343− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
344− , testB "pullChain2" $ do
345− es <- events 4
346− e' <- plan [(0, ())]
347−
348− b <- hold (constant 0) $
349− pushAlways (const $ fmapChain n <$> hold 0 es) e'
350−
351− return (join b)
352− , testB "counters/pullTree" $ mergeTree 8 <$> counters
353−
354− ]
355− where
356− e :: TestPlan t m => m (Event t Word)
357− e = events 10
358−
359− dense, sparse :: TestPlan t m => m [Event t Word]
360− dense = continuous n 2
361− sparse = occasional n 8 16
362−
363− counters :: TestPlan t m => m [Behavior t Int]
364− counters = countMany =<< sparse
365−
366−
367−
368−
369−
test/Reflex/Plan/Pure.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−
9−module Reflex.Plan.Pure where
10−
11−import Reflex
12−import Reflex.Pure
13−import Reflex.TestPlan
14−
15−import Control.Applicative
16−import Control.Monad.Fix
17−import Control.Monad.State
18−import Data.IntMap (IntMap)
19−import qualified Data.IntMap as IntMap
20−import Data.IntSet (IntSet)
21−import qualified Data.IntSet as IntSet
22−
23−import Data.Bifunctor
24−import Data.Maybe
25−import Data.Monoid
26−import Prelude
27−
28−
29−mapToPureEvent :: IntMap a -> Event (Pure Int) a
30−mapToPureEvent m = Event $ flip IntMap.lookup m
31−
32−type TimeM = (->) Int
33−newtype PurePlan a = PurePlan { unPlan :: StateT IntSet TimeM a } deriving (Functor, Applicative, Monad, MonadFix)
34−
35−liftPlan :: TimeM a -> PurePlan a
36−liftPlan = PurePlan . lift
37−
38−instance MonadHold (Pure Int) PurePlan where
39− hold initial = liftPlan . hold initial
40− holdDyn initial = liftPlan . holdDyn initial
41− holdIncremental initial = liftPlan . holdIncremental initial
42−
43−instance MonadSample (Pure Int) PurePlan where
44− sample = liftPlan . sample
45−
46−
47−instance TestPlan (Pure Int) PurePlan where
48− plan occs = do
49− PurePlan . modify $ IntSet.union (IntMap.keysSet m)
50− return $ mapToPureEvent m
51− where m = IntMap.fromList (first fromIntegral <$> occs)
52−
53−runPure :: PurePlan a -> (a, IntSet)
54−runPure (PurePlan p) = runStateT p mempty $ 0
55−
56−relavantTimes :: IntSet -> IntSet
57−relavantTimes occs = IntSet.fromList [0..l + 1]
58− where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
59−
60−testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
61−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
62−
63−testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
64−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
65−
66−
67−
68−
69−
70−
test/Reflex/Plan/Reflex.hsdeleted−135
@@ -1,135 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE StandaloneDeriving #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−module Reflex.Plan.Reflex
12− ( TestPlan(..)
13− , runPlan
14− , Plan (..)
15− , Schedule
16− , Firing (..)
17− , MonadIORef
18−
19−
20− , readSchedule
21− , readSchedule_
22− , testSchedule
23− , readEvent'
24− , makeDense
25−
26− , runTestE
27− , runTestB
28−
29− ) where
30−
31−import Reflex.Class
32−import Reflex.Host.Class
33−import Reflex.TestPlan
34−
35−import Control.Applicative
36−import Control.Monad
37−import Control.Monad.Identity
38−import Control.Monad.State.Strict
39−
40−import Control.Monad.Ref
41−import Data.Dependent.Sum (DSum (..))
42−import qualified Data.IntMap.Strict as IntMap
43−import Data.Maybe
44−import Data.Monoid
45−import Data.Traversable (sequenceA, traverse)
46−
47−import Control.DeepSeq
48−import Control.Exception
49−import Data.IntMap.Strict (IntMap)
50−import Data.IORef
51−import System.Mem
52−
53−-- Note: this import must come last to silence warnings from AMP
54−import Prelude
55−
56−type MonadIORef m = (MonadIO m, MonadRef m, Ref m ~ Ref IO)
57−
58−data Firing t where
59− Firing :: IORef (Maybe (EventTrigger t a)) -> a -> Firing t
60−
61−
62−type Schedule t = IntMap [Firing t]
63−
64−-- Implementation of a TestPlan in terms of ReflexHost
65−newtype Plan t a = Plan (StateT (Schedule t) (HostFrame t) a)
66−
67−deriving instance ReflexHost t => Functor (Plan t)
68−deriving instance ReflexHost t => Applicative (Plan t)
69−deriving instance ReflexHost t => Monad (Plan t)
70−
71−deriving instance ReflexHost t => MonadSample t (Plan t)
72−deriving instance ReflexHost t => MonadHold t (Plan t)
73−deriving instance ReflexHost t => MonadFix (Plan t)
74−
75−
76−instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => TestPlan t (Plan t) where
77− plan occurances = Plan $ do
78− (e, ref) <- newEventWithTriggerRef
79− modify (IntMap.unionWith mappend (firings ref))
80− return e
81−
82− where
83− firings ref = IntMap.fromList (makeFiring ref <$> occurances)
84− makeFiring ref (t, a) = (fromIntegral t, [Firing ref a])
85−
86−
87−firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
88−firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
89−
90−runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
91−runPlan (Plan p) = runHostFrame $ runStateT p mempty
92−
93−
94−makeDense :: Schedule t -> Schedule t
95−makeDense s = fromMaybe (emptyRange 0) $ do
96− (end, _) <- fst <$> IntMap.maxViewWithKey s
97− return $ IntMap.union s (emptyRange end)
98− where
99− emptyRange end = IntMap.fromList (zip [0..end + 1] (repeat []))
100−
101−
102−-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
103−-- after the last event)
104−-- performGC is called at each frame to test for GC issues
105−testSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
106−testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
107−
108−readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
109−readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
110−
111−readSchedule_ :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m ()
112−readSchedule_ schedule readResult = mapM_ (uncurry $ triggerFrame readResult) $ IntMap.toList schedule
113−
114−triggerFrame :: (MonadReflexHost t m, MonadIORef m, NFData a) => ReadPhase m a -> Int -> [Firing t] -> m a
115−triggerFrame readResult _ occs = do
116− triggers <- catMaybes <$> traverse firingTrigger occs
117− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
118−
119−readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
120−readEvent' = readEvent >=> sequenceA
121−
122−
123−-- Convenience functions for running tests producing Events/Behaviors
124−runTestB :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Behavior t a) -> m (IntMap a)
125−runTestB p = do
126− (b, s) <- runPlan p
127− testSchedule s $ sample b
128−
129−runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
130−runTestE p = do
131− (e, s) <- runPlan p
132− h <- subscribeEvent e
133− testSchedule s (readEvent' h)
134−
135−
test/Reflex/Test.hsdeleted−66
@@ -1,66 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Reflex.Test
9− ( testAgreement
10− , compareResult
11− , runTests
12−
13− , module Reflex.TestPlan
14−
15− ) where
16−
17−import Reflex.Spider
18−
19−import Reflex.TestPlan
20−
21−import Reflex.Plan.Pure
22−import Reflex.Plan.Reflex
23−
24−import Control.Monad
25−import Data.Monoid
26−
27−import Data.IntMap (IntMap)
28−
29−--import Data.Foldable
30−import System.Exit
31−
32−import Prelude
33−
34−
35−testAgreement :: TestCase -> IO Bool
36−testAgreement (TestE p) = do
37− spider <- runSpiderHost $ runTestE p
38− let results = [("spider", spider)]
39−
40− compareResult results (testEvent $ runPure p)
41−
42−testAgreement (TestB p) = do
43− spider <- runSpiderHost $ runTestB p
44− let results = [("spider", spider)]
45−
46− compareResult results (testBehavior $ runPure p)
47−
48−
49−compareResult :: (Show a, Eq a) => [(String, IntMap a)] -> IntMap a -> IO Bool
50−compareResult results expected = fmap and $ forM results $ \(name, r) -> do
51−
52− when (r /= expected) $ do
53− putStrLn ("Got: " ++ show (name, r))
54− putStrLn ("Expected: " ++ show expected)
55− return (r == expected)
56−
57−
58−runTests :: [(String, TestCase)] -> IO ()
59−runTests testCases = do
60− results <- forM testCases $ \(name, test) -> do
61− putStrLn $ "Test: " <> name
62− testAgreement test
63− exitWith $ if and results
64− then ExitSuccess
65− else ExitFailure 1
66−
test/Reflex/Test/CrossImpl.hsdeleted−244
@@ -1,244 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE LambdaCase #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE NoMonomorphismRestriction #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−module Reflex.Test.CrossImpl (test) where
16−
17−import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
18−
19−import Control.Monad.Ref
20−import Reflex.Class
21−import Reflex.Dynamic
22−import Reflex.Host.Class
23−import qualified Reflex.Pure as P
24−import qualified Reflex.Spider.Internal as S
25−
26−import Control.Arrow (second, (&&&))
27−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29−import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30−import Data.Dependent.Map (DSum (..))
31−import Data.Foldable
32−import Data.Map.Strict (Map)
33−import qualified Data.Map.Strict as Map
34−import qualified Data.Set as Set
35−import Data.Traversable
36−import System.Exit
37−import System.Mem
38−
39−import System.IO.Unsafe
40−
41−mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
42−mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
43− Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
44− Just (_, v) -> v
45−
46−mapToPureEvent :: Map Int a -> Event PureReflexDomain a
47−mapToPureEvent m = P.Event $ flip Map.lookup m
48−
49−relevantTestingTimes :: (Map Int a, Map Int b) -> [Int]
50−relevantTestingTimes (b, e) = case Set.minView &&& Set.maxView $ Map.keysSet b `Set.union` Map.keysSet e of
51− (Just (t0, _), Just (t1, _)) -> [t0..t1+1] -- We need to go to b+1 to see the result of the final event
52− _ -> [] -- Doesn't actually make much sense
53−
54−type PureReflexDomain = P.Pure Int
55−type TimeM = (->) Int
56−
57−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)
58−testPure builder (b, e) =
59− let (P.Behavior b', P.Event e') = ($ 0) $ builder (mapToPureBehavior b, mapToPureEvent e)
60− relevantTimes = relevantTestingTimes (b, e)
61− e'' = Map.mapMaybe id $ Map.fromList $ map (id &&& e') relevantTimes
62− b'' = Map.fromList $ map (id &&& b') relevantTimes
63− in (b'', e'')
64−
65−class MapMSignals a a' t t' | a -> t, a' -> t', a t' -> a', a' t -> a where
66− mapMSignals :: Monad m => (forall b. Behavior t b -> m (Behavior t' b)) -> (forall b. Event t b -> m (Event t' b)) -> a -> m a'
67−
68−instance MapMSignals (Behavior t a) (Behavior t' a) t t' where
69− mapMSignals fb _ = fb
70−
71−instance MapMSignals (Event t a) (Event t' a) t t' where
72− mapMSignals _ fe = fe
73−
74−instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
75− mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
76−
77−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)
78−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
79− (re, reTrigger) <- newEventWithTriggerRef
80− (rb, rbTrigger) <- newEventWithTriggerRef
81− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
82− (b', e') <- runHostFrame $ builder (b, re)
83− e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
84− let times = relevantTestingTimes (bMap, eMap)
85− liftIO performGC
86− outputs <- forM times $ \t -> do
87− forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
88− bOutput <- sample b'
89− eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
90− mret <- readRef reTrigger
91− let firing = case mret of
92− Just ret -> [ret :=> Identity val]
93− Nothing -> []
94− fireEventsAndRead firing $ sequence =<< readEvent e'Handle
95− liftIO performGC
96− return (t, (bOutput, eOutput))
97− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
98−
99−tracePerf :: Show a => a -> b -> b
100−tracePerf = flip const
101−
102−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
103−testAgreement builder inputs = do
104− let identityResult = testPure builder inputs
105− tracePerf "---------" $ return ()
106− let spiderResult = testSpider builder inputs
107− tracePerf "---------" $ return ()
108− let resultsAgree = identityResult == spiderResult
109− if resultsAgree
110− then do putStrLn "Success:"
111− print identityResult
112− else do putStrLn "Failure:"
113− putStrLn $ "Pure result: " <> show identityResult
114− putStrLn $ "Spider result: " <> show spiderResult
115− return resultsAgree
116−
117−type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
118−
119−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))
120−
121−testCases :: [(String, TestCase)]
122−testCases =
123− [ (,) "hold" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
124− b' <- hold "123" e
125− return (b', e)
126− , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
127− e' <- updated <$> count e
128− b' <- hold (0 :: Int) e'
129− return (b', e')
130− , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
131− e' <- onceE $ leftmost [e, e]
132− return (b, e')
133− , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134− let e' = fmap (const e) e
135− b' <- hold never e'
136− let e'' = switch b'
137− return (b, e'')
138− , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
139− let e' = flip pushAlways e $ const $ do
140− let eab = splitRecombineEvent e
141− switch <$> hold eab never
142− e'' = coincidence e'
143− return (b, e'')
144− , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145− let e' = flip pushAlways e $ const $ do
146− let eab = splitRecombineEvent e
147− switch <$> hold eab (fmap (const e) e)
148− e'' = coincidence e'
149− return (b, e'')
150− , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
151− let e' = leftmost [e, e]
152− e'' <- switch <$> hold e' (fmap (const e) e)
153− return (b, e'')
154− , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
155− let e' = fmap (const e) e
156− e'' <- switchPromptly never e'
157− return (b, e'')
158− , (,) "switchPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
159− let e' = fmap (const e) e
160− e'' <- switchPromptly never $ leftmost [e', e']
161− return (b, e'')
162− , (,) "switchPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
163− let e' = leftmost [e, e]
164− e'' <- switchPromptly never (fmap (const e) e')
165− return (b, e'')
166− , (,) "switchPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
167− let e' = leftmost [e, e]
168− e'' <- switchPromptly never (fmap (const e') e)
169− return (b, e'')
170− , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171− let e' = leftmost [e, e]
172− e'' <- switch <$> hold never (fmap (const e') e)
173− return (b, e'')
174− , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175− let e' = flip push e $ \_ -> do
176− Just <$> onceE e
177− e'' <- switchPromptly never e'
178− return (b, e'')
179− , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
180− let e' = flip pushAlways e $ \_ -> do
181− switchPromptly e never
182− e'' <- switchPromptly never e'
183− return (b, e'')
184− , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
185− let e' = flip pushAlways e $ \_ -> return e
186− e'' = coincidence e'
187− return (b, e'')
188− , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
189− let e' = flip pushAlways e $ \_ -> return $ leftmost [e, e]
190− e'' = coincidence e'
191− return (b, e'')
192− , (,) "coincidence-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
193− let e' = flip pushAlways e $ \_ -> return $ coincidence $ fmap (const e) e
194− e'' = coincidence e'
195− return (b, e'')
196− , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
197− let e' = flip pushAlways e $ \_ -> onceE e
198− e'' = coincidence e'
199− return (b, e'')
200− , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
201− let eChild = flip pushAlways e $ const $ do
202− let eNewValues = leftmost [e, e]
203− return $ coincidence $ fmap (const eNewValues) eNewValues
204− e' = coincidence eChild
205− return (b, e')
206− , (,) "coincidence-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
207− let eChild = flip pushAlways e $ const $ do
208− let e' = coincidence $ fmap (const e) e
209− return $ leftmost [e', e']
210− e'' = coincidence eChild
211− return (b, e'')
212− , (,) "coincidence-7" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
213− let e' = leftmost [e, e]
214− eCoincidences = coincidence $ fmap (const e') e
215− return (b, eCoincidences)
216− , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
217− eo <- onceE e
218− bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219− let b' = pull $ sample =<< sample bb
220− return (b', e)
221− , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222− bb <- hold "b" e
223− bd <- hold never . fmap (const e) =<< onceE e
224− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
225− let eInner = switch bd
226− e' = leftmost [eOuter, eInner]
227− return (b, e')
228− ]
229−
230−splitRecombineEvent :: Reflex t => Event t a -> Event t String
231−splitRecombineEvent e =
232− let ea = "a" <$ e
233− eb = "b" <$ e
234− in leftmost [ea, eb]
235−
236−
237−test :: IO ()
238−test = do
239− results <- forM testCases $ \(name, TestCase inputs builder) -> do
240− putStrLn $ "Test: " <> name
241− testAgreement builder inputs
242− exitWith $ if and results
243− then ExitSuccess
244− else ExitFailure 1
test/Reflex/Test/Micro.hsdeleted−263
@@ -1,263 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Reflex.Test.Micro (testCases) where
9−
10−import Reflex
11−import Reflex.TestPlan
12−
13−import Control.Applicative
14−import Control.Monad
15−import Data.Char
16−import Data.Functor.Misc
17−import Data.Monoid
18−
19−import Data.Foldable
20−
21−import qualified Data.Map as Map
22−
23−import Prelude
24−
25−testCases :: [(String, TestCase)]
26−testCases =
27− [ testB "hold" $ hold "0" =<< events1
28−
29− , testB "count" $ do
30− b <- current <$> (count =<< events2)
31− return $ (+ (0::Int)) <$> b
32−
33− , testB "pull-1" $ do
34− b <- hold "0" =<< events1
35− return (pull $ sample $ pull $ sample b)
36−
37− , testB "pull-2" $ do
38− b1 <- behavior1
39− return (pull $ liftA2 (<>) (sample b1) (sample b1))
40−
41− , testB "pull-3" $ do
42− b1 <- behavior1
43− b2 <- behavior2
44− return (pull $ liftA2 (<>) (sample b1) (sample b2))
45−
46− , testB "pull-4" $ do
47− es <- planList ["a", "b", "c"]
48− e <- plan [(0, ())]
49− b <- hold (constant "") $
50− pushAlways (const $ hold "z" es) e
51− return (join b)
52−
53− , testE "tag-1" $ do
54− b1 <- behavior1
55− e <- events2
56− return (tag b1 e)
57−
58− , testE "tag-2" $ do
59− b1 <- behavior1
60− e <- events2
61− return (tag (map toUpper <$> b1) e)
62−
63− , testE "attach-1" $ do
64− b1 <- behavior1
65− e <- events2
66− return (attachWith (++) (map toUpper <$> b1) e)
67−
68− , testE "leftmost" $ liftA2 leftmost2 events1 events2
69−
70− , testE "appendEvents-1" $ liftA2 mappend events1 events2
71−
72− , testE "appendEvents-2" $ liftA2 mappend events3 events2
73−
74− , testE "merge-1" $ do
75− e <- events1
76− return $ leftmost ["x" <$ e, "y" <$ e]
77−
78− , testE "merge-2" $ do
79− e <- events1
80− let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
81− let ee = flip pushAlways e $ const $ return m
82− return $ coincidence ee
83−
84− , testE "headE-1" $ do
85− e <- events1
86− headE $ leftmost [e, e]
87−
88− , testE "headE-2" $ do
89− e <- events1
90− b <- hold never (e <$ e)
91− headE $ switch b
92−
93− , testE "switch-1" $ do
94− e <- events1
95− b <- hold never (e <$ e)
96− return $ switch b
97−
98− , testE "switch-2" $ do
99− e <- events1
100− return $ coincidence $ flip pushAlways e $ const $ do
101− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e)
102−
103− , testE "switch-3" $ do
104− e <- events1
105− return $ coincidence $ flip pushAlways e $ const $ do
106− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never
107−
108− , testE "switch-4" $ do
109− e <- events1
110− switch <$> hold (deep e) (e <$ e)
111−
112− , testE "switch-5" $ do
113− e <- events1
114− return $ coincidence $ flip pushAlways e $ const $
115− return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e]
116−
117− , testE "switch-6" $ do
118− e <- events1
119− return $ coincidence $ flip pushAlways e $ const $ do
120− switch <$> hold ("x" <$ e) (e <$ e)
121−
122− , testE "switchPromptly-1" $ do
123− e <- events1
124− let e' = e <$ e
125− switchPromptly never $ e <$ e'
126−
127− , testE "switchPromptly-2" $ do
128− e <- events1
129− switchPromptly never $ deep (e <$ e)
130−
131− , testE "switchPromptly-3" $ do
132− e <- events1
133− switchPromptly never $ (e <$ deep e)
134−
135− , testE "switchPromptly-4" $ do
136− e <- events1
137− switchPromptly never $ (deep e <$ e)
138−
139− , testE "switch-5" $ do
140− e <- events1
141− switch <$> hold never (deep e <$ e)
142−
143− , testE "switchPromptly-5" $ do
144− e <- events1
145− switchPromptly never $ flip push e $
146− const (Just <$> headE e)
147−
148− , testE "switchPromptly-6" $ do
149− e <- events1
150− switchPromptly never $ flip pushAlways e $
151− const (switchPromptly e never)
152−
153− , testE "coincidence-1" $ do
154− e <- events1
155− return $ coincidence $ flip pushAlways e $
156− const $ return e
157−
158− , testE "coincidence-2" $ do
159− e <- events1
160− return $ coincidence $ flip pushAlways e $
161− const $ return (deep e)
162−
163− , testE "coincidence-3" $ do
164− e <- events1
165− return $ coincidence $ flip pushAlways e $
166− const $ return (coincidence (e <$ e))
167−
168− , testE "coincidence-4" $ do
169− e <- events1
170− return $ coincidence $ flip pushAlways e $
171− const (headE e)
172−
173− , testE "coincidence-5" $ do
174− e <- events1
175− return $ coincidence $ flip pushAlways e $ const $ do
176− let e' = deep e
177− return (coincidence (e' <$ e'))
178−
179− , testE "coincidence-6" $ do
180− e <- events1
181− return $ coincidence $ flip pushAlways e $ const $ do
182− let e' = coincidence (e <$ e)
183− return $ deep e'
184−
185− , testE "coincidence-7" $ do
186− e <- events1
187− return $ coincidence (deep e <$ e)
188−
189− , testB "holdWhileFiring" $ do
190− e <- events1
191− eo <- headE e
192− bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
193− return $ pull $ sample =<< sample bb
194−
195− , testE "joinDyn" $ do
196− e <- events1
197− bb <- hold "b" e
198− bd <- hold never . fmap (const e) =<< headE e
199−
200− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
201− let eInner = switch bd
202− return $ leftmost [eOuter, eInner]
203−
204− , testB "foldDyn" $ do
205− d <- foldDyn (++) "0" =<< events1
206− return (current d)
207−
208− , testB "mapDyn" $ do
209− d <- foldDyn (++) "0" =<< events1
210− return $ current $ fmap (map toUpper) d
211−
212− , testB "combineDyn" $ do
213− d1 <- foldDyn (++) "0" =<< events1
214− d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
215−
216− return $ current $ zipDynWith (<>) d1 d2
217− , testE "fan-1" $ do
218− e <- fmap toMap <$> events1
219− let es = select (fanMap e) . Const2 <$> values
220−
221− return (mergeList es)
222−
223− , testE "fan-2" $ do
224− e <- fmap toMap <$> events3
225− let es = select (fanMap e) . Const2 <$> values
226−
227− return (mergeList es)
228−
229− , testE "fan-3" $ do
230− f <- fanMap . fmap toMap <$> events3
231− return $ select f (Const2 'c')
232−
233− , testE "fan-4" $ do
234− e <- fmap toMap <$> events1
235− return $ toUpper <$> select (fanMap e) (Const2 'a')
236−
237− , testE "fan-5" $ do
238− e <- fmap toMap <$> events2
239− return $ toUpper <$> select (fanMap e) (Const2 'c')
240−
241− , testE "fan-6" $ do
242− f <- fanMap . fmap toMap <$> events1
243− return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
244−
245− ] where
246−
247− events1, events2, events3 :: TestPlan t m => m (Event t String)
248− events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
249− events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
250−
251− events3 = liftA2 mappend events1 events2
252−
253− values = "abcde"
254− toMap str = Map.fromList $ map (\c -> (c, c)) str
255−
256− behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
257− behavior1 = hold "1" =<< events1
258− behavior2 = hold "2" =<< events2
259−
260− deep e = leftmost [e, e]
261− leftmost2 e1 e2 = leftmost [e1, e2]
262−
263−
test/Reflex/TestPlan.hsdeleted−52
@@ -1,52 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−module Reflex.TestPlan
10− ( TestPlan(..)
11−
12− , TestCase (..)
13− , testE, testB
14− , TestE, TestB
15−
16− , planList
17−
18− ) where
19−
20−import Control.DeepSeq
21−import Control.Monad.Fix
22−import Data.Word
23−import Reflex.Class
24−
25−
26−import Prelude
27−
28−class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
29− -- | Speicify a plan of an input Event firing
30− -- Occurances must be in the future (i.e. Time > 0)
31− -- Initial specification is
32−
33− plan :: [(Word, a)] -> m (Event t a)
34−
35−
36−planList :: TestPlan t m => [a] -> m (Event t a)
37−planList xs = plan $ zip [1..] xs
38−
39−type TestE a = forall t m. TestPlan t m => m (Event t a)
40−type TestB a = forall t m. TestPlan t m => m (Behavior t a)
41−
42−data TestCase where
43− TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
44− TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
45−
46−-- Helpers to declare test cases
47−testE :: (Eq a, Show a, NFData a) => String -> TestE a -> (String, TestCase)
48−testE name test = (name, TestE test)
49−
50−testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
51−testB name test = (name, TestB test)
52−
test/hlint.hs+34−11
@@ -1,16 +1,39 @@
1−import Language.Haskell.HLint3 (hlint)
1+module Main where
2+
3+import Control.Monad
4+import Language.Haskell.HLint (hlint)
5+import System.Directory
26import System.Exit (exitFailure, exitSuccess)
7+import System.FilePath
8+import System.FilePath.Find
39
410main :: IO ()
511main = do
6− ideas <- hlint
7− [ "."
8− , "--ignore=Redundant do"
9− , "--ignore=Use camelCase"
10− , "--ignore=Redundant $"
11− , "--ignore=Use &&&"
12− , "--ignore=Use const"
13− , "--ignore=Use >=>"
14− , "--ignore=Use ."
15− ]
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
1639 if null ideas then exitSuccess else exitFailure
test/semantics.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Main (main) where
9−
10−import Reflex.Test
11−
12−import Data.Bifunctor
13−import Data.Functor
14−import Data.List
15−import qualified Reflex.Bench.Focused as Focused
16−import qualified Reflex.Test.Micro as Micro
17−
18−import System.Environment
19−import System.Exit
20−
21−import Prelude
22−
23−matchPrefixes :: [String] -> (String -> Bool)
24−matchPrefixes [] = const True
25−matchPrefixes args = \name -> any (`isPrefixOf` name) args
26−
27−
28−main :: IO ()
29−main = do
30− args <- getArgs
31−
32− case args of
33− ["--list"] -> mapM_ putStrLn (fst <$> allTests) >> exitWith (ExitFailure 1)
34− _ -> case filter (matchPrefixes args . fst) allTests of
35− [] -> putStrLn "filter did not match any tests" >> exitWith (ExitFailure 1)
36− tests -> runTests tests
37−
38− where
39− allTests = concat
40− [ makeGroup "micro" Micro.testCases
41− , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
42− , makeGroup "firing 1000" (Focused.firing 1000)
43− , makeGroup "merge 100" (Focused.merging 100)
44− , makeGroup "fan 50" (Focused.fans 50)
45− ]
46−
47− makeGroup name tests = first (\test -> intercalate "/" [name, test]) <$> tests
48−
49−
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