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