on develop help sign in
74 files changed+1955−9385
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+47 .stylish-haskell.yaml+2−3 .travis.yml−15 CONTRIBUTING.md+66 ChangeLog.md+61 Quickref.md−183 README.md+44−11 bench-cbits/checkCapability.c+11−12 bench/Main.hs−109 bench/RunAll.hs−157 cabal.project+1 default.nix−34 dep/reflex-platform/default.nix+8 dep/reflex-platform/github.json+8 patch.cabal+100 reflex.cabal−252 release.nix+49 src/Data/Functor/Misc.hs+25−133 src/Data/Monoid/DecidablyEmpty.hs+115 src/Data/Patch.hs+95 src/Data/Patch/Class.hs+45 src/Data/Patch/DMap.hs+93 src/Reflex/Patch/DMapWithMove.hs → src/Data/Patch/DMapWithMove.hs+158−68 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−124 src/Reflex.hs−26 src/Reflex/Class.hs−1101 src/Reflex/Dynamic.hs−601 src/Reflex/Dynamic/TH.hs−130 src/Reflex/Dynamic/Uniq.hs−105 src/Reflex/DynamicWriter.hs−191 src/Reflex/EventWriter.hs−170 src/Reflex/FunctorMaybe.hs−29 src/Reflex/Host/Class.hs−299 src/Reflex/Optimizer.hs−51 src/Reflex/Patch.hs−33 src/Reflex/Patch/Class.hs−20 src/Reflex/Patch/DMap.hs−61 src/Reflex/Patch/Map.hs−48 src/Reflex/Patch/MapWithMove.hs−156 src/Reflex/PerformEvent/Base.hs−157 src/Reflex/PerformEvent/Class.hs−64 src/Reflex/PostBuild/Base.hs−142 src/Reflex/PostBuild/Class.hs−34 src/Reflex/Pure.hs−189 src/Reflex/Query/Base.hs−255 src/Reflex/Query/Class.hs−77 src/Reflex/Requester/Base.hs−156 src/Reflex/Requester/Class.hs−87 src/Reflex/Spider.hs−22 src/Reflex/Spider/Internal.hs−2192 src/Reflex/Spider/SpiderTimeline.include.hs−122 src/Reflex/Spider/SpiderTimelineHost.include.hs−57 src/Reflex/TriggerEvent/Base.hs−129 src/Reflex/TriggerEvent/Class.hs−38 stylize+4 test/EventWriterT.hs−61 test/GC.hs−95 test/Reflex/Bench/Focused.hs−369 test/Reflex/Plan/Pure.hs−70 test/Reflex/Plan/Reflex.hs−135 test/Reflex/Test.hs−66 test/Reflex/Test/CrossImpl.hs−251 test/Reflex/Test/Micro.hs−285 test/Reflex/TestPlan.hs−52 test/RequesterT.hs−70 test/hlint.hs+34−14 test/rootCleanup.hs−25 test/semantics.hs−49 test/tests.hs+43
.github/workflows/haskell.ymlnew file+47
@@ -0,0 +1,47 @@
1+name: github-action
2+
3+on: [push, pull_request]
4+
5+jobs:
6+ build:
7+ strategy:
8+ matrix:
9+ ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2']
10+ os: ['ubuntu-latest', 'macos-latest']
11+ exclude:
12+ # There are some linker warnings in 802 on darwin that
13+ # cause compilation to fail
14+ # See https://github.com/NixOS/nixpkgs/issues/25139
15+ - ghc: '8.0.2'
16+ os: 'macos-latest'
17+ runs-on: ${{ matrix.os }}
18+
19+ name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
20+ steps:
21+ - uses: actions/checkout@v2
22+ - uses: actions/setup-haskell@v1
23+ with:
24+ ghc-version: ${{ matrix.ghc }}
25+ - name: Cache
26+ uses: actions/cache@v1
27+ env:
28+ cache-name: cache-cabal
29+ with:
30+ path: ~/.cabal
31+ key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
32+ restore-keys: |
33+ ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
34+ ${{ runner.os }}-${{ matrix.ghc }}-build-
35+ ${{ runner.os }}-${{ matrix.ghc }}-
36+ ${{ runner.os }}
37+
38+ - name: Install dependencies
39+ run: |
40+ cabal update
41+ cabal build --only-dependencies --enable-tests --enable-benchmarks
42+ - name: Build
43+ run: cabal build --enable-tests --enable-benchmarks all
44+ - name: Run tests
45+ run: cabal test --enable-tests all
46+ - name: Build Docs
47+ run: cabal haddock
.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−15
@@ -1,15 +0,0 @@
1−# Cache Buster: 0
2−language: nix
3−cache:
4− directories:
5− - /nix
6−matrix:
7− include:
8− - env: COMPILER=ghc7103
9− - env: COMPILER=ghc801
10− - env: COMPILER=ghcjs
11−script:
12− - set -e
13− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
14− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
15−
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−183
@@ -1,183 +0,0 @@
1−# Reflex Quick(ish) Reference
2−
3−## Typeclasses
4−
5−Many Reflex functions operate in monadic context `m a`, where the monad 'm' supports various additional typeclasses such as MonadWidget, MonadHold, or MonadSample in addition to Monad itself. The actual 'm' in use will be determined by the top-level entry point of the FRP host (such as Reflex-Dom -- see the bottom of the Reflex-Dom quick reference for details).
6−
7−The function signatures here have been simplified by removing many typeclass constraints and adding simple annotations to each function. Also the ubiquitous 't' type parameter has been removed.
8−
9−Some of these functions are *pure*: They operate on Events, Behaviors, or Dynamics uniformly without regard to the "current time". Other functions must operate in some monadic context, because they produce a result "as of now" (e.g., any function that takes an "initial" value, or returns a "current" value). Annotations are used to distinguish these cases:
10−
11−```haskell
12−[ ] -- Pure function
13−[S] -- Function runs in any monad supporting MonadSample
14−[H] -- Function runs in any monad supporting MonadHold
15−```
16−
17−Since MonadHold depends on MonadSample, any [S] function also runs in [H] context.
18−
19−## Functions producing Event
20−
21−```haskell
22−-- Trivial Event
23−[ ] never :: Event a
24−
25−-- Extract Event from Dynamic
26−[ ] updated :: Dynamic a -> Event a
27−
28−-- Transform Event to Event using function
29−[ ] fmap :: (a -> b) -> Event a -> Event b
30−[ ] fmapMaybe :: (a -> Maybe b) -> Event a -> Event b
31−[ ] ffilter :: (a -> Bool) -> Event a -> Event a
32−[ ] ffor :: Event a -> (a -> b) -> Event b
33−[ ] fforMaybe :: Event a -> (a -> Maybe b) -> Event b
34−
35−-- Event to identical Event with debug trace. (Only prints if Event is ultimately used.)
36−[ ] traceEvent :: Show a => String -> Event a -> Event a
37−[ ] traceEventWith :: (a -> String) -> Event a -> Event a
38−
39−-- Transform Event to Event by sampling Behavior or Dynamic
40−[ ] gate :: Behavior Bool -> Event a -> Event a
41−[ ] tag :: Behavior a -> Event b -> Event a
42−[ ] tagPromptlyDyn :: Dynamic a -> Event b -> Event a
43−[ ] attach :: Behavior a -> Event b -> Event (a, b)
44−[ ] attachPromptlyDyn :: Dynamic a -> Event b -> Event (a, b)
45−[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46−[ ] attachPromptlyDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47−[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48−[ ] attachPromptlyDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49−
50−-- Combine multiple Events
51−[ ] <> :: Semigroup a => Event a -> Event a -> Event a
52−[ ] difference :: Event a -> Event b -> Event a
53−[ ] align :: Event a -> Event b -> Event (These a b)
54−[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
55−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
56−[ ] leftmost :: [Event a] -> Event a
57−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
58−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
59−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
60−
61−-- Efficient one-to-many fanout
62−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
63−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
64−[ ] select :: EventSelector k -> k a -> Event a
65−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
66−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
67−
68−-- Event to Event via function that can sample current values
69−[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
70−[ ] pushAlways :: (a -> m b ) -> Event a -> Event b
71− -- Note supplied function operates in [H] context
72−
73−-- Split Event into next occurrence and all other future occurrences, as of now.
74−[H] headE :: Event a -> m (Event a)
75−[H] tailE :: Event a -> m (Event a)
76−[H] headTailE :: Event a -> m (Event a, Event a)
77−```
78−
79−## Functions producing Behavior
80−
81−```haskell
82−-- Trivial Behavior
83−[ ] constant :: a -> Behavior a
84−
85−-- Extract Behavior from Dynamic
86−[ ] current :: Dynamic a -> Behavior a
87−
88−-- Create Behavior with given initial value, updated when the Event fires.
89−[H] hold :: a -> Event a -> m (Behavior a)
90−
91−-- Transform Behavior to Behavior using function
92−[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
93−[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
94−[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
95−[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
96−[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
97−-- ... plus many more due to typeclass membership
98−
99−-- Behavior to Behavior by sampling current values
100−[S] sample :: Behavior a -> m a
101−[ ] pull :: m a -> Behavior a
102− -- Note supplied value is in [S] context
103−```
104−
105−## Functions producing Dynamic
106−
107−```haskell
108−-- Trivial Dynamic
109−[ ] constDyn :: a -> Dynamic a
110−
111−-- Create Dynamic with given initial value, updated (various ways) when the Event fires.
112−[H] holdDyn :: a -> Event a -> m (Dynamic a)
113−[H] foldDyn :: (a -> b -> b ) -> b -> Event a -> m (Dynamic b)
114−[H] foldDynMaybe :: (a -> b -> Maybe b ) -> b -> Event a -> m (Dynamic b)
115−[H] foldDynM :: (a -> b -> m' b ) -> b -> Event a -> m (Dynamic b)
116−[H] foldDynMaybeM :: (a -> b -> m' (Maybe b)) -> b -> Event a -> m (Dynamic b)
117− -- Note m' supplies [H] context
118−
119−-- Initial value is 0; counts Event firings from now.
120−[H] count :: Num b => Event a -> m (Dynamic b)
121−-- Initial Bool value is supplied; toggles at each Event firing.
122−[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
123−
124−-- Transform Dynamic to Dynamic using function
125−[ ] ffor :: Dynamic a -> (a -> b) -> Dynamic b
126−[ ] fmap :: (a -> b) -> Dynamic a -> Dynamic b
127−[ ] splitDynPure :: Dynamic (a, b) -> (Dynamic a, Dynamic b)
128−
129−-- Combine multiple Dynamics
130−[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
131−[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
132−[ ] <*> :: Dynamic (a -> b) -> Dynamic a -> Dynamic b
133−[ ] >>= :: Dynamic a -> (a -> Dynamic b) -> Dynamic b
134−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
135−
136−-- Efficient one-to-many fanout
137−[ ] demux :: Ord k => Dynamic k -> Demux k
138−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
139−
140−-- Dynamic to Dynamic, removing updates w/o value change
141−[ ] uniqDyn :: Eq a => Dynamic a -> Dynamic a
142−
143−-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
144−[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
145−[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
146−```
147−
148−## Flattening functions
149−
150−These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
151−
152−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.
153−
154−```haskell
155−-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
156−[ ] switch :: Behavior (Event a) -> Event a
157−
158−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
159−[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
160−
161−-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
162−[ ] coincidence :: Event (Event a) -> Event a
163−
164−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
165−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
166−-- a Behavior whose value is equal to the current value of the current value of
167−-- the input.
168−[ ] join :: Behavior (Behavior a) -> Behavior a
169−
170−
171−-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
172−-- Output updated whenever inner OR outer Dynamic updates.
173−[ ] join :: Dynamic (Dynamic a) -> Dynamic a
174−[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
175−
176−-- Analogous to 'hold': Create a Behavior that is initially identical to the
177−-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
178−[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
179−
180−-- Similar to above, for Events. Created Event initially tracks the first argument.
181−-- At switchover, the output Event immediately tracks the new Event.
182−[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
183−```
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 x 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−34
@@ -1,34 +0,0 @@
1−{ mkDerivation, ghc, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl, primitive, ref-tf, semigroupoids
5−, semigroups, split, stdenv, stm, syb, template-haskell
6−, these, transformers, transformers-compat, prim-uniq
7−, data-default
8−, useTemplateHaskell ? true
9−}:
10−mkDerivation {
11− pname = "reflex";
12− version = "0.5.0";
13− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
14− libraryHaskellDepends = [
15− base bifunctors containers dependent-map dependent-sum
16− exception-transformers lens
17− MemoTrie monad-control mtl primitive ref-tf semigroupoids
18− semigroups stm syb template-haskell these transformers
19− transformers-compat prim-uniq
20− base bifunctors containers deepseq dependent-map dependent-sum
21− mtl ref-tf split transformers data-default
22− ] ++ (if !useTemplateHaskell then [] else [
23− haskell-src-exts haskell-src-meta
24− ]);
25− testHaskellDepends = if ghc.isGhcjs or false then [] else [
26− hlint
27− ];
28− configureFlags = if useTemplateHaskell then [] else [
29− "-f-use-template-haskell"
30− ];
31− homepage = "https://github.com/reflex-frp/reflex";
32− description = "Higher-order Functional Reactive Programming";
33− license = stdenv.lib.licenses.bsd3;
34−}
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−252
@@ -1,252 +0,0 @@
1−Name: reflex
2−Version: 0.5.0
3−Synopsis: Higher-order Functional Reactive Programming
4−Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5−License: BSD3
6−License-file: LICENSE
7−Author: Ryan Trinkle
8−Maintainer: ryan.trinkle@gmail.com
9−Stability: Experimental
10−Category: FRP
11−Build-type: Simple
12−Cabal-version: >=1.9.2
13−homepage: https://github.com/reflex-frp/reflex
14−bug-reports: https://github.com/reflex-frp/reflex/issues
15−
16−flag specialize-to-spidertimeline-global
17− description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18− default: False
19− manual: True
20−
21−flag use-reflex-optimizer
22− description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
23− default: False
24− manual: True
25−
26−flag use-template-haskell
27− description: Use template haskell to generate lenses
28− default: True
29− manual: True
30−
31−flag expose-all-unfoldings
32− description: Build the library with -fexpose-all-unfoldings which can help client code specialize better
33− default: False
34− manual: True
35−
36−flag debug-trace-events
37− description: Add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
38− default: False
39− manual: True
40−
41−flag experimental-dependent-sum-instances
42− description: Use experimental incoherent instances for certain forked dependent-sum typeclasses
43− default: False
44− manual: True
45−
46−library
47− hs-source-dirs: src
48− build-depends:
49− MemoTrie == 0.6.*,
50− base >= 4.7 && < 4.11,
51− bifunctors >= 5.2 && < 5.5,
52− containers == 0.5.*,
53− data-default >= 0.5 && < 0.8,
54− dependent-map >= 0.2.4 && < 0.3,
55− exception-transformers == 0.4.*,
56− lens >= 4.7 && < 5,
57− monad-control >= 1.0.1 && < 1.1,
58− mtl >= 2.1 && < 2.3,
59− prim-uniq >= 0.1.0.1 && < 0.2,
60− primitive >= 0.5 && < 0.7,
61− ref-tf == 0.4.*,
62− reflection == 2.1.*,
63− semigroupoids >= 4.0 && < 6,
64− semigroups >= 0.16 && < 0.19,
65− stm == 2.4.*,
66− syb >= 0.5 && < 0.7,
67− these >= 0.4 && < 0.8,
68− transformers >= 0.2,
69− transformers-compat >= 0.3
70−
71− exposed-modules:
72− Data.Functor.Misc,
73− Data.WeakBag,
74− Reflex,
75− Reflex.Class,
76− Reflex.EventWriter,
77− Reflex.Dynamic,
78− Reflex.Dynamic.Uniq,
79− Reflex.DynamicWriter,
80− Reflex.FunctorMaybe,
81− Reflex.Host.Class,
82− Reflex.Patch,
83− Reflex.Patch.Class,
84− Reflex.Patch.DMap,
85− Reflex.Patch.DMapWithMove,
86− Reflex.Patch.Map,
87− Reflex.Patch.MapWithMove,
88− Reflex.TriggerEvent.Base,
89− Reflex.TriggerEvent.Class,
90− Reflex.PerformEvent.Base,
91− Reflex.PerformEvent.Class,
92− Reflex.PostBuild.Base,
93− Reflex.PostBuild.Class,
94− Reflex.Query.Base,
95− Reflex.Query.Class,
96− Reflex.Requester.Base,
97− Reflex.Requester.Class,
98− Reflex.Spider,
99− Reflex.Spider.Internal
100−
101− ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
102− ghc-prof-options: -fprof-auto
103−
104− if flag(specialize-to-spidertimeline-global)
105− cpp-options: -DSPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
106− else
107− exposed-modules: Reflex.Pure
108−
109− if flag(debug-trace-events)
110− cpp-options: -DDEBUG_TRACE_EVENTS
111− build-depends: bytestring
112−
113− if flag(use-reflex-optimizer)
114− cpp-options: -DUSE_REFLEX_OPTIMIZER
115− build-depends: ghc
116− exposed-modules: Reflex.Optimizer
117−
118− if flag(use-template-haskell)
119− cpp-options: -DUSE_TEMPLATE_HASKELL
120− build-depends:
121− dependent-sum >= 0.3 && < 0.5,
122− haskell-src-exts >= 1.16 && < 1.19,
123− haskell-src-meta >= 0.6 && < 0.8,
124− template-haskell >= 2.9 && < 2.12
125− exposed-modules:
126− Reflex.Dynamic.TH
127− other-extensions: TemplateHaskell
128− else
129− build-depends:
130− dependent-sum == 0.4.*
131−
132− if flag(expose-all-unfoldings)
133− ghc-options: -fexpose-all-unfoldings
134−
135− if flag(experimental-dependent-sum-instances)
136− ghc-options: -DEXPERIMENTAL_DEPENDENT_SUM_INSTANCES
137−
138−test-suite semantics
139− type: exitcode-stdio-1.0
140− main-is: semantics.hs
141− hs-source-dirs: test
142− ghc-options: -O2 -Wall -rtsopts
143− build-depends:
144− base,
145− bifunctors,
146− containers,
147− deepseq >= 1.3 && < 1.5,
148− dependent-map,
149− dependent-sum,
150− mtl,
151− ref-tf,
152− reflex,
153− split,
154− transformers >= 0.3
155− if flag(specialize-to-spidertimeline-global)
156− buildable: False
157−
158−test-suite hlint
159− type: exitcode-stdio-1.0
160− main-is: hlint.hs
161− hs-source-dirs: test
162− build-depends: base, hlint == 1.9.*
163− if impl(ghcjs)
164− buildable: False
165−
166−test-suite EventWriterT
167− type: exitcode-stdio-1.0
168− main-is: EventWriterT.hs
169− hs-source-dirs: test
170− build-depends: base
171− , dependent-sum
172− , transformers
173− , reflex
174− , ref-tf
175−
176−test-suite RequesterT
177− type: exitcode-stdio-1.0
178− main-is: RequesterT.hs
179− hs-source-dirs: test
180− build-depends: base
181− , dependent-sum
182− , dependent-map
183− , transformers
184− , reflex
185− , ref-tf
186−
187−test-suite GC-Semantics
188− type: exitcode-stdio-1.0
189− main-is: GC.hs
190− hs-source-dirs: test
191− if flag(specialize-to-spidertimeline-global)
192− buildable: False
193− build-depends: base
194− , dependent-sum
195− , dependent-map
196− , these
197− , transformers
198− , reflex
199− , ref-tf
200−
201−test-suite rootCleanup
202− type: exitcode-stdio-1.0
203− main-is: rootCleanup.hs
204− hs-source-dirs: test
205− build-depends: base
206− , reflex
207−
208−benchmark spider-bench
209− type: exitcode-stdio-1.0
210− hs-source-dirs: bench
211− main-is: Main.hs
212− ghc-options: -Wall -O2 -rtsopts
213− build-depends:
214− base,
215− criterion == 1.1.*,
216− deepseq >= 1.3 && < 1.5,
217− dependent-map,
218− dependent-sum,
219− mtl,
220− primitive,
221− reflex,
222− stm,
223− transformers >= 0.3
224−
225−benchmark saulzar-bench
226− type: exitcode-stdio-1.0
227− hs-source-dirs: bench test
228− c-sources: bench-cbits/checkCapability.c
229− main-is: RunAll.hs
230− ghc-options: -Wall -O2 -rtsopts -threaded
231− ghc-prof-options: -fprof-auto
232− build-depends:
233− base,
234− containers == 0.5.*,
235− criterion == 1.1.*,
236− deepseq >= 1.3 && < 1.5,
237− dependent-map,
238− dependent-sum,
239− loch-th,
240− mtl,
241− primitive,
242− process,
243− ref-tf,
244− reflex,
245− split,
246− stm,
247− time,
248− transformers >= 0.3
249−
250−source-repository head
251− type: git
252− 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+25−133
@@ -4,16 +4,12 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE LambdaCase #-}
98{-# LANGUAGE MultiParamTypeClasses #-}
109{-# LANGUAGE PolyKinds #-}
1110{-# LANGUAGE RankNTypes #-}
1211{-# LANGUAGE ScopedTypeVariables #-}
1312{-# LANGUAGE StandaloneDeriving #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
1713-- | This module provides types and functions with no particular theme, but
1814-- which are relevant to the use of 'Functor'-based datastructures like
1915-- 'Data.Dependent.Map.DMap'.
@@ -22,6 +18,7 @@ module Data.Functor.Misc
2218 Const2 (..)
2319 , unConst2
2420 , dmapToMap
21+ , dmapToIntMap
2522 , dmapToMapWith
2623 , mapToDMap
2724 , weakenDMapWith
@@ -29,34 +26,30 @@ module Data.Functor.Misc
2926 , WrapArg (..)
3027 -- * Convenience functions for DMap
3128 , mapWithFunctorToDMap
29+ , intMapWithFunctorToDMap
3230 , mapKeyValuePairsMonotonic
3331 , combineDMapsWithKey
3432 , EitherTag (..)
3533 , dmapToThese
3634 , eitherToDSum
3735 , dsumToEither
38− -- * Deprecated functions
39− , sequenceDmap
40− , wrapDMap
41− , rewrapDMap
42− , unwrapDMap
43− , unwrapDMapMaybe
44− , extractFunctorDMap
4536 , ComposeMaybe (..)
4637 ) where
4738
48−import Control.Applicative (Applicative, (<$>))
49−import Control.Monad.Identity
39+import Control.Applicative ((<$>))
5040import Data.Dependent.Map (DMap)
5141import qualified Data.Dependent.Map as DMap
5242import Data.Dependent.Sum
43+import Data.Functor.Identity
5344import Data.GADT.Compare
5445import Data.GADT.Show
46+import Data.IntMap (IntMap)
47+import qualified Data.IntMap as IntMap
5548import Data.Map (Map)
5649import qualified Data.Map as Map
57−import Data.Some (Some)
58−import qualified Data.Some as Some
50+import Data.Some (Some, mkSome)
5951import Data.These
52+import Data.Type.Equality ((:~:)(Refl))
6053import Data.Typeable hiding (Refl)
6154
6255--------------------------------------------------------------------------------
@@ -69,6 +62,7 @@ data Const2 :: * -> x -> x -> * where
6962 Const2 :: k -> Const2 k v v
7063 deriving (Typeable)
7164
65+-- | Extract the value from a Const2
7266unConst2 :: Const2 k v v' -> k
7367unConst2 (Const2 k) = k
7468
@@ -78,53 +72,30 @@ deriving instance Show k => Show (Const2 k v v')
7872deriving instance Read k => Read (Const2 k v v)
7973
8074instance Show k => GShow (Const2 k v) where
81−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
82− gshowToShow (Const2 _) = id
83−#else
8475 gshowsPrec n x@(Const2 _) = showsPrec n x
85−#endif
86−
87−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
88−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
89− showTagToShow (Const2 _) _ = id
90−#else
91− showTaggedPrec (Const2 _) = showsPrec
92−#endif
9376
9477instance Eq k => GEq (Const2 k v) where
95−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
96− geqToEq _ = id
97− geqUnify (Const2 _) (Const2 _) _ = id
98−#else
9978 geq (Const2 a) (Const2 b) =
10079 if a == b
10180 then Just Refl
10281 else Nothing
103−#endif
10482
10583instance Ord k => GCompare (Const2 k v) where
106−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
107− gcompareToOrd (Const2 _) = id
108− gcompare (Const2 a) (Const2 b) = strengthenOrdering $ compare a b
109−#else
11084 gcompare (Const2 a) (Const2 b) = case compare a b of
11185 LT -> GLT
11286 EQ -> GEQ
11387 GT -> GGT
114−#endif
115−
116−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
117−instance {-# INCOHERENT #-} (Eq k, Eq v) => EqTag (Const2 k v) Identity where
118− eqTagToEq (Const2 _) _ = id
119−
120−instance {-# INCOHERENT #-} Eq k => EqTag (Const2 k v) (Const2 k v) where
121− eqTagToEq (Const2 _) _ = id
122−#endif
12388
12489-- | Convert a 'DMap' to a regular 'Map'
12590dmapToMap :: DMap (Const2 k v) Identity -> Map k v
12691dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
12792
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'
12899dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v'
129100dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList
130101
@@ -137,8 +108,15 @@ mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v)
137108mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
138109mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
139110
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'
140118weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
141−weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (Some.This k, f v)) . DMap.toAscList
119+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
142120
143121--------------------------------------------------------------------------------
144122-- WrapArg
@@ -156,17 +134,9 @@ deriving instance Show (f a) => Show (WrapArg g f (g' a))
156134deriving instance Read (f a) => Read (WrapArg g f (g a))
157135
158136instance GEq f => GEq (WrapArg g f) where
159−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
160− geqToEq (WrapArg f) = geqToEq f
161− geqUnify (WrapArg a) (WrapArg b) = geqUnify a b
162−#else
163137 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
164−#endif
165138
166139instance GCompare f => GCompare (WrapArg g f) where
167−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
168− gcompareToOrd (WrapArg f) = gcompareToOrd f
169−#endif
170140 gcompare (WrapArg a) (WrapArg b) = case gcompare a b of
171141 GLT -> GLT
172142 GEQ -> GEQ
@@ -218,34 +188,13 @@ deriving instance Show (EitherTag l r a)
218188deriving instance Eq (EitherTag l r a)
219189deriving instance Ord (EitherTag l r a)
220190
221−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
222−instance {-# INCOHERENT #-} (Show (f l), Show (f r)) => ShowTag (EitherTag l r) f where
223− showTagToShow e _ = case e of
224− LeftTag -> id
225− RightTag -> id
226−
227−instance {-# INCOHERENT #-} (Eq (f l), Eq (f r)) => EqTag (EitherTag l r) f where
228− eqTagToEq e _ = case e of
229− LeftTag -> id
230− RightTag -> id
231−#endif
232−
233191instance GEq (EitherTag l r) where
234−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
235− geqToEq _ = id
236− geqUnify LeftTag LeftTag _ same = same
237− geqUnify RightTag RightTag _ same = same
238− geqUnify _ _ unknown _ = unknown
239−#endif
240192 geq a b = case (a, b) of
241193 (LeftTag, LeftTag) -> Just Refl
242194 (RightTag, RightTag) -> Just Refl
243195 _ -> Nothing
244196
245197instance GCompare (EitherTag l r) where
246−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
247− gcompareToOrd _ = id
248−#endif
249198 gcompare a b = case (a, b) of
250199 (LeftTag, LeftTag) -> GEQ
251200 (LeftTag, RightTag) -> GLT
@@ -253,24 +202,9 @@ instance GCompare (EitherTag l r) where
253202 (RightTag, RightTag) -> GEQ
254203
255204instance GShow (EitherTag l r) where
256−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
257− gshowToShow _ = id
258−#else
259205 gshowsPrec _ a = case a of
260206 LeftTag -> showString "LeftTag"
261207 RightTag -> showString "RightTag"
262−#endif
263−
264−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
265−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
266− showTagToShow t _ = case t of
267− LeftTag -> id
268− RightTag -> id
269−#else
270− showTaggedPrec t n (Identity a) = case t of
271− LeftTag -> showsPrec n a
272− RightTag -> showsPrec n a
273−#endif
274208
275209-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
276210eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
@@ -291,49 +225,7 @@ dsumToEither = \case
291225-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
292226-- make the 'f' parameter have a nominal type role. We need f to be
293227-- representational so that we can use safe 'coerce'.
294−newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
228+newtype ComposeMaybe f a =
229+ ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
295230
296231deriving instance Functor f => Functor (ComposeMaybe f)
297−
298−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
299−instance {-# INCOHERENT #-} EqTag f g => EqTag f (ComposeMaybe g) where
300− eqTagToEq f _ = eqTagToEq f (Proxy :: Proxy g)
301−
302−instance {-# INCOHERENT #-} GShow k => ShowTag k (ComposeMaybe k) where
303− showTagToShow k _ r = gshowToShow k (showTagToShow k (Proxy :: Proxy k) r)
304−#endif
305−--------------------------------------------------------------------------------
306−-- Deprecated functions
307−--------------------------------------------------------------------------------
308−
309−{-# INLINE sequenceDmap #-}
310−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ -> fmap Identity)' instead" #-}
311−-- | Run the actions contained in the 'DMap'
312−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
313−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
314−
315−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
316−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
317−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
318−wrapDMap f = DMap.map $ f . runIdentity
319−
320−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
321−-- | Replace one functor for a 'DMap''s values with a different functor
322−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
323−rewrapDMap = DMap.map
324−
325−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
326−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
327−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
328−unwrapDMap f = DMap.map $ Identity . f
329−
330−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
331−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
332−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
333−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
334−
335−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
336−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
337−-- underlying functor
338−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
339−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
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/Reflex/Patch/DMapWithMove.hs → src/Data/Patch/DMapWithMove.hsrenamed+158−68
@@ -10,85 +10,122 @@
1010{-# LANGUAGE ScopedTypeVariables #-}
1111{-# LANGUAGE TypeFamilies #-}
1212{-# LANGUAGE UndecidableInstances #-}
13−module Reflex.Patch.DMapWithMove where
1413
15−import Reflex.Patch.Class
16−import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
17−import qualified Reflex.Patch.MapWithMove as MapWithMove
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
1817
19−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
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 (..))
2025import qualified Data.Dependent.Map as DMap
21−import Data.Dependent.Sum (EqTag (..))
2226import Data.Functor.Constant
2327import Data.Functor.Misc
2428import Data.Functor.Product
25−import Data.GADT.Compare (GEq (..))
29+import Data.GADT.Compare (GEq (..), GCompare (..))
30+import Data.GADT.Show (GShow, gshow)
2631import qualified Data.Map as Map
2732import Data.Maybe
28−import Data.Semigroup (Semigroup (..), (<>))
29−import Data.Some (Some)
30−import qualified Data.Some as Some
31−import Data.These
32−
33−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
34−import Data.Dependent.Sum (ShowTag (..))
35−import Data.GADT.Show (GShow (..))
36−import Data.Typeable (Proxy (..))
33+import Data.Monoid.DecidablyEmpty
34+#if !MIN_VERSION_base(4,11,0)
35+import Data.Semigroup (Semigroup (..))
3736#endif
37+import Data.Some (Some, mkSome)
38+import Data.These
3839
39−-- | Like 'PatchMapWithMove', but for 'DMap'.
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)@)
4047newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
41−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
42− deriving (Show)
43−#endif
4448
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.
4555data NodeInfo k v a = NodeInfo
4656 { _nodeInfo_from :: !(From k v a)
57+ -- ^Change applying to the current key, be it an insert, move, or delete.
4758 , _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'.
4860 }
4961 deriving (Show)
5062
51−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
52−instance {-# INCOHERENT #-} (GShow k, ShowTag k v) => ShowTag k (NodeInfo k v) where
53− showTagToShow k _ r = gshowToShow k $ showTagToShow k (Proxy :: Proxy v) r
54−
55−instance {-# INCOHERENT #-} (GEq k, EqTag k v) => EqTag k (From k v) where
56− eqTagToEq k _ r = eqTagToEq k (Proxy :: Proxy v) (geqToEq k r)
57−#endif
58−
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@).
5965data From (k :: a -> *) (v :: a -> *) :: a -> * where
66+ -- |Insert a new or update an existing key with the given value @v a@
6067 From_Insert :: v a -> From k v a
68+ -- |Delete the existing key
6169 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@.
6272 From_Move :: !(k a) -> From k v a
6373 deriving (Show, Read, Eq, Ord)
6474
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.
6577type To = ComposeMaybe
6678
67−validPatchDMapWithMove :: forall k v. (GCompare k, EqTag k (ComposeMaybe k)) => PatchDMapWithMove k v -> Bool
68−validPatchDMapWithMove (PatchDMapWithMove m) = src == srcFromDst
69− where
70− src = DMap.map _nodeInfo_to m
71− dst = DMap.map _nodeInfo_from m
72− srcFromDst :: DMap k (ComposeMaybe k)
73− srcFromDst = DMap.fromList $ flip fmap (DMap.toList dst) $ \(to :=> edit) -> case edit of
74− From_Move from -> from :=> ComposeMaybe (Just to)
75− _ -> to :=> ComposeMaybe Nothing
76−
77−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
78− PatchDMapWithMove a == PatchDMapWithMove b = a == b
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
7982
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
80119data Pair1 f g a = Pair1 (f a) (g a)
81120
82−instance GCompare k => Semigroup (PatchDMapWithMove k v) where
83− (<>) = mappend
84−
121+-- |Helper data structure used for composing patches using the monoid instance.
85122data Fixup k v a
86123 = Fixup_Delete
87124 | Fixup_Update (These (From k v a) (To k a))
88125
89−instance GCompare k => Monoid (PatchDMapWithMove k v) where
90− mempty = PatchDMapWithMove mempty
91− PatchDMapWithMove ma `mappend` PatchDMapWithMove mb = PatchDMapWithMove m
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
92129 where
93130 connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
94131 h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
@@ -122,18 +159,21 @@ instance GCompare k => Monoid (PatchDMapWithMove k v) where
122159 , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
123160 }
124161 m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
125−
126−getHere :: These a b -> Maybe a
127−getHere = \case
128− This a -> Just a
129− These a _ -> Just a
130− That _ -> Nothing
131−
132−getThere :: These a b -> Maybe b
133−getThere = \case
134− This _ -> Nothing
135− These _ b -> Just b
136− That b -> Just b
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 = (<>)
137177
138178{-
139179mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
@@ -157,6 +197,16 @@ PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWith
157197 src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
158198-}
159199
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+-- @
160210moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
161211moveDMapKey src dst = case src `geq` dst of
162212 Nothing -> PatchDMapWithMove $ DMap.fromList
@@ -165,6 +215,24 @@ moveDMapKey src dst = case src `geq` dst of
165215 ]
166216 Just _ -> mempty
167217
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'.
168236deleteDMapKey :: k a -> PatchDMapWithMove k v
169237deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
170238
@@ -192,14 +260,25 @@ dst (PatchDMapWithMove x _) = x
192260src (PatchDMapWithMove _ x) = x
193261-}
194262
263+-- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'.
195264unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
196265unPatchDMapWithMove (PatchDMapWithMove p) = p
197266
198−-- | Warning: when using this function, you must ensure that the invariants of
199−-- 'PatchDMapWithMove' are preserved; they will not be checked.
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.
200270unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
201271unsafePatchDMapWithMove = PatchDMapWithMove
202272
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'@.
203282mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
204283mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
205284 DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
@@ -209,9 +288,11 @@ mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
209288 From_Delete -> From_Delete
210289 From_Move k -> From_Move k
211290
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')@.
212292traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
213293traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
214294
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')@.
215296traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
216297traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
217298 where g :: forall a. k a -> From k v a -> m (From k v' a)
@@ -220,12 +301,16 @@ traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$>
220301 From_Delete -> pure From_Delete
221302 From_Move fromKey -> pure $ From_Move fromKey
222303
304+-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
223305nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
224306nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
225307
308+-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
226309nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
227310nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
228311
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.
229314weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
230315weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
231316 where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
@@ -233,13 +318,15 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
233318 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
234319 From_Insert v -> MapWithMove.From_Insert $ f v
235320 From_Delete -> MapWithMove.From_Delete
236− From_Move k -> MapWithMove.From_Move $ Some.This k
237− , MapWithMove._nodeInfo_to = Some.This <$> getComposeMaybe (_nodeInfo_to ni)
321+ From_Move k -> MapWithMove.From_Move $ mkSome k
322+ , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
238323 }
239324
240−patchDMapWithMoveToPatchMapWithMoveWith :: forall k f v v'. (f v -> v') -> PatchDMapWithMove (Const2 k v) f -> PatchMapWithMove k v'
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'
241328patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
242− where g :: NodeInfo (Const2 k v) f v -> MapWithMove.NodeInfo k v'
329+ where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
243330 g ni = MapWithMove.NodeInfo
244331 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
245332 From_Insert v -> MapWithMove.From_Insert $ f v
@@ -248,9 +335,12 @@ patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMo
248335 , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
249336 }
250337
251−const2PatchDMapWithMoveWith :: forall k v f a. (v -> f a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) f
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'
252342const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
253− where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) f)
343+ where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
254344 g (k, ni) = Const2 k :=> NodeInfo
255345 { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
256346 MapWithMove.From_Insert v -> From_Insert $ f v
@@ -259,6 +349,7 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
259349 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
260350 }
261351
352+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
262353instance GCompare k => Patch (PatchDMapWithMove k v) where
263354 type PatchTarget (PatchDMapWithMove k v) = DMap k v
264355 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?
@@ -274,8 +365,7 @@ instance GCompare k => Patch (PatchDMapWithMove k v) where
274365 From_Delete -> Just $ Constant ()
275366 _ -> Nothing
276367
277−-- | Get the values that will be deleted or moved if the given patch is applied
278−-- to the given 'DMap'.
368+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
279369getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
280370getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
281371 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−124
@@ -1,124 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ExistentialQuantification #-}
4−#ifdef USE_REFLEX_OPTIMIZER
5−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
6−#endif
7−-- | This module defines the 'WeakBag' type, which represents a mutable
8−-- collection of items that does not cause the items to be retained in memory.
9−-- This is useful for situations where a value needs to be inspected or modified
10−-- if it is still alive, but can be ignored if it is dead.
11−module Data.WeakBag
12− ( WeakBag
13− , WeakBagTicket
14− , empty
15− , singleton
16− , insert
17− , traverse
18− , remove
19− -- * Internal functions
20− -- These will not always be available.
21− , _weakBag_children --TODO: Don't export this
22− ) where
23−
24−import Control.Exception
25−import Control.Monad hiding (forM_, mapM_)
26−import Control.Monad.IO.Class
27−import Data.Foldable (forM_, mapM_)
28−import Data.IntMap.Strict (IntMap)
29−import qualified Data.IntMap.Strict as IntMap
30−import Data.IORef
31−import System.Mem.Weak
32−
33−import Prelude hiding (mapM_, traverse)
34−
35−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
36−-- that is, they can still be garbage-collected. As long as the @a@s remain
37−-- alive, the 'WeakBag' will continue to refer to them.
38−data WeakBag a = WeakBag
39− { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
40− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
41− }
42−
43−-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
44−-- the caller retains the ticket, the item is guranteed to stay in memory (and
45−-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
46−-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
47−data WeakBagTicket = forall a. WeakBagTicket
48− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
49− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
50− }
51−
52−-- | Insert an item into a 'WeakBag'.
53−{-# INLINE insert #-}
54−insert :: a -- ^ The item
55− -> WeakBag a -- ^ The 'WeakBag' to insert into
56− -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
57− -- callback
58− -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
59− -- (whether automatically by the item being garbage
60− -- collected or manually via 'remove')
61− -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
62− -- is retained and allows the item to be removed.
63−insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
64− a' <- evaluate a
65− wbRef' <- evaluate wbRef
66− myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
67− let cleanup = do
68− wb <- readIORef wbRef'
69− mb <- deRefWeak wb
70− forM_ mb $ \b -> do
71− csWithoutMe <- atomicModifyIORef children $ \cs ->
72− let !csWithoutMe = IntMap.delete myId cs
73− in (csWithoutMe, csWithoutMe)
74− when (IntMap.null csWithoutMe) $ finalizer b
75− return ()
76− return ()
77− wa <- mkWeakPtr a' $ Just cleanup
78− atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
79− return $ WeakBagTicket
80− { _weakBagTicket_weakItem = wa
81− , _weakBagTicket_item = a'
82− }
83−
84−-- | Create an empty 'WeakBag'.
85−{-# INLINE empty #-}
86−empty :: IO (WeakBag a)
87−empty = {-# SCC "empty" #-} do
88− nextId <- newIORef 1
89− children <- newIORef IntMap.empty
90− let bag = WeakBag
91− { _weakBag_nextId = nextId
92− , _weakBag_children = children
93− }
94− return bag
95−
96−-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
97−-- 'empty', then using 'insert'.
98−{-# INLINE singleton #-}
99−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket)
100−singleton a wbRef finalizer = {-# SCC "singleton" #-} do
101− bag <- empty
102− ticket <- insert a bag wbRef finalizer
103− return (bag, ticket)
104−
105−{-# INLINE traverse #-}
106−-- | Visit every node in the given list. If new nodes are appended during the
107−-- traversal, they will not be visited. Every live node that was in the list
108−-- when the traversal began will be visited exactly once; however, no guarantee
109−-- is made about the order of the traversal.
110−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
111−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
112− cs <- liftIO $ readIORef children
113− forM_ cs $ \c -> do
114− ma <- liftIO $ deRefWeak c
115− mapM_ f ma
116−
117−-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
118−-- on the same 'WeakBagTicket'.
119−{-# INLINE remove #-}
120−remove :: WeakBagTicket -> IO ()
121−remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w
122−--TODO: Should 'remove' also drop the reference to the item?
123−
124−--TODO: can/should we provide a null WeakBagTicket?
src/Reflex.hsdeleted−26
@@ -1,26 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−-- | This module exports all of the commonly-used functionality of Reflex; if
3−-- you are just getting started with Reflex, this is probably what you want.
4−module Reflex
5− ( module X
6− ) where
7−
8−import Reflex.Class as X
9−import Reflex.EventWriter as X
10−import Reflex.Dynamic as X
11−#ifdef USE_TEMPLATE_HASKELL
12−import Reflex.Dynamic.TH as X
13−#endif
14−import Reflex.Dynamic.Uniq as X
15−import Reflex.DynamicWriter as X
16−import Reflex.PerformEvent.Base as X
17−import Reflex.PerformEvent.Class as X
18−import Reflex.PostBuild.Base as X
19−import Reflex.PostBuild.Class as X
20−import Reflex.Query.Base as X
21−import Reflex.Query.Class as X
22−import Reflex.Requester.Base as X
23−import Reflex.Requester.Class as X
24−import Reflex.Spider as X
25−import Reflex.TriggerEvent.Base as X
26−import Reflex.TriggerEvent.Class as X
src/Reflex/Class.hsdeleted−1101
@@ -1,1101 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE RecursiveDo #-}
11−{-# LANGUAGE RoleAnnotations #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−#ifdef USE_REFLEX_OPTIMIZER
17−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
18−#endif
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−{-# LANGUAGE EmptyDataDecls #-}
21−{-# OPTIONS_GHC -Wno-redundant-constraints #-}
22−#endif
23−
24−-- | This module contains the Reflex interface, as well as a variety of
25−-- convenience functions for working with 'Event's, 'Behavior's, and other
26−-- signals.
27−module Reflex.Class
28− ( module Reflex.Patch
29− -- * Primitives
30− , Reflex (..)
31− , coerceBehavior
32− , coerceEvent
33− , coerceDynamic
34− , MonadSample (..)
35− , MonadHold (..)
36− -- ** 'fan'-related types
37− , EventSelector (..)
38− -- ** 'Incremental'-related types
39− -- * Convenience functions
40− , constDyn
41− , pushAlways
42− -- ** Combining 'Event's
43− , leftmost
44− , mergeMap
45− , mergeMapIncremental
46− , mergeMapIncrementalWithMove
47− , mergeList
48− , mergeWith
49− , difference
50− , alignEventWithMaybe
51− -- ** Breaking up 'Event's
52− , splitE
53− , fanEither
54− , fanThese
55− , fanMap
56− , dmapToThese
57− , EitherTag (..)
58− , eitherToDSum
59− , dsumToEither
60− -- ** Switching between 'Event's
61− , switchPromptly
62− , switchPromptOnly
63− -- ** Using 'Event's to sample 'Behavior's
64− , tag
65− , attach
66− , attachWith
67− , attachWithMaybe
68− -- ** Blocking an 'Event' based on a 'Behavior'
69− , gate
70− -- ** Combining 'Dynamic's
71− , distributeDMapOverDynPure
72− , distributeListOverDyn
73− , distributeListOverDynWith
74− , zipDyn
75− , zipDynWith
76− -- ** Accumulating state
77− , Accumulator (..)
78− , mapAccum_
79− , mapAccumM_
80− , mapAccumMaybe_
81− , mapAccumMaybeM_
82− , zipListWithEvent
83− , numberOccurrences
84− , numberOccurrencesFrom
85− , (<@>)
86− , (<@)
87− , headE
88− , tailE
89− , headTailE
90− , switcher
91− -- * Debugging functions
92− , traceEvent
93− , traceEventWith
94− -- * Unsafe functions
95− , unsafeDynamic
96− , unsafeMapIncremental
97− -- * 'FunctorMaybe'
98− , FunctorMaybe (..)
99− , fforMaybe
100− , ffilter
101− -- * Miscellaneous convenience functions
102− , ffor
103− , MonadAdjust (..)
104− , sequenceDMapWithAdjust
105− , sequenceDMapWithAdjustWithMove
106− , mapMapWithAdjustWithMove
107− -- * Deprecated functions
108− , appendEvents
109− , onceE
110− , sequenceThese
111− , fmapMaybeCheap
112− , fmapCheap
113− , fforCheap
114− , fforMaybeCheap
115− , pushAlwaysCheap
116− , tagCheap
117− , mergeWithCheap
118− , mergeWithCheap'
119−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
120− , Spider
121− , SpiderEnv
122− , SpiderTimeline
123− , Global
124− , Behavior (..)
125− , Event (..)
126− , Dynamic (..)
127− , Incremental (..)
128− , never
129− , constant
130− , push
131− , pushCheap
132− , pull
133− , merge
134− , fan
135− , switch
136− , coincidence
137− , current
138− , updated
139− , unsafeBuildDynamic
140− , unsafeBuildIncremental
141− , mergeIncremental
142− , mergeIncrementalWithMove
143− , currentIncremental
144− , updatedIncremental
145− , incrementalToDynamic
146− , behaviorCoercion
147− , eventCoercion
148− , dynamicCoercion
149−#endif
150− ) where
151−
152−import Control.Applicative
153−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
154−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
155−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
156−import Control.Monad.Trans.Cont (ContT ())
157−import Control.Monad.Trans.Except (ExceptT ())
158−import Control.Monad.Trans.RWS (RWST ())
159−import Control.Monad.Trans.Writer (WriterT ())
160−import Data.Align
161−import Data.Bifunctor
162−import Data.Coerce
163−import Data.Default
164−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
165−import qualified Data.Dependent.Map as DMap
166−import Data.Either
167−import Data.Foldable
168−import Data.Functor.Bind hiding (join)
169−import qualified Data.Functor.Bind as Bind
170−import Data.Functor.Constant
171−import Data.Functor.Misc
172−import Data.Functor.Plus
173−import Data.List.NonEmpty (NonEmpty (..))
174−import Data.Map (Map)
175−import Data.Maybe
176−import Data.Monoid hiding (Alt, (<>))
177−import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
178−import Data.String
179−import Data.These
180−import Data.Traversable
181−import Data.Type.Coercion
182−import Reflex.FunctorMaybe
183−import Reflex.Patch
184−
185−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
186−import Reflex.Spider.Internal (EventM, Global, HasSpiderTimeline, SpiderHost (..), SpiderHostFrame (..),
187− SpiderPullM, SpiderPushM (..), behaviorHoldIdentity, dynamicConst,
188− dynamicCurrent, dynamicDynIdentity, dynamicHold, dynamicHoldIdentity,
189− dynamicUpdated, newMapDyn, readBehaviorTracked, readBehaviorUntracked,
190− runFrame, runSpiderHostFrame, unsafeDyn)
191−import qualified Reflex.Spider.Internal
192−import qualified Reflex.Spider.Internal as S
193−import Unsafe.Coerce
194−#endif
195−
196−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
197−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
198−
199−import Debug.Trace (trace)
200−
201−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
202−#include "Spider/SpiderTimeline.include.hs"
203−#endif
204−
205−-- | The 'Reflex' class contains all the primitive functionality needed for
206−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
207−-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
208−-- and the type of the timeline determines the FRP engine to be used. For most
209−-- purposes, the 'Reflex.Spider' implementation is recommended.
210−class ( MonadHold t (PushM t)
211− , MonadSample t (PullM t)
212− , MonadFix (PushM t)
213− , Functor (Dynamic t)
214− , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
215− , Monad (Dynamic t)
216−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
217− , t ~ SpiderTimeline Global
218−#endif
219− ) => Reflex t where
220− -- | A container for a value that can change over time. 'Behavior's can be
221− -- sampled at will, but it is not possible to be notified when they change
222− data Behavior t :: * -> *
223− -- | A stream of occurrences. During any given frame, an 'Event' is either
224− -- occurring or not occurring; if it is occurring, it will contain a value of
225− -- the given type (its "occurrence type")
226− data Event t :: * -> *
227− -- | A container for a value that can change over time and allows
228− -- notifications on changes. Basically a combination of a 'Behavior' and an
229− -- 'Event', with a rule that the 'Behavior' will change if and only if the
230− -- 'Event' fires.
231− data Dynamic t :: * -> *
232− -- | An 'Incremental' is a more general form of a 'Dynamic'.
233− -- Instead of always fully replacing the value, only parts of it can be patched.
234− -- This is only needed for performance critical code via `mergeIncremental` to make small
235− -- changes to large values.
236− data Incremental t :: * -> *
237− -- | A monad for doing complex push-based calculations efficiently
238− type PushM t :: * -> *
239− -- | A monad for doing complex pull-based calculations efficiently
240− type PullM t :: * -> *
241−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
242− -- | An 'Event' with no occurrences
243− never :: Event t a
244− -- | Create a 'Behavior' that always has the given value
245− constant :: a -> Behavior t a --TODO: Refactor to use 'pure' from Applicative instead; however, we need to make sure that encouraging Applicative-style use of 'Behavior's doesn't have a negative performance impact
246− -- | Create an 'Event' from another 'Event'; the provided function can sample
247− -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
248− -- (Just) or non-occurring (Nothing) result
249− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
250− -- | Like 'push' but intended for functions that the implementation can consider cheap to compute for performance considerations. WARNING: The function passed to 'pushCheap' may be run multiple times without any caching.
251− pushCheap :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
252− -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
253− -- recomputed whenever any of the read 'Behavior's changes
254− pull :: PullM t a -> Behavior t a
255− -- | Merge a collection of events; the resulting 'Event' will only occur if at
256− -- least one input event is occuring, and will contain all of the input keys
257− -- that are occurring simultaneously
258− merge :: GCompare k => DMap k (Event t) -> Event t (DMap k Identity) --TODO: Generalize to get rid of DMap use --TODO: Provide a type-level guarantee that the result is not empty
259− -- | Efficiently fan-out an event to many destinations. This function should
260− -- be partially applied, and then the result applied repeatedly to create
261− -- child events
262− 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
263− -- | Create an 'Event' that will occur whenever the currently-selected input
264− -- 'Event' occurs
265− switch :: Behavior t (Event t a) -> Event t a
266− -- | Create an 'Event' that will occur whenever the input event is occurring
267− -- and its occurrence value, another 'Event', is also occurring
268− coincidence :: Event t (Event t a) -> Event t a
269− -- | Extract the 'Behavior' of a 'Dynamic'.
270− current :: Dynamic t a -> Behavior t a
271− -- | Extract the 'Event' of the 'Dynamic'.
272− updated :: Dynamic t a -> Event t a
273− -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
274− -- recent firing of the given 'Event', if any.
275− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
276− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
277− -- in the same way that the accumulated application of patches would change
278− -- that value.
279− unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
280− -- | Create a merge whose parents can change over time
281− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
282− -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
283− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
284− -- | Extract the 'Behavior' component of an 'Incremental'
285− currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
286− -- | Extract the 'Event' component of an 'Incremental'
287− updatedIncremental :: Patch p => Incremental t p -> Event t p
288− -- | Convert an 'Incremental' to a 'Dynamic'
289− incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
290− -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
291− -- occurrence type
292− behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
293− -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
294− -- occurrence type
295− eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
296− -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
297− -- occurrence type
298− dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
299−#endif
300−
301−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
302−instance t ~ SpiderTimeline Global => Reflex t where
303− newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
304− newtype Event t a = SpiderEvent { unSpiderEvent :: Reflex.Spider.Internal.Event Global a }
305− newtype Dynamic t a = SpiderDynamic { unSpiderDynamic :: Reflex.Spider.Internal.Dynamic Global (Identity a) } -- deriving (Functor, Applicative, Monad)
306− newtype Incremental t p = SpiderIncremental { unSpiderIncremental :: Reflex.Spider.Internal.Dynamic Global p }
307− type PullM t = SpiderPullM Global
308− type PushM t = SpiderPushM Global
309−never :: Reflex t => Event t a
310−constant :: Reflex t => a -> Behavior t a
311−push :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
312−pushCheap :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
313−pull :: Reflex t => PullM t a -> Behavior t a
314−merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
315−fan :: (Reflex t, GCompare k) => Event t (DMap k Identity) -> EventSelector t k
316−switch :: Reflex t => Behavior t (Event t a) -> Event t a
317−coincidence :: Reflex t => Event t (Event t a) -> Event t a
318−current :: Reflex t => Dynamic t a -> Behavior t a
319−updated :: Reflex t => Dynamic t a -> Event t a
320−unsafeBuildDynamic :: Reflex t => PullM t a -> Event t a -> Dynamic t a
321−unsafeBuildIncremental :: (Reflex t, Patch p) => PullM t (PatchTarget p) -> Event t p -> Incremental t p
322−mergeIncremental :: (Reflex t, GCompare k) => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
323−mergeIncrementalWithMove :: (Reflex t, GCompare k) => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
324−currentIncremental :: (Reflex t, Patch p) => Incremental t p -> Behavior t (PatchTarget p)
325−updatedIncremental :: (Reflex t, Patch p) => Incremental t p -> Event t p
326−incrementalToDynamic :: (Reflex t, Patch p) => Incremental t p -> Dynamic t (PatchTarget p)
327−behaviorCoercion :: Reflex t => Coercion a b -> Coercion (Behavior t a) (Behavior t b)
328−eventCoercion :: Reflex t => Coercion a b -> Coercion (Event t a) (Event t b)
329−dynamicCoercion :: Reflex t => Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
330−{-# INLINE never #-}
331−never = SpiderEvent S.eventNever
332−{-# INLINE constant #-}
333−constant = SpiderBehavior . S.behaviorConst
334−{-# INLINE push #-}
335−push f = SpiderEvent . S.push (coerce f) . unSpiderEvent
336−{-# INLINE pushCheap #-}
337−pushCheap f = SpiderEvent . S.pushCheap (coerce f) . unSpiderEvent
338−{-# INLINE pull #-}
339−pull = SpiderBehavior . S.pull . coerce
340−{-# INLINE merge #-}
341−merge = SpiderEvent . S.merge . S.dynamicConst . (coerce :: DMap k (Event (SpiderTimeline x)) -> DMap k (S.Event x))
342−{-# INLINE fan #-}
343−fan e = EventSelector $ SpiderEvent . S.select (S.fan (unSpiderEvent e))
344−{-# INLINE switch #-}
345−switch = SpiderEvent . S.switch . (coerce :: S.Behavior x (Event (SpiderTimeline x) a) -> S.Behavior x (S.Event x a)) . unSpiderBehavior
346−{-# INLINE coincidence #-}
347−coincidence = SpiderEvent . S.coincidence . (coerce :: S.Event x (Event (SpiderTimeline x) a) -> S.Event x (S.Event x a)) . unSpiderEvent
348−{-# INLINE current #-}
349−current = SpiderBehavior . S.dynamicCurrent . unSpiderDynamic
350−{-# INLINE updated #-}
351−updated = coerceEvent . SpiderEvent . dynamicUpdated . unSpiderDynamic
352−{-# INLINE unsafeBuildDynamic #-}
353−unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
354−{-# INLINE unsafeBuildIncremental #-}
355−unsafeBuildIncremental readV0 dv = SpiderIncremental $ S.dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
356−{-# INLINE mergeIncremental #-}
357−mergeIncremental = SpiderEvent . S.merge . (unsafeCoerce :: S.Dynamic x (PatchDMap k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMap k (S.Event x))) . unSpiderIncremental
358−{-# INLINE mergeIncrementalWithMove #-}
359−mergeIncrementalWithMove = SpiderEvent . S.mergeWithMove . (unsafeCoerce :: S.Dynamic x (PatchDMapWithMove k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMapWithMove k (S.Event x))) . unSpiderIncremental
360−{-# INLINE currentIncremental #-}
361−currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
362−{-# INLINE updatedIncremental #-}
363−updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
364−{-# INLINE incrementalToDynamic #-}
365−incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip S.push (dynamicUpdated i) $ \p -> do
366− c <- readBehaviorUntracked $ dynamicCurrent i
367− return $ Identity <$> apply p c
368−eventCoercion Coercion = Coercion
369−behaviorCoercion Coercion = Coercion
370−dynamicCoercion = unsafeCoerce
371−#endif
372−
373−-- | Coerce a 'Behavior' between representationally-equivalent value types
374−coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
375−coerceBehavior = coerceWith $ behaviorCoercion Coercion
376−
377−-- | Coerce an 'Event' between representationally-equivalent occurrence types
378−coerceEvent :: (Reflex t, Coercible a b) => Event t a -> Event t b
379−coerceEvent = coerceWith $ eventCoercion Coercion
380−
381−-- | Coerce a 'Dynamic' between representationally-equivalent value types
382−coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
383−coerceDynamic = coerceWith $ dynamicCoercion Coercion
384−
385−-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
386−-- _must_ change when and only when the 'Event' fires, such that the
387−-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
388−-- if this is not the case, the resulting 'Dynamic' will behave
389−-- nondeterministically.
390−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
391−unsafeDynamic = unsafeBuildDynamic . sample
392−
393−-- | Construct a 'Dynamic' value that never changes
394−constDyn :: Reflex t => a -> Dynamic t a
395−constDyn = pure
396−
397−instance (Reflex t, Default a) => Default (Dynamic t a) where
398− def = pure def
399−
400−-- | 'MonadSample' designates monads that can read the current value of a
401−-- 'Behavior'. This includes both 'PullM' and 'PushM'.
402−class (Applicative m, Monad m) => MonadSample t m | m -> t where
403− -- | Get the current value in the 'Behavior'
404− sample :: Behavior t a -> m a
405−
406−-- | 'MonadHold' designates monads that can create new 'Behavior's based on
407−-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
408−-- is required to create any stateful computations with Reflex.
409−class MonadSample t m => MonadHold t m where
410− -- | Create a new 'Behavior' whose value will initially be equal to the given
411− -- value and will be updated whenever the given 'Event' occurs. The update
412− -- takes effect immediately after the 'Event' occurs; if the occurrence that
413− -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
414− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
415− -- one.
416− hold :: a -> Event t a -> m (Behavior t a)
417− -- | Create a 'Dynamic' value using the given initial value that changes every
418− -- time the 'Event' occurs.
419− holdDyn :: a -> Event t a -> m (Dynamic t a)
420− -- | Create an 'Incremental' value using the given initial value that changes
421− -- every time the 'Event' occurs.
422− holdIncremental :: Patch p => PatchTarget p -> Event t p -> m (Incremental t p)
423−
424−-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
425−-- key. This is much more efficient than filtering for each key with
426−-- 'fmapMaybe'.
427−newtype EventSelector t k = EventSelector
428− { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
429− -- determined by the type of the key, so this can be used to fan-out
430− -- 'Event's whose sub-'Event's have different types.
431− --
432− -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
433− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
434− -- occurrences of an 'Event'.
435− select :: forall a. k a -> Event t a
436− }
437−
438−--------------------------------------------------------------------------------
439−-- Instances
440−--------------------------------------------------------------------------------
441−
442−instance MonadSample t m => MonadSample t (ReaderT r m) where
443− sample = lift . sample
444−
445−instance MonadHold t m => MonadHold t (ReaderT r m) where
446− hold a0 = lift . hold a0
447− holdDyn a0 = lift . holdDyn a0
448− holdIncremental a0 = lift . holdIncremental a0
449−
450−instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
451− sample = lift . sample
452−
453−instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
454− hold a0 = lift . hold a0
455− holdDyn a0 = lift . holdDyn a0
456− holdIncremental a0 = lift . holdIncremental a0
457−
458−instance MonadSample t m => MonadSample t (StateT s m) where
459− sample = lift . sample
460−
461−instance MonadHold t m => MonadHold t (StateT s m) where
462− hold a0 = lift . hold a0
463− holdDyn a0 = lift . holdDyn a0
464− holdIncremental a0 = lift . holdIncremental a0
465−
466−instance MonadSample t m => MonadSample t (ExceptT e m) where
467− sample = lift . sample
468−
469−instance MonadHold t m => MonadHold t (ExceptT e m) where
470− hold a0 = lift . hold a0
471− holdDyn a0 = lift . holdDyn a0
472− holdIncremental a0 = lift . holdIncremental a0
473−
474−instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
475− sample = lift . sample
476−
477−instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
478− hold a0 = lift . hold a0
479− holdDyn a0 = lift . holdDyn a0
480− holdIncremental a0 = lift . holdIncremental a0
481−
482−instance MonadSample t m => MonadSample t (ContT r m) where
483− sample = lift . sample
484−
485−instance MonadHold t m => MonadHold t (ContT r m) where
486− hold a0 = lift . hold a0
487− holdDyn a0 = lift . holdDyn a0
488− holdIncremental a0 = lift . holdIncremental a0
489−
490−--------------------------------------------------------------------------------
491−-- Convenience functions
492−--------------------------------------------------------------------------------
493−
494−-- | Create an 'Event' from another 'Event'. The provided function can sample
495−-- 'Behavior's and hold 'Event's.
496−pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
497−pushAlways f = push (fmap Just . f)
498−
499−-- | Flipped version of 'fmap'.
500−ffor :: Functor f => f a -> (a -> b) -> f b
501−ffor = flip fmap
502−
503−instance Reflex t => Applicative (Behavior t) where
504− pure = constant
505− f <*> x = pull $ sample f `ap` sample x
506− _ *> b = b
507− a <* _ = a
508−
509−instance Reflex t => Apply (Behavior t) where
510− (<.>) = (<*>)
511−
512−instance Reflex t => Bind (Behavior t) where
513− (>>-) = (>>=)
514−
515−instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
516− (/) = liftA2 (/)
517− fromRational = pure . fromRational
518− recip = fmap recip
519−
520−instance Reflex t => Functor (Behavior t) where
521− fmap f = pull . fmap f . sample
522−
523−instance (Reflex t, IsString a) => IsString (Behavior t a) where
524− fromString = pure . fromString
525−
526−instance Reflex t => Monad (Behavior t) where
527− a >>= f = pull $ sample a >>= sample . f
528− -- Note: it is tempting to write (_ >> b = b); however, this would result in (fail x >> return y) succeeding (returning y), which violates the law that (a >> b = a >>= \_ -> b), since the implementation of (>>=) above actually will fail. Since we can't examine 'Behavior's other than by using sample, I don't think it's possible to write (>>) to be more efficient than the (>>=) above.
529− return = constant
530− fail = error "Monad (Behavior t) does not support fail"
531−
532−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
533− mempty = constant mempty
534− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
535− mconcat = pull . fmap mconcat . mapM sample
536−
537−instance (Reflex t, Num a) => Num (Behavior t a) where
538− (+) = liftA2 (+)
539− (-) = liftA2 (-)
540− (*) = liftA2 (*)
541− abs = fmap abs
542− fromInteger = pure . fromInteger
543− negate = fmap negate
544− signum = fmap signum
545−
546−instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
547− a <> b = pull $ liftM2 (<>) (sample a) (sample b)
548− sconcat = pull . fmap sconcat . mapM sample
549−#if MIN_VERSION_semigroups(0,17,0)
550− stimes n = fmap $ stimes n
551−#else
552− times1p n = fmap $ times1p n
553−#endif
554−
555−-- | Flipped version of 'fmapMaybe'.
556−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
557−fforMaybe = flip fmapMaybe
558−
559−-- | Filter 'f a' using the provided predicate.
560−-- Relies on 'fforMaybe'.
561−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
562−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
563−
564−-- | Left-biased event union (prefers left event on simultaneous
565−-- occurrence).
566−instance Reflex t => Alt (Event t) where
567− ev1 <!> ev2 = leftmost [ev1, ev2]
568−
569−-- | 'Event' intersection (convenient interface to 'coincidence').
570−instance Reflex t => Apply (Event t) where
571− evf <.> evx = coincidence (fmap (<$> evx) evf)
572−
573−-- | 'Event' intersection (convenient interface to 'coincidence').
574−instance Reflex t => Bind (Event t) where
575− evx >>- f = coincidence (f <$> evx)
576− join = coincidence
577−
578−instance Reflex t => Functor (Event t) where
579− {-# INLINE fmap #-}
580− fmap f = fmapMaybe $ Just . f
581−
582−instance Reflex t => FunctorMaybe (Event t) where
583− {-# INLINE fmapMaybe #-}
584− fmapMaybe f = push $ return . f
585−
586−-- | Never: @'zero' = 'never'@.
587−instance Reflex t => Plus (Event t) where
588− zero = never
589−
590−-- | Replace each occurrence value of the 'Event' with the value of the
591−-- 'Behavior' at the time of that occurrence.
592−tag :: Reflex t => Behavior t b -> Event t a -> Event t b
593−tag b = pushAlways $ \_ -> sample b
594−
595−-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
596−-- current value of the 'Behavior'.
597−attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
598−attach = attachWith (,)
599−
600−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
601−-- combining it with the current value of the 'Behavior'.
602−attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
603−attachWith f = attachWithMaybe $ \a b -> Just $ f a b
604−
605−-- | Create a new 'Event' by combining each occurence with the current value of
606−-- the 'Behavior'. The occurrence is discarded if the combining function returns
607−-- Nothing
608−attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
609−attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
610−
611−-- | Create a new 'Event' that only occurs only once, on the first occurence of
612−-- the supplied 'Event'.
613−headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
614−headE e = do
615− rec be <- hold e $ fmap (const never) e'
616− let e' = switch be
617− return e'
618−
619−-- | Create a new 'Event' that occurs on all but the first occurence of the
620−-- supplied 'Event'.
621−tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
622−tailE e = snd <$> headTailE e
623−
624−-- | Create a tuple of two 'Event's with the first one occuring only the first
625−-- time the supplied 'Event' occurs and the second occuring on all but the first
626−-- occurence.
627−headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
628−headTailE e = do
629− eHead <- headE e
630− be <- hold never $ fmap (const e) eHead
631− return (eHead, switch be)
632−
633−-- | Split the supplied 'Event' into two individual 'Event's occuring at the
634−-- same time with the respective values from the tuple.
635−splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
636−splitE e = (fmap fst e, fmap snd e)
637−
638−-- | Print the supplied 'String' and the value of the 'Event' on each
639−-- occurence. This should /only/ be used for debugging.
640−--
641−-- Note: As with Debug.Trace.trace, the message will only be printed if the
642−-- 'Event' is actually used.
643−traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
644−traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
645−
646−-- | Print the output of the supplied function on each occurence of the
647−-- 'Event'. This should /only/ be used for debugging.
648−--
649−-- Note: As with Debug.Trace.trace, the message will only be printed if the
650−-- 'Event' is actually used.
651−traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
652−traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
653−
654−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
655− (<>) = alignWith (mergeThese (<>))
656− sconcat = fmap sconcat . mergeList . toList
657−#if MIN_VERSION_semigroups(0,17,0)
658− stimes n = fmap $ stimes n
659−#else
660− times1p n = fmap $ times1p n
661−#endif
662−
663−instance (Semigroup a, Reflex t) => Monoid (Event t a) where
664− mempty = never
665− mappend = (<>)
666− mconcat = fmap sconcat . mergeList
667−
668−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
669−-- list occurs. If multiple occur at the same time they are folded from the left
670−-- with the given function.
671−{-# INLINE mergeWith #-}
672−mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
673−mergeWith = mergeWith' id
674−
675−{-# INLINE mergeWith' #-}
676−mergeWith' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
677−mergeWith' f g es = fmap (Prelude.foldl1 g . map (\(Const2 _ :=> Identity v) -> f v) . DMap.toList)
678− . merge
679− . DMap.fromDistinctAscList
680− . map (\(k, v) -> Const2 k :=> v)
681− $ zip [0 :: Int ..] es
682−
683−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
684−-- list occurs. If multiple occur at the same time the value is the value of the
685−-- leftmost event.
686−{-# INLINE leftmost #-}
687−leftmost :: Reflex t => [Event t a] -> Event t a
688−leftmost = mergeWith const
689−
690−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
691−-- list occurs and has a list of the values of all 'Event's occuring at that
692−-- time.
693−mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
694−mergeList [] = never
695−mergeList es = mergeWithFoldCheap' id es
696−
697−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
698−unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
699−
700−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
701−-- occurs if at least one of them occurs and has a map of values of all 'Event's
702−-- occuring at that time.
703−mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
704−mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
705−
706−-- | Create a merge whose parents can change over time
707−mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
708−mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
709−
710−-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
711−mergeMapIncrementalWithMove :: (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Event t a)) -> Event t (Map k a)
712−mergeMapIncrementalWithMove = fmap dmapToMap . mergeIncrementalWithMove . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWithMoveWith id)
713−
714−-- | Split the event into separate events for 'Left' and 'Right' values.
715−fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
716−fanEither e =
717− let justLeft = either Just (const Nothing)
718− justRight = either (const Nothing) Just
719− in (fmapMaybe justLeft e, fmapMaybe justRight e)
720−
721−-- | Split the event into separate events for 'This' and 'That' values,
722−-- allowing them to fire simultaneously when the input value is 'These'.
723−fanThese :: Reflex t => Event t (These a b) -> (Event t a, Event t b)
724−fanThese e =
725− let this (This x) = Just x
726− this (These x _) = Just x
727− this _ = Nothing
728− that (That y) = Just y
729− that (These _ y) = Just y
730− that _ = Nothing
731− in (fmapMaybe this e, fmapMaybe that e)
732−
733−-- | Split the event into an 'EventSelector' that allows efficient selection of
734−-- the individual 'Event's.
735−fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
736−fanMap = fan . fmap mapToDMap
737−
738−-- | Switches to the new event whenever it receives one. Whenever a new event
739−-- is provided, if it is firing, its value will be the resulting event's value;
740−-- if it is not firing, but the old one is, the old one's value will be used.
741−switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
742−switchPromptly ea0 eea = do
743− bea <- hold ea0 eea
744− let eLag = switch bea
745− eCoincidences = coincidence eea
746− return $ leftmost [eCoincidences, eLag]
747−
748−-- | 'switch'es to a new event whenever it receives one. At the moment of
749−-- switching, the old event will be ignored if it fires, and the new one will be
750−-- used if it fires; this is the opposite of 'switch', which will use only the
751−-- old value.
752−switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
753−switchPromptOnly e0 e' = do
754− eLag <- switch <$> hold e0 e'
755− return $ coincidence $ leftmost [e', eLag <$ eLag]
756−
757−instance Reflex t => Align (Event t) where
758− nil = never
759− align = alignEventWithMaybe Just
760−
761−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
762−-- the 'Behavior' is true at the time of occurence.
763−gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
764−gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
765−
766−-- | Create a new behavior given a starting behavior and switch to a the behvior
767−-- carried by the event when it fires.
768−switcher :: (Reflex t, MonadHold t m)
769− => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
770−switcher b eb = pull . (sample <=< sample) <$> hold b eb
771−
772−-- | Combine two 'Dynamic's. The result will change whenever either (or both)
773−-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
774−zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
775−zipDyn = zipDynWith (,)
776−
777−-- | Combine two 'Dynamic's with a combining function. The result will change
778−-- whenever either (or both) input 'Dynamic' changes.
779−-- More efficient than liftA2.
780−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
781−zipDynWith f da db =
782− let eab = align (updated da) (updated db)
783− ec = flip push eab $ \o -> do
784− (a, b) <- case o of
785− This a -> do
786− b <- sample $ current db
787− return (a, b)
788− That b -> do
789− a <- sample $ current da
790− return (a, b)
791− These a b -> return (a, b)
792− return $ Just $ f a b
793− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
794−
795−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
796− mconcat = distributeListOverDynWith mconcat
797− mempty = constDyn mempty
798− mappend = zipDynWith mappend
799−
800−-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
801−-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
802−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
803−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
804−distributeDMapOverDynPure dm = case DMap.toList dm of
805− [] -> constDyn DMap.empty
806− [k :=> v] -> fmap (DMap.singleton k . Identity) v
807− _ ->
808− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
809− edmPre = merge $ DMap.map updated dm
810− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
811− olds <- sample $ current result
812− return $ DMap.unionWithKey (\_ _ new -> new) olds news
813− in result
814−
815−-- | Convert a list of 'Dynamic's into a 'Dynamic' list.
816−distributeListOverDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
817−distributeListOverDyn = distributeListOverDynWith id
818−
819−-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
820−distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
821−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
822−
823−-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
824−-- unless the second supplied 'Event' occurs simultaneously.
825−difference :: Reflex t => Event t a -> Event t b -> Event t a
826−difference = alignEventWithMaybe $ \case
827− This a -> Just a
828− _ -> Nothing
829−
830−alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
831−alignEventWithMaybe f ea eb =
832− fmapMaybe (f <=< dmapToThese)
833− $ merge
834− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
835−
836−
837−--------------------------------------------------------------------------------
838−-- Accumulator
839−--------------------------------------------------------------------------------
840−
841−-- | An 'Accumulator' type can be built by accumulating occurrences of an
842−-- 'Event'.
843−class Reflex t => Accumulator t f | f -> t where
844− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
845− accum f = accumMaybe $ \v o -> Just $ f v o
846− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
847− accumM f = accumMaybeM $ \v o -> Just <$> f v o
848− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
849− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
850− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
851− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
852− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
853− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
854− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
855− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
856− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
857− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
858−
859−-- | Accumulate occurrences of an 'Event', producing an output occurrence each
860−-- time. Discard the underlying 'Accumulator'.
861−mapAccum_ :: forall t m a b c. (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Event t c)
862−mapAccum_ f z e = do
863− (_ :: Behavior t a, result) <- mapAccum f z e
864− return result
865−
866−-- | Accumulate occurrences of an 'Event', possibly producing an output
867−-- occurrence each time. Discard the underlying 'Accumulator'.
868−mapAccumMaybe_ :: forall t m a b c. (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
869−mapAccumMaybe_ f z e = do
870− (_ :: Behavior t a, result) <- mapAccumMaybe f z e
871− return result
872−
873−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
874−-- an output occurrence each time. Discard the underlying 'Accumulator'.
875−mapAccumM_ :: forall t m a b c. (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Event t c)
876−mapAccumM_ f z e = do
877− (_ :: Behavior t a, result) <- mapAccumM f z e
878− return result
879−
880−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
881−-- producing an output occurrence each time. Discard the underlying
882−-- 'Accumulator'.
883−mapAccumMaybeM_ :: forall t m a b c. (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Event t c)
884−mapAccumMaybeM_ f z e = do
885− (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
886− return result
887−
888−instance Reflex t => Accumulator t (Dynamic t) where
889− accumMaybeM f z e = do
890− rec let e' = flip push e $ \o -> do
891− v <- sample $ current d'
892− f v o
893− d' <- holdDyn z e'
894− return d'
895− mapAccumMaybeM f z e = do
896− rec let e' = flip push e $ \o -> do
897− v <- sample $ current d'
898− result <- f v o
899− return $ case result of
900− (Nothing, Nothing) -> Nothing
901− _ -> Just result
902− d' <- holdDyn z $ fmapMaybe fst e'
903− return (d', fmapMaybe snd e')
904−
905−instance Reflex t => Accumulator t (Behavior t) where
906− accumMaybeM f z e = current <$> accumMaybeM f z e
907− mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
908−
909−instance Reflex t => Accumulator t (Event t) where
910− accumMaybeM f z e = updated <$> accumMaybeM f z e
911− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
912−
913−-- | Create a new 'Event' by combining each occurence with the next value of the
914−-- list using the supplied function. If the list runs out of items, all
915−-- subsequent 'Event' occurrences will be ignored.
916−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
917−zipListWithEvent f l e = do
918− let f' a b = case a of
919− h:t -> (Just t, Just $ f h b)
920− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
921− mapAccumMaybe_ f' l e
922−
923−-- | Assign a number to each occurence of the given 'Event', starting from 0
924−numberOccurrences :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Event t (b, a))
925−numberOccurrences = numberOccurrencesFrom 0
926−
927−-- | Assign a number to each occurence of the given 'Event'
928−numberOccurrencesFrom :: (Reflex t, MonadHold t m, MonadFix m, Num b) => b -> Event t a -> m (Event t (b, a))
929−numberOccurrencesFrom = mapAccum_ (\n a -> (n + 1, (n, a)))
930−
931−-- | This is used to sample the value of a 'Behavior' using an 'Event'.
932−--
933−-- The '<@>' operator is intended to be used in conjunction with
934−-- the 'Applicative' instance for 'Behavior'.
935−--
936−-- This is useful when we want to combine the values of one 'Event' and
937−-- the value of several 'Behavior's at the time the 'Event' is firing.
938−--
939−-- If we have:
940−--
941−-- > f :: a -> b -> c -> d
942−-- > b1 :: Behavior t a
943−-- > b2 :: Behavior t b
944−-- > e :: Event t c
945−--
946−-- then we can do:
947−--
948−-- > f <$> b1 <*> b2 <@> e :: Event t d
949−--
950−-- in order to apply the function 'f' to the relevant values.
951−--
952−-- The alternative would be something like:
953−--
954−-- > attachWith (\(x1, x2) y -> f x1 x2 y) ((,) <$> b1 <*> b2) e :: Event t d
955−--
956−-- or a variation involing a custom data type to hold the combination of
957−-- 'Behavior's even when that combination might only ever be used by 'f'.
958−--
959−-- A more suggestive example might be:
960−--
961−-- > handleMouse <$> bPlayerState <*> bMousePosition <@> eMouseClick :: Event t (GameState -> GameState)
962−--
963−(<@>) :: Reflex t => Behavior t (a -> b) -> Event t a -> Event t b
964−(<@>) b = push $ \x -> do
965− f <- sample b
966− return . Just . f $ x
967−infixl 4 <@>
968−
969−-- | An version of '<@>' that does not use the value of the 'Event'.
970−--
971−-- Alternatively, it is 'tag' in operator form.
972−--
973−-- This is useful when we want to combine the values of several
974−-- 'Behavior's at particular points in time using an 'Applicative'
975−-- style syntax.
976−--
977−-- If we have:
978−--
979−-- > g :: a -> b -> d
980−-- > b1 :: Behavior t a
981−-- > b2 :: Behavior t b
982−-- > e :: Event t c
983−--
984−-- where 'e' is firing at the points in time of interest.
985−--
986−-- Then we can use '<@':
987−--
988−-- > g <$> b1 <*> b2 <@ e :: Event t d
989−--
990−-- to combine the values of 'b1' and 'b2' at each of those points of time,
991−-- with the function 'g' being used to combine the values.
992−--
993−-- This is the same as '<@>' except that the 'Event' is being used only
994−-- to act as a trigger.
995−(<@) :: (Reflex t) => Behavior t b -> Event t a -> Event t b
996−(<@) = tag
997−infixl 4 <@
998−
999−-- | A 'Monad' that supports adjustment over time. After an action has been
1000−-- run, if the given events fire, it will adjust itself so that its net effect
1001−-- is as though it had originally been run with the new value. Note that there
1002−-- is some issue here with persistent side-effects: obviously, IO (and some
1003−-- other side-effects) cannot be undone, so it is up to the instance implementer
1004−-- to determine what the best meaning for this class is in such cases.
1005−class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
1006− runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
1007− traverseDMapWithKeyWithAdjust :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> m (DMap k v', Event t (PatchDMap k v'))
1008− traverseDMapWithKeyWithAdjustWithMove :: GCompare k => (forall a. k a -> v a -> m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> m (DMap k v', Event t (PatchDMapWithMove k v'))
1009−
1010−instance MonadAdjust t m => MonadAdjust t (ReaderT r m) where
1011− runWithReplace a0 a' = do
1012− r <- ask
1013− lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
1014− traverseDMapWithKeyWithAdjust f dm0 dm' = do
1015− r <- ask
1016− lift $ traverseDMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
1017− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
1018− r <- ask
1019− lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
1020−
1021−sequenceDMapWithAdjust :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
1022−sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
1023−
1024−sequenceDMapWithAdjustWithMove :: (GCompare k, MonadAdjust t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
1025−sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
1026−
1027−mapMapWithAdjustWithMove :: forall t m k v v'. (MonadAdjust t m, Ord k) => (k -> v -> m v') -> Map k v -> Event t (PatchMapWithMove k v) -> m (Map k v', Event t (PatchMapWithMove k v'))
1028−mapMapWithAdjustWithMove f m0 m' = do
1029− (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
1030− return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
1031−
1032−------------------
1033−-- Cheap Functions
1034−------------------
1035−
1036−{-# INLINE pushAlwaysCheap #-}
1037−pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1038−pushAlwaysCheap f = pushCheap (fmap Just . f)
1039−
1040−{-# INLINE fmapMaybeCheap #-}
1041−fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1042−fmapMaybeCheap f = pushCheap $ return . f
1043−
1044−{-# INLINE fforMaybeCheap #-}
1045−fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1046−fforMaybeCheap = flip fmapMaybeCheap
1047−
1048−{-# INLINE fforCheap #-}
1049−fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1050−fforCheap = flip fmapCheap
1051−
1052−{-# INLINE fmapCheap #-}
1053−fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1054−fmapCheap f = pushCheap $ return . Just . f
1055−
1056−{-# INLINE tagCheap #-}
1057−tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1058−tagCheap b = pushAlwaysCheap $ \_ -> sample b
1059−
1060−{-# INLINE mergeWithCheap #-}
1061−mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1062−mergeWithCheap = mergeWithCheap' id
1063−
1064−{-# INLINE mergeWithCheap' #-}
1065−mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1066−mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1067−
1068−{-# INLINE mergeWithFoldCheap' #-}
1069−mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1070−mergeWithFoldCheap' f es =
1071− fmapCheap (f . (\(h : t) -> h :| t) . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
1072− . merge
1073− . DMap.fromDistinctAscList
1074− . map (\(k, v) -> Const2 k :=> v)
1075− $ zip [0 :: Int ..] es
1076−
1077−--------------------------------------------------------------------------------
1078−-- Deprecated functions
1079−--------------------------------------------------------------------------------
1080−
1081−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1082−-- occurs. If both occur at the same time they are combined using 'mappend'.
1083−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1084−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1085−appendEvents = alignWith $ mergeThese mappend
1086−
1087−-- | Alias for 'headE'
1088−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1089−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
1090−onceE = headE
1091−
1092−-- | Run both sides of a 'These' monadically, combining the results.
1093−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1094−#ifdef USE_TEMPLATE_HASKELL
1095−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1096−#endif
1097−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1098−sequenceThese t = case t of
1099− This ma -> fmap This ma
1100− These ma mb -> liftM2 These ma mb
1101− That mb -> fmap That mb
src/Reflex/Dynamic.hsdeleted−601
@@ -1,601 +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 StandaloneDeriving #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−#ifdef USE_REFLEX_OPTIMIZER
17−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
18−#endif
19−-- | This module contains various functions for working with 'Dynamic' values.
20−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
21−module Reflex.Dynamic
22− ( -- * Basics
23− Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
24− , current
25− , updated
26− , holdDyn
27− , constDyn
28− , uniqDyn
29− , uniqDynBy
30− , count
31− , toggle
32− , switchPromptlyDyn
33− , tagPromptlyDyn
34− , attachPromptlyDyn
35− , attachPromptlyDynWith
36− , attachPromptlyDynWithMaybe
37− , foldDyn
38− , foldDynM
39− , foldDynMaybe
40− , foldDynMaybeM
41− , joinDynThroughMap
42− , traceDyn
43− , traceDynWith
44− , splitDynPure
45− , distributeMapOverDynPure
46− , distributeDMapOverDynPure
47− , distributeListOverDynPure
48− , Demux
49− , demux
50− , demuxed
51− -- Things that probably aren't very useful:
52− , HList (..)
53− , FHList (..)
54− , collectDynPure
55− , RebuildSortedHList (..)
56− , IsHList (..)
57− , AllAreFunctors (..)
58− , HListPtr (..)
59− , distributeFHListOverDynPure
60− -- Unsafe
61− , unsafeDynamic
62− -- * Deprecated functions
63− , apDyn
64− , attachDyn
65− , attachDynWith
66− , attachDynWithMaybe
67− , collectDyn
68− , combineDyn
69− , distributeDMapOverDyn
70− , distributeFHListOverDyn
71− , forDyn
72− , forDynM
73− , getDemuxed
74− , joinDyn
75− , mapDyn
76− , mapDynM
77− , mconcatDyn
78− , nubDyn
79− , splitDyn
80− , tagDyn
81− ) where
82−
83−import Prelude hiding (mapM, mapM_)
84−
85−import Data.Functor.Misc
86−import Reflex.Class
87−
88−import Control.Applicative ((<*>))
89−import Control.Monad hiding (forM, forM_, mapM, mapM_)
90−import Control.Monad.Fix
91−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
92−import Data.Align
93−import Data.Dependent.Map (DMap)
94−import qualified Data.Dependent.Map as DMap
95−import Data.Dependent.Sum (DSum (..))
96−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
97−import Data.Map (Map)
98−import Data.Monoid
99−import Data.These
100−
101−import Debug.Trace
102−
103−-- | Create a new 'Dynamic' that only signals changes if the values actually
104−-- changed.
105−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
106−uniqDyn = uniqDynBy (==)
107−
108−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
109−-- changes and the given function returns 'False' when given both the old and
110−-- the new values.
111−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
112−uniqDynBy eq d =
113− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
114− in unsafeDynamic (current d) e'
115−
116−-- | Create a 'Dynamic' using the initial value and change it each time the
117−-- 'Event' occurs using a folding function on the previous value and the value
118−-- of the 'Event'.
119−foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
120−foldDyn = accum . flip
121−
122−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
123−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
124−foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
125−foldDynM = accumM . flip
126−
127−-- | Create a 'Dynamic' using the provided initial value and change it each time
128−-- the provided 'Event' occurs, using a function to combine the old value with
129−-- the 'Event''s value. If the function returns 'Nothing', the value is not
130−-- changed; this is distinct from returning 'Just' the old value, since the
131−-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
132−-- in the 'Nothing' case.
133−foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
134−foldDynMaybe = accumMaybe . flip
135−
136−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
137−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
138−foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
139−foldDynMaybeM = accumMaybeM . flip
140−
141−-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
142−count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
143−count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
144−
145−-- | Create a new 'Dynamic' using the initial value that flips its
146−-- value every time the 'Event' occurs.
147−toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
148−toggle = foldDyn (const not)
149−
150−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
151−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
152−-- old and new inner Events fire simultaneously, the output will fire with the
153−-- value of the *new* 'Event'.
154−switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
155−switchPromptlyDyn de =
156− let eLag = switch $ current de
157− eCoincidences = coincidence $ updated de
158− in leftmost [eCoincidences, eLag]
159−
160−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
161−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
162−splitDynPure d = (fmap fst d, fmap snd d)
163−
164−-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
165−-- non-'Dynamic' elements.
166−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
167−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
168−
169−-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
170−-- non-'Dynamic' elements, preserving the order of the elements.
171−distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
172−distributeListOverDynPure =
173− fmap (map fromDSum . DMap.toAscList) .
174− distributeDMapOverDynPure .
175− DMap.fromDistinctAscList .
176− zipWith toDSum [0..]
177− where
178− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
179− toDSum k v = Const2 k :=> v
180− fromDSum :: DSum (Const2 Int a) Identity -> a
181− fromDSum (Const2 _ :=> Identity v) = v
182−
183−--TODO: Generalize this to functors other than Maps
184−-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
185−-- with the current values of the 'Dynamic's in a map.
186−joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
187−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
188−
189−-- | Print the value of the 'Dynamic' when it is first read and on each
190−-- subsequent change that is observed (as traceEvent), prefixed with the
191−-- provided string. This should /only/ be used for debugging.
192−--
193−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
194−-- else in the system is depending on it.
195−traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
196−traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
197−
198−-- | Print the result of applying the provided function to the value
199−-- of the 'Dynamic' when it is first read and on each subsequent change
200−-- that is observed (as traceEvent). This should /only/ be used for
201−-- debugging.
202−--
203−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
204−-- else in the system is depending on it.
205−traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
206−traceDynWith f d =
207− let e' = traceEventWith f $ updated d
208− getV0 = do
209− x <- sample $ current d
210− trace (f x) $ return x
211− in unsafeBuildDynamic getV0 e'
212−
213−-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
214−-- each time the 'Event' occurs.
215−--
216−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
217−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
218−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
219−-- will be used, since the 'Behavior' won't be updated until the end of the frame.
220−-- Additionally, this means that the output 'Event' may not be used to directly change
221−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
222−-- cyclic data flows, generally `tag (current d) e` is preferred.
223−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
224−tagPromptlyDyn = attachPromptlyDynWith const
225−
226−-- | Attach the current value of the 'Dynamic' to the value of the
227−-- 'Event' each time it occurs.
228−--
229−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
230−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
231−attachPromptlyDyn = attachPromptlyDynWith (,)
232−
233−-- | Combine the current value of the 'Dynamic' with the value of the
234−-- 'Event' each time it occurs.
235−--
236−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
237−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
238−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
239−
240−-- | Create a new 'Event' by combining the value at each occurence with the
241−-- current value of the 'Dynamic' value and possibly filtering if the combining
242−-- function returns 'Nothing'.
243−--
244−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
245−-- (current d)`. See 'tagPromptlyDyn' for details.
246−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
247−attachPromptlyDynWithMaybe f d e =
248− let e' = attach (current d) e
249− in fforMaybe (align e' $ updated d) $ \case
250− This (a, b) -> f a b -- Only the tagging event is firing, so use that
251− These (_, b) a -> f a b -- Both events are firing, so use the newer value
252− That _ -> Nothing -- The tagging event isn't firing, so don't fire
253−
254−--------------------------------------------------------------------------------
255−-- Demux
256−--------------------------------------------------------------------------------
257−
258−-- | Represents a time changing value together with an 'EventSelector' that can
259−-- efficiently detect when the underlying 'Dynamic' has a particular value.
260−-- This is useful for representing data like the current selection of a long
261−-- list.
262−--
263−-- Semantically,
264−--
265−-- > demuxed (demux d) k === fmap (== k) d
266−--
267−-- However, the when getDemuxed is used multiple times, the complexity is only
268−-- /O(log(n))/, rather than /O(n)/ for fmap.
269−data Demux t k = Demux { demuxValue :: Behavior t k
270− , demuxSelector :: EventSelector t (Const2 k Bool)
271− }
272−
273−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
274−-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
275−demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
276−demux k = Demux (current k)
277− (fan $ attachWith (\k0 k1 -> if k0 == k1
278− then DMap.empty
279− else DMap.fromList [Const2 k0 :=> Identity False,
280− Const2 k1 :=> Identity True])
281− (current k) (updated k))
282−
283−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
284−-- faster than) mapping over the original 'Dynamic' and checking whether it is
285−-- equal to the given key.
286−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
287−demuxed d k =
288− let e = select (demuxSelector d) (Const2 k)
289− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
290−
291−--------------------------------------------------------------------------------
292−-- collectDyn
293−--------------------------------------------------------------------------------
294−
295−--TODO: This whole section is badly in need of cleanup
296−
297−-- | A heterogeneous list whose type and length are fixed statically. This is
298−-- reproduced from the 'HList' package due to integration issues, and because
299−-- very little other functionality from that library is needed.
300−data HList (l::[*]) where
301− HNil :: HList '[]
302− HCons :: e -> HList l -> HList (e ': l)
303−
304−infixr 2 `HCons`
305−
306−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
307−type instance HRevApp '[] l = l
308−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
309−
310−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
311−hRevApp HNil l = l
312−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
313−
314−hReverse :: HList l -> HList (HRevApp l '[])
315−hReverse l = hRevApp l HNil
316−
317−hBuild :: (HBuild' '[] r) => r
318−hBuild = hBuild' HNil
319−
320−class HBuild' l r where
321− hBuild' :: HList l -> r
322−
323−instance (l' ~ HRevApp l '[])
324− => HBuild' l (HList l') where
325− hBuild' = hReverse
326−
327−instance HBuild' (a ': l) r
328− => HBuild' l (a->r) where
329− hBuild' l x = hBuild' (HCons x l)
330−
331−-- | Like 'HList', but with a functor wrapping each element.
332−data FHList f l where
333− FHNil :: FHList f '[]
334− FHCons :: f e -> FHList f l -> FHList f (e ': l)
335−
336−instance GEq (HListPtr l) where
337−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
338− geqToEq _ = id
339− geqUnify HHeadPtr HHeadPtr _ same = same
340− geqUnify (HTailPtr a) (HTailPtr b) different same = geqUnify a b different same
341− geqUnify _ _ different _ = different
342−#endif
343− HHeadPtr `geq` HHeadPtr = Just Refl
344− HHeadPtr `geq` HTailPtr _ = Nothing
345− HTailPtr _ `geq` HHeadPtr = Nothing
346− HTailPtr a `geq` HTailPtr b = a `geq` b
347−
348−instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
349−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
350− gcompareToOrd _ = id
351−#endif
352− HHeadPtr `gcompare` HHeadPtr = GEQ
353− HHeadPtr `gcompare` HTailPtr _ = GLT
354− HTailPtr _ `gcompare` HHeadPtr = GGT
355− HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
356−
357−-- | A typed index into a typed heterogeneous list.
358−data HListPtr l a where
359− HHeadPtr :: HListPtr (h ': t) h
360− HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
361−
362−deriving instance Eq (HListPtr l a)
363−deriving instance Ord (HListPtr l a)
364−
365−fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
366−fhlistToDMap = DMap.fromList . go
367− where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
368− go = \case
369− FHNil -> []
370− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
371−
372−-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
373−-- they must be contiguous and sorted.
374−class RebuildSortedHList l where
375− rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
376− rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
377−
378−instance RebuildSortedHList '[] where
379− rebuildSortedFHList l = case l of
380− [] -> FHNil
381− _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
382− rebuildSortedHList l = case l of
383− [] -> HNil
384− _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
385−
386−instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
387− rebuildSortedFHList l = case l of
388− ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
389− _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
390− rebuildSortedHList l = case l of
391− ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
392− _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
393−
394−dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
395−dmapToHList = rebuildSortedHList . DMap.toList
396−
397−-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
398−-- single 'Dynamic' whose value represents the current values of all of the
399−-- input 'Dynamic's.
400−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
401−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
402−
403−-- | Indicates that all elements in a type-level list are applications of the
404−-- same functor.
405−class AllAreFunctors (f :: a -> *) (l :: [a]) where
406− type FunctorList f l :: [*]
407− toFHList :: HList (FunctorList f l) -> FHList f l
408− fromFHList :: FHList f l -> HList (FunctorList f l)
409−
410−instance AllAreFunctors f '[] where
411− type FunctorList f '[] = '[]
412− toFHList l = case l of
413− HNil -> FHNil
414−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
415− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
416−#endif
417− fromFHList FHNil = HNil
418−
419−instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
420− type FunctorList f (h ': t) = f h ': FunctorList f t
421− toFHList l = case l of
422− a `HCons` b -> a `FHCons` toFHList b
423−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
424− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
425−#endif
426− fromFHList (a `FHCons` b) = a `HCons` fromFHList b
427−
428−-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
429−-- single 'Dynamic' whose value represents all the current values of the input's
430−-- consitutent 'Dynamic's.
431−collectDynPure :: ( RebuildSortedHList (HListElems b)
432− , IsHList a, IsHList b
433− , AllAreFunctors (Dynamic t) (HListElems b)
434− , Reflex t
435− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
436− ) => a -> Dynamic t b
437−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
438−
439−-- | Poor man's 'Generic's for product types only.
440−class IsHList a where
441− type HListElems a :: [*]
442− toHList :: a -> HList (HListElems a)
443− fromHList :: HList (HListElems a) -> a
444−
445−instance IsHList (a, b) where
446− type HListElems (a, b) = [a, b]
447− toHList (a, b) = hBuild a b
448− fromHList l = case l of
449− a `HCons` b `HCons` HNil -> (a, b)
450−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
451− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
452−#endif
453−
454−instance IsHList (a, b, c, d) where
455− type HListElems (a, b, c, d) = [a, b, c, d]
456− toHList (a, b, c, d) = hBuild a b c d
457− fromHList l = case l of
458− a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
459−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
460− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
461−#endif
462−
463−instance IsHList (a, b, c, d, e, f) where
464− type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
465− toHList (a, b, c, d, e, f) = hBuild a b c d e f
466− fromHList l = case l of
467− a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
468−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
469− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
470−#endif
471−
472−--------------------------------------------------------------------------------
473−-- Deprecated functions
474−--------------------------------------------------------------------------------
475−
476−-- | Map a function over a 'Dynamic'.
477−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
478−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
479−mapDyn f = return . fmap f
480−
481−-- | Map a sampling function over a 'Dynamic'. The sampling function will use the input 'Dynamic''s 'current' 'Behavior' until the first time its 'updated' 'Event' fires, at which point it will be invoked only once each time the 'Event' fires.
482−{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
483−mapDynM :: forall t m a b. (Reflex t, MonadHold t m) => (forall m'. MonadSample t m' => a -> m' b) -> Dynamic t a -> m (Dynamic t b)
484−mapDynM f d = do
485− let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
486− eb' = fmap constant e'
487− v0 = pull $ f =<< sample (current d)
488− bb' :: Behavior t (Behavior t b) <- hold v0 eb'
489− let b' = pull $ sample =<< sample bb'
490− return $ unsafeDynamic b' e'
491−
492−-- | Flipped version of 'mapDyn'.
493−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
494−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
495−forDyn a = return . ffor a
496−
497−-- | Flipped version of 'mapDynM'
498−{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
499−forDynM :: forall t m a b. (Reflex t, MonadHold t m) => Dynamic t a -> (forall m'. MonadSample t m' => a -> m' b) -> m (Dynamic t b)
500−forDynM d f = mapDynM f d
501−
502−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
503−-- of the tuple.
504−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
505−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
506−splitDyn = return . splitDynPure
507−
508−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
509−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
510−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
511−mconcatDyn = return . mconcat
512−
513−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
514−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
515−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
516−distributeDMapOverDyn = return . distributeDMapOverDynPure
517−
518−-- | Merge two 'Dynamic's into a new one using the provided function. The new
519−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
520−-- its value.
521−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
522−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
523−combineDyn f a b = return $ zipDynWith f a b
524−
525−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
526−--
527−-- > do
528−-- > person <- Person `mapDyn` dynFirstName
529−-- > `apDyn` dynListName
530−-- > `apDyn` dynAge
531−-- > `apDyn` dynAddress
532−{-# DEPRECATED apDyn "Use 'ffor m (<*> a)' instead of 'apDyn m a'; consider eliminating monadic style, since Dynamics are now Applicative and can be used with applicative style directly" #-}
533−#ifdef USE_TEMPLATE_HASKELL
534−{-# ANN apDyn "HLint: ignore Use fmap" #-}
535−#endif
536−apDyn :: forall t m a b. (Reflex t, Monad m)
537− => m (Dynamic t (a -> b))
538− -> Dynamic t a
539− -> m (Dynamic t b)
540−apDyn m a = fmap (<*> a) m
541−
542−--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
543−-- | This function no longer needs to be monadic, so it has been replaced by
544−-- 'demuxed', which is pure.
545−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
546−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
547−getDemuxed d = return . demuxed d
548−
549−-- | This function no longer needs to be monadic, so it has been replaced by
550−-- 'distributeFHListOverDynPure', which is pure.
551−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
552−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
553−distributeFHListOverDyn = return . distributeFHListOverDynPure
554−
555−-- | This function no longer needs to be monadic, so it has been replaced by
556−-- 'collectDynPure', which is pure.
557−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
558−collectDyn :: ( RebuildSortedHList (HListElems b)
559− , IsHList a, IsHList b
560− , AllAreFunctors (Dynamic t) (HListElems b)
561− , Reflex t, Monad m
562− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
563− ) => a -> m (Dynamic t b)
564−collectDyn = return . collectDynPure
565−
566−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
567−-- semantics.
568−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
569−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
570−tagDyn = tagPromptlyDyn
571−
572−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
573−-- semantics.
574−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
575−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
576−attachDyn = attachPromptlyDyn
577−
578−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
579−-- semantics.
580−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
581−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
582−attachDynWith = attachPromptlyDynWith
583−
584−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
585−-- its semantics.
586−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
587−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
588−attachDynWithMaybe = attachPromptlyDynWithMaybe
589−
590−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
591−-- current value will always be equal to the current value's current value, and
592−-- will change whenever either the inner or the outer (or both) values change.
593−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
594−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
595−joinDyn = join
596−
597−-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
598−-- been renamed to 'uniqDyn'
599−{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
600−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
601−nubDyn = uniqDyn
src/Reflex/Dynamic/TH.hsdeleted−130
@@ -1,130 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE PatternGuards #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE TemplateHaskell #-}
7−{-# LANGUAGE TypeOperators #-}
8−#ifdef USE_REFLEX_OPTIMIZER
9−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10−#endif
11−-- | Template Haskell helper functions for building complex 'Dynamic' values.
12−module Reflex.Dynamic.TH
13− ( qDynPure
14− , unqDyn
15− , mkDynPure
16− -- * Deprecated functions
17− , qDyn
18− , mkDyn
19− ) where
20−
21−import Reflex.Dynamic
22−
23−import Control.Monad.State
24−import Data.Data
25−import Data.Generics
26−import Data.Monoid
27−import qualified Language.Haskell.Exts as Hs
28−import qualified Language.Haskell.Meta.Syntax.Translate as Hs
29−import Language.Haskell.TH
30−import Language.Haskell.TH.Quote
31−import qualified Language.Haskell.TH.Syntax as TH
32−
33−-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
34−-- @$(unqDyn [| x |])@ to refer to any expression @x@ of type @Dynamic t a@; the
35−-- unquoted result will be of type @a@
36−qDynPure :: Q Exp -> Q Exp
37−qDynPure qe = do
38− e <- qe
39− let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
40− f d = case eqT of
41− Just (Refl :: d :~: Exp)
42− | AppE (VarE m) eInner <- d
43− , m == 'unqMarker
44− -> do n <- lift $ newName "dynamicQuotedExpressionVariable"
45− modify ((n, eInner):)
46− return $ VarE n
47− _ -> gmapM f d
48− (e', exprsReversed) <- runStateT (gmapM f e) []
49− let exprs = reverse exprsReversed
50− arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
51− param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
52− [| $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |]
53−
54−-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
55−-- 'qDyn' quotation.
56−unqDyn :: Q Exp -> Q Exp
57−unqDyn e = [| unqMarker $e |]
58−
59−-- | This type represents an occurrence of unqDyn before it has been processed
60−-- by qDyn. If you see it in a type error, it probably means that unqDyn has
61−-- been used outside of a qDyn context.
62−data UnqDyn
63−
64−-- unqMarker must not be exported; it is used only as a way of smuggling data
65−-- from unqDyn to qDyn
66−
67−--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
68−unqMarker :: a -> UnqDyn
69−unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
70−
71−-- | Create a 'Dynamic' value using other 'Dynamic's as inputs. The result is
72−-- sometimes more concise and readable than the equivalent 'Applicative'-based
73−-- expression. For example:
74−--
75−-- > [mkDyn| $x + $v * $t + 1/2 * $a * $t ^ 2 |]
76−--
77−-- would have a very cumbersome 'Applicative' encoding.
78−mkDynPure :: QuasiQuoter
79−mkDynPure = QuasiQuoter
80− { quoteExp = mkDynExp
81− , quotePat = error "mkDyn: pattern splices are not supported"
82− , quoteType = error "mkDyn: type splices are not supported"
83− , quoteDec = error "mkDyn: declaration splices are not supported"
84− }
85−
86−mkDynExp :: String -> Q Exp
87−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
88− Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
89− Hs.ParseOk e -> qDynPure $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
90− where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
91−#if MIN_VERSION_haskell_src_exts(1,18,0)
92− antiE :: Hs.Exp Hs.SrcSpanInfo -> Hs.Exp Hs.SrcSpanInfo
93− antiE x = case x of
94− Hs.SpliceExp l se ->
95− Hs.App l (Hs.Var l $ Hs.Qual l (Hs.ModuleName l modName) (Hs.Ident l occName)) $ case se of
96− Hs.IdSplice l2 v -> Hs.Var l2 $ Hs.UnQual l2 $ Hs.Ident l2 v
97− Hs.ParenSplice _ ps -> ps
98− _ -> x
99−#else
100− antiE x = case x of
101− Hs.SpliceExp se ->
102− Hs.App (Hs.Var $ Hs.Qual (Hs.ModuleName modName) (Hs.Ident occName)) $ case se of
103− Hs.IdSplice v -> Hs.Var $ Hs.UnQual $ Hs.Ident v
104− Hs.ParenSplice ps -> ps
105− _ -> x
106−#endif
107− reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
108− | modName == modName' && occName == occName' = 'unqMarker
109− reinstateUnqDyn x = x
110−
111−--------------------------------------------------------------------------------
112−-- Deprecated
113−--------------------------------------------------------------------------------
114−
115−{-# DEPRECATED qDyn "Instead of $(qDyn x), use return $(qDynPure x)" #-}
116−-- | Like 'qDynPure', but wraps its result monadically using 'return'. This is
117−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
118−qDyn :: Q Exp -> Q Exp
119−qDyn qe = [| return $(qDynPure qe) |]
120−
121−{-# DEPRECATED mkDyn "Instead of [mkDyn| x |], use return [mkDynPure| x |]" #-}
122−-- | Like 'mkDynPure', but wraps its result monadically using 'return'. This is
123−-- no longer necessary, due to 'Dynamic' being an instance of 'Functor'.
124−mkDyn :: QuasiQuoter
125−mkDyn = QuasiQuoter
126− { quoteExp = \s -> [| return $(mkDynExp s) |]
127− , quotePat = error "mkDyn: pattern splices are not supported"
128− , quoteType = error "mkDyn: type splices are not supported"
129− , quoteDec = error "mkDyn: declaration splices are not supported"
130− }
src/Reflex/Dynamic/Uniq.hsdeleted−105
@@ -1,105 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−#ifdef USE_REFLEX_OPTIMIZER
6−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7−#endif
8−-- | This module provides a variation of 'Dynamic' values that uses cheap
9−-- pointer equality checks to reduce the amount of signal propagation needed.
10−module Reflex.Dynamic.Uniq
11− ( UniqDynamic
12− , uniqDynamic
13− , fromUniqDynamic
14− , alreadyUniqDynamic
15− ) where
16−
17−import Control.Applicative (Applicative (..))
18−import GHC.Exts
19−import Reflex.Class
20−import Reflex.Dynamic
21−
22−-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
23−-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
24−-- the value inside a 'UniqDynamic' is always evaluated to
25−-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
26−--
27−-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
28−-- unnecessary update propagation; this is much more efficient than performing
29−-- full comparisons. However, when the 'UniqDynamic' is converted back into a
30−-- regular 'Dynamic', a full comparison is performed.
31−newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
32−
33−-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
34−uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
35−uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
36− old <- sample $ current d --TODO: Is it better to sample ourselves here?
37− return $ unsafeJustChanged old new
38−
39−-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
40−-- final check using the output type's 'Eq' instance to ensure deterministic
41−-- behavior.
42−--
43−-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
44−-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
45−-- eliminate more 'updated' occurrences than it should. For example, NaN values
46−-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
47−-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
48−-- to lose changes from NaN to NaN.
49−fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
50−fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
51− where
52− -- Only consider values different if they fail both pointer equality /and/
53− -- 'Eq' equality. This is to make things a bit more deterministic in the
54− -- case of unlawful 'Eq' instances. However, it is still possible to
55− -- achieve nondeterminism by constructing elements that are identical in
56− -- value, unequal according to 'Eq', and nondeterministically equal or
57− -- nonequal by pointer quality. I suspect that it is impossible to make the
58− -- behavior deterministic in this case.
59− superEq a b = a `unsafePtrEq` b || a == b
60−
61−-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
62−-- faster than uniqDynamic when used with a Dynamic whose values are always (or
63−-- nearly always) different from its previous values; if used with a Dynamic
64−-- whose values do not change frequently, it may be much slower than uniqDynamic
65−alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
66−alreadyUniqDynamic = UniqDynamic
67−
68−unsafePtrEq :: a -> a -> Bool
69−unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
70− 0# -> False
71− _ -> True
72−
73−unsafeJustChanged :: a -> a -> Maybe a
74−unsafeJustChanged old new =
75− if old `unsafePtrEq` new
76− then Nothing
77− else Just new
78−
79−instance Reflex t => Accumulator t (UniqDynamic t) where
80− accumMaybeM f z e = do
81− let f' old change = do
82− mNew <- f old change
83− return $ unsafeJustChanged old =<< mNew
84− d <- accumMaybeM f' z e
85− return $ UniqDynamic d
86− mapAccumMaybeM f z e = do
87− let f' old change = do
88− (mNew, output) <- f old change
89− return (unsafeJustChanged old =<< mNew, output)
90− (d, out) <- mapAccumMaybeM f' z e
91− return (UniqDynamic d, out)
92−
93−instance Reflex t => Functor (UniqDynamic t) where
94− fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
95−
96−instance Reflex t => Applicative (UniqDynamic t) where
97− pure = UniqDynamic . constDyn
98− UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
99− _ *> b = b
100− a <* _ = a
101−
102−instance Reflex t => Monad (UniqDynamic t) where
103− UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
104− _ >> b = b
105− return = pure
src/Reflex/DynamicWriter.hsdeleted−191
@@ -1,191 +0,0 @@
1−-- | This module defines 'MonadDynamicWriter' and 'DynamicWriterT', its standard
2−-- implementation.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE LambdaCase #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.DynamicWriter
18− ( MonadDynamicWriter (..)
19− , DynamicWriterT (..)
20− , runDynamicWriterT
21− , withDynamicWriterT
22− ) where
23−
24−import Control.Monad.Exception
25−import Control.Monad.Identity
26−import Control.Monad.IO.Class
27−import Control.Monad.Reader
28−import Control.Monad.Ref
29−import Control.Monad.State.Strict
30−import Data.Align
31−import Data.Dependent.Map (DMap)
32−import qualified Data.Dependent.Map as DMap
33−import Data.Functor.Misc
34−import Data.Map (Map)
35−import qualified Data.Map as Map
36−import Data.Semigroup
37−import Data.Some (Some)
38−import Data.These
39−import Reflex.Class
40−import Reflex.Host.Class
41−import qualified Reflex.Patch.MapWithMove as MapWithMove
42−import Reflex.PerformEvent.Class
43−import Reflex.PostBuild.Class
44−import Reflex.Requester.Class
45−import Reflex.TriggerEvent.Class
46−
47−instance MonadTrans (DynamicWriterT t w) where
48− lift = DynamicWriterT . lift
49−
50−mapIncrementalMapValues :: (Reflex t, Patch (p v), Patch (p v'), PatchTarget (p v) ~ f v, PatchTarget (p v') ~ f v', Functor p, Functor f) => (v -> v') -> Incremental t (p v) -> Incremental t (p v')
51−mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
52−
53−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
54−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
55− where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
56− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
57−
58−mergeDynIncrementalWithMove :: forall t k v. (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
59−mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
60− where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
61− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
62− f :: These (PatchMapWithMove k v) (Map k v) -> PatchMapWithMove k v
63− f x = unsafePatchMapWithMove $
64− let (p, changed) = case x of
65− This p_ -> (unPatchMapWithMove p_, mempty)
66− That c -> (mempty, c)
67− These p_ c -> (unPatchMapWithMove p_, c)
68− (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ MapWithMove.nodeInfoMapMFrom $ \case
69− MapWithMove.From_Insert v -> return $ MapWithMove.From_Insert v
70− MapWithMove.From_Delete -> return $ MapWithMove.From_Delete
71− MapWithMove.From_Move k -> case Map.lookup k changed of
72− Nothing -> return $ MapWithMove.From_Move k
73− Just v -> do
74− modify (k:)
75− return $ MapWithMove.From_Insert v
76− noLongerMovedMap = Map.fromList $ fmap (\k -> (k, ())) noLongerMoved
77− in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
78−
79−-- | A basic implementation of 'MonadDynamicWriter'.
80−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
81−
82−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
83− type Ref (DynamicWriterT t w m) = Ref m
84− newRef = lift . newRef
85− readRef = lift . readRef
86− writeRef r = lift . writeRef r
87−
88−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
89− atomicModifyRef r = lift . atomicModifyRef r
90−
91−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
92− newEventWithTrigger = lift . newEventWithTrigger
93− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
94−
95−-- | Run a 'DynamicWriterT' action. The dynamic writer output will be provided
96−-- along with the result of the action.
97−runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
98−runDynamicWriterT (DynamicWriterT a) = do
99− (result, ws) <- runStateT a []
100− return (result, mconcat $ reverse ws)
101−
102−-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
103−-- and combines them monoidally to provide a 'Dynamic' result.
104−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
105− tellDyn :: Dynamic t w -> m ()
106−
107−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
108− tellDyn w = DynamicWriterT $ modify (w :)
109−
110−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
111− ask = lift ask
112− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
113− reader = lift . reader
114−
115−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
116− type Performable (DynamicWriterT t w m) = Performable m
117− performEvent_ = lift . performEvent_
118− performEvent = lift . performEvent
119−
120−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
121− newTriggerEvent = lift newTriggerEvent
122− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
123− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
124−
125−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
126− getPostBuild = lift getPostBuild
127−
128−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
129− tellDyn = lift . tellDyn
130−
131−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
132− get = lift get
133− put = lift . put
134−
135−newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
136−
137−-- | When the execution of a 'DynamicWriterT' action is adjusted using
138−-- 'MonadAdjust', the 'Dynamic' output of that action will also be updated to
139−-- match.
140−instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => MonadAdjust t (DynamicWriterT t w m) where
141− runWithReplace a0 a' = do
142− (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
143− tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
144− return (fst result0, fst <$> result')
145− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap weakenPatchDMapWith mergeDynIncremental
146− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeDynIncrementalWithMove
147−
148−traverseDMapWithKeyWithAdjustImpl :: forall t w k v' p p' v m. (PatchTarget (p' (Some k) (Dynamic t w)) ~ Map (Some k) (Dynamic t w), PatchTarget (p' (Some k) w) ~ Map (Some k) w, Patch (p' (Some k) w), Patch (p' (Some k) (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m)
149− => ( (forall a. k a -> v a -> m (DynamicWriterTLoweredResult t w v' a))
150− -> DMap k v
151− -> Event t (p k v)
152− -> m (DMap k (DynamicWriterTLoweredResult t w v'), Event t (p k (DynamicWriterTLoweredResult t w v')))
153− )
154− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> v' a) -> p k (DynamicWriterTLoweredResult t w v') -> p k v')
155− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> Dynamic t w) -> p k (DynamicWriterTLoweredResult t w v') -> p' (Some k) (Dynamic t w))
156− -> (Incremental t (p' (Some k) (Dynamic t w)) -> Incremental t (p' (Some k) w))
157− -> (forall a. k a -> v a -> DynamicWriterT t w m (v' a))
158− -> DMap k v
159− -> Event t (p k v)
160− -> DynamicWriterT t w m (DMap k v', Event t (p k v'))
161−traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith mergeMyDynIncremental f (dm0 :: DMap k v) dm' = do
162− (result0, result') <- lift $ base (\k v -> fmap DynamicWriterTLoweredResult $ runDynamicWriterT $ f k v) dm0 dm'
163− let getValue (DynamicWriterTLoweredResult (v, _)) = v
164− getWritten (DynamicWriterTLoweredResult (_, w)) = w
165− liftedResult0 = DMap.map getValue result0
166− liftedResult' = ffor result' $ mapPatch getValue
167− liftedWritten0 :: Map (Some k) (Dynamic t w)
168− liftedWritten0 = weakenDMapWith getWritten result0
169− liftedWritten' = ffor result' $ weakenPatchWith getWritten
170− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
171− i <- holdIncremental liftedWritten0 liftedWritten'
172− tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
173− return (liftedResult0, liftedResult')
174−
175−-- | Map a function over the output of a 'DynamicWriterT'.
176−withDynamicWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m, MonadFix m)
177− => (w -> w')
178− -> DynamicWriterT t w m a
179− -> DynamicWriterT t w' m a
180−withDynamicWriterT f dw = do
181− (r, d) <- lift $ do
182− (r, d) <- runDynamicWriterT dw
183− let d' = fmap f d
184− return (r, d')
185− tellDyn d
186− return r
187−
188−instance Requester t m => Requester t (DynamicWriterT t w m) where
189− type Request (DynamicWriterT t w m) = Request m
190− type Response (DynamicWriterT t w m) = Response m
191− withRequesting f = DynamicWriterT $ withRequesting $ unDynamicWriterT . f
src/Reflex/EventWriter.hsdeleted−170
@@ -1,170 +0,0 @@
1−-- | This module provides 'EventWriterT', the standard implementation of
2−-- 'EventWriter'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−module Reflex.EventWriter
17− ( EventWriterT (..)
18− , runEventWriterT
19− , EventWriter (..)
20− , runWithReplaceEventWriterTWith
21− , sequenceDMapWithAdjustEventWriterTWith
22− ) where
23−
24−import Reflex.Class
25−import Reflex.Host.Class
26−import Reflex.PerformEvent.Class
27−import Reflex.PostBuild.Class
28−import Reflex.Requester.Class
29−import Reflex.TriggerEvent.Class
30−
31−import Control.Monad.Exception
32−import Control.Monad.Identity
33−import Control.Monad.Reader
34−import Control.Monad.Ref
35−import Control.Monad.State.Strict
36−import Data.Dependent.Map (DMap)
37−import qualified Data.Dependent.Map as DMap
38−import Data.Foldable
39−import Data.Functor.Compose
40−import Data.Functor.Misc
41−import qualified Data.List.NonEmpty as NonEmpty
42−import Data.Map (Map)
43−import Data.Semigroup
44−import Data.Sequence
45−import Data.Some (Some)
46−import Data.Tuple
47−
48−-- | 'EventWriter' efficiently collects 'Event' values using 'tellEvent'
49−-- and combines them monoidally to provide an 'Event' result.
50−class (Monad m, Monoid w) => EventWriter t w m | m -> t w where
51− tellEvent :: Event t w -> m ()
52−
53−-- | A basic implementation of 'EventWriter'.
54−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (Seq (Event t w)) m a }
55− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
56−
57−-- | Run a 'EventWriterT' action.
58−runEventWriterT :: (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
59−runEventWriterT (EventWriterT a) = do
60− (result, requests) <- runStateT a mempty
61− return (result, mconcat $ toList requests)
62−
63−instance (Reflex t, Monad m, Monoid w) => EventWriter t w (EventWriterT t w m) where
64− tellEvent w = EventWriterT $ modify (|> w)
65−
66−instance EventWriter t w m => EventWriter t w (ReaderT r m) where
67− tellEvent = lift . tellEvent
68−
69−instance MonadTrans (EventWriterT t w) where
70− lift = EventWriterT . lift
71−
72−instance MonadSample t m => MonadSample t (EventWriterT t w m) where
73− sample = lift . sample
74−
75−instance MonadHold t m => MonadHold t (EventWriterT t w m) where
76− hold v0 = lift . hold v0
77− holdDyn v0 = lift . holdDyn v0
78− holdIncremental v0 = lift . holdIncremental v0
79−
80−instance (Reflex t, MonadAdjust t m, MonadHold t m, Monoid w, Semigroup w) => MonadAdjust t (EventWriterT t w m) where
81− runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
82− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental
83− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove
84−
85−instance Requester t m => Requester t (EventWriterT t w m) where
86− type Request (EventWriterT t w m) = Request m
87− type Response (EventWriterT t w m) = Response m
88− withRequesting f = EventWriterT $ withRequesting $ unEventWriterT . f
89−
90−
91−-- | Given a function like 'runWithReplace' for the underlying monad, implement
92−-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
93−-- monad doesn't have a 'MonadAdjust' instance or to override the default
94−-- 'MonadAdjust' behavior.
95−runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Monoid w)
96− => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
97− -> EventWriterT t w m a
98− -> Event t (EventWriterT t w m b)
99− -> EventWriterT t w m (a, Event t b)
100−runWithReplaceEventWriterTWith f a0 a' = do
101− let g :: EventWriterT t w m c -> m (c, Seq (Event t w))
102− g (EventWriterT r) = runStateT r mempty
103− (result0, result') <- f (g a0) $ fmap g a'
104− request <- holdDyn (fmapCheap (mconcat . NonEmpty.toList) $ mergeList $ toList $ snd result0) $ fmapCheap (fmapCheap (mconcat . NonEmpty.toList) . mergeList . toList . snd) result'
105− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
106− EventWriterT $ modify $ flip (|>) $ coincidence $ updated request
107− EventWriterT $ modify $ flip (|>) $ switch $ current request
108− return (fst result0, fmapCheap fst result')
109−
110−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
111−sequenceDMapWithAdjustEventWriterTWith :: forall t m p p' w k v v'. (Reflex t, MonadHold t m, Monoid w, Semigroup w, Patch (p' (Some k) (Event t w)), PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w))
112− => ( (forall a. k a -> v a -> m (Compose ((,) (Seq (Event t w))) v' a))
113− -> DMap k v
114− -> Event t (p k v)
115− -> EventWriterT t w m (DMap k (Compose ((,) (Seq (Event t w))) v'), Event t (p k (Compose ((,) (Seq (Event t w))) v')))
116− )
117− -> ((forall a. Compose ((,) (Seq (Event t w))) v' a -> v' a) -> p k (Compose ((,) (Seq (Event t w))) v') -> p k v')
118− -> ((forall a. Compose ((,) (Seq (Event t w))) v' a -> Event t w) -> p k (Compose ((,) (Seq (Event t w))) v') -> p' (Some k) (Event t w))
119− -> (p' (Some k) (Event t w) -> [Event t w])
120− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (Map (Some k) w))
121− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
122− -> DMap k v
123− -> Event t (p k v)
124− -> EventWriterT t w m (DMap k v', Event t (p k v'))
125−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
126− let f' :: forall a. k a -> v a -> m (Compose ((,) (Seq (Event t w))) v' a)
127− f' k v = Compose . swap <$> runStateT (unEventWriterT $ f k v) mempty
128− (children0, children') <- base f' dm0 dm'
129− let result0 = DMap.map (snd . getCompose) children0
130− result' = fforCheap children' $ mapPatch $ snd . getCompose
131− requests0 :: Map (Some k) (Event t w)
132− requests0 = weakenDMapWith (mconcat . toList . fst . getCompose) children0
133− requests' :: Event t (p' (Some k) (Event t w))
134− requests' = fforCheap children' $ weakenPatchWith $ mconcat . toList . fst . getCompose
135− childRequestMap :: Incremental t (p' (Some k) (Event t w)) <- holdIncremental requests0 requests'
136− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
137− EventWriterT $ modify $ flip (|>) $ coincidence $ fforCheap requests' $ \p -> mconcat $ patchNewElements p --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
138− EventWriterT $ modify $ flip (|>) $ fold <$> mergePatchIncremental childRequestMap
139− return (result0, result')
140−
141−instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
142− type Performable (EventWriterT t w m) = Performable m
143− performEvent_ = lift . performEvent_
144− performEvent = lift . performEvent
145−
146−instance PostBuild t m => PostBuild t (EventWriterT t w m) where
147− getPostBuild = lift getPostBuild
148−
149−instance TriggerEvent t m => TriggerEvent t (EventWriterT t w m) where
150− newTriggerEvent = lift newTriggerEvent
151− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
152− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
153−
154−instance MonadReader r m => MonadReader r (EventWriterT t w m) where
155− ask = lift ask
156− local f (EventWriterT a) = EventWriterT $ mapStateT (local f) a
157− reader = lift . reader
158−
159−instance MonadRef m => MonadRef (EventWriterT t w m) where
160− type Ref (EventWriterT t w m) = Ref m
161− newRef = lift . newRef
162− readRef = lift . readRef
163− writeRef r = lift . writeRef r
164−
165−instance MonadAtomicRef m => MonadAtomicRef (EventWriterT t w m) where
166− atomicModifyRef r = lift . atomicModifyRef r
167−
168−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (EventWriterT t w m) where
169− newEventWithTrigger = lift . newEventWithTrigger
170− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
src/Reflex/FunctorMaybe.hsdeleted−29
@@ -1,29 +0,0 @@
1−-- | This module defines the FunctorMaybe class, which extends Functors with the
2−-- ability to delete values.
3−module Reflex.FunctorMaybe
4− ( FunctorMaybe (..)
5− ) where
6−
7−import Data.Maybe
8−
9−--TODO: See if there's a better class in the standard libraries already
10−
11−-- | A class for values that combines filtering and mapping using 'Maybe'.
12−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
13−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
14−-- and e.g. 'Event' is instance of neither.
15−--
16−-- A definition of 'fmapMaybe' must satisfy the following laws:
17−--
18−-- [/identity/]
19−-- @'fmapMaybe' 'Just' ≡ 'id'@
20−--
21−-- [/composition/]
22−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
23−class FunctorMaybe f where
24− -- | Combined mapping and filtering function.
25− fmapMaybe :: (a -> Maybe b) -> f a -> f b
26−
27−-- | @fmapMaybe f = catMaybes . fmap f@
28−instance FunctorMaybe [] where
29− fmapMaybe f = catMaybes . fmap f
src/Reflex/Host/Class.hsdeleted−299
@@ -1,299 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
9−{-# LANGUAGE RoleAnnotations #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−-- | This module provides the interface for hosting 'Reflex' engines. This
17−-- should only be necessary if you're writing a binding or some other library
18−-- that provides a core event loop.
19−module Reflex.Host.Class
20− ( ReflexHost (..)
21− , MonadSubscribeEvent (..)
22− , MonadReadEvent (..)
23− , MonadReflexCreateTrigger (..)
24− , MonadReflexHost (..)
25− , fireEvents
26− , newEventWithTriggerRef
27− , fireEventRef
28− , fireEventRefAndRead
29− ) where
30−
31−import Reflex.Class
32−
33−import Control.Applicative
34−import Control.Monad
35−import Control.Monad.Fix
36−import Control.Monad.Identity
37−import Control.Monad.Ref
38−import Control.Monad.Trans
39−import Control.Monad.Trans.Cont (ContT ())
40−import Control.Monad.Trans.Except (ExceptT ())
41−import Control.Monad.Trans.Reader (ReaderT ())
42−import Control.Monad.Trans.RWS (RWST ())
43−import Control.Monad.Trans.State (StateT ())
44−import qualified Control.Monad.Trans.State.Strict as Strict
45−import Control.Monad.Trans.Writer (WriterT ())
46−import Data.Dependent.Sum (DSum (..))
47−import Data.GADT.Compare
48−import Data.Monoid
49−
50−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
51−import Control.Monad.Primitive (touch)
52−import Data.IORef
53−import Reflex.Spider.Internal (EventM (..), HasSpiderTimeline, RootTrigger, SpiderEventHandle (..),
54− SpiderHost (..), SpiderHostFrame (..), Subscriber (..), newEventWithTriggerIO,
55− newFanEventWithTriggerIO, run, runFrame, scheduleClear, subscribe)
56−import qualified Reflex.Spider.Internal
57−#endif
58−
59−-- Note: this import must come last to silence warnings from AMP
60−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
61−
62−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
63−#include "../Spider/SpiderTimelineHost.include.hs"
64−#endif
65−
66−-- | Framework implementation support class for the reflex implementation
67−-- represented by @t@.
68−class ( Reflex t
69− , MonadReflexCreateTrigger t (HostFrame t)
70− , MonadSample t (HostFrame t)
71− , MonadHold t (HostFrame t)
72− , MonadFix (HostFrame t)
73− , MonadSubscribeEvent t (HostFrame t)
74− ) => ReflexHost t where
75− type EventTrigger t :: * -> *
76− type EventHandle t :: * -> *
77− type HostFrame t :: * -> *
78−
79−-- | Monad in which Events can be 'subscribed'. This forces all underlying
80−-- event sources to be initialized, so that the event will fire whenever it
81−-- ought to. Events must be subscribed before they are read using readEvent
82−class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
83− -- | Subscribe to an event and set it up if needed.
84− --
85− -- This function will create a new 'EventHandle' from an 'Event'. This handle
86− -- may then be used via 'readEvent' in the read callback of
87− -- 'fireEventsAndRead'.
88− --
89− -- If the event wasn't subscribed to before (either manually or through a
90− -- dependent event or behavior) then this function will cause the event and
91− -- all dependencies of this event to be set up. For example, if the event was
92− -- created by 'newEventWithTrigger', then it's callback will be executed.
93− --
94− -- It's safe to call this function multiple times.
95− subscribeEvent :: Event t a -> m (EventHandle t a)
96−
97−-- | Monad that allows to read events' values.
98−class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
99− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling
100− -- 'subscribeEvent').
101− --
102− -- After event propagation is done, all events can be in two states: either
103− -- they are firing with some value or they are not firing. In the former
104− -- case, this function returns @Just act@, where @act@ in an action to read
105− -- the current value of the event. In the latter case, the function returns
106− -- @Nothing@.
107− --
108− -- This function is normally used in the calllback for 'fireEventsAndRead'.
109− readEvent :: EventHandle t a -> m (Maybe (m a))
110−
111−-- | A monad where new events feed from external sources can be created.
112−class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
113− -- | Creates a root 'Event' (one that is not based on any other event).
114− --
115− -- When a subscriber first subscribes to an event (building another event that
116− -- depends on the subscription) the given callback function is run and passed
117− -- a trigger. The callback function can then set up the event source in IO.
118− -- After this is done, the callback function must return an accompanying
119− -- teardown action.
120− --
121− -- Any time between setup and teardown the trigger can be used to fire the
122− -- event, by passing it to 'fireEventsAndRead'.
123− --
124− -- Note: An event may be set up multiple times. So after the teardown action
125− -- is executed, the event may still be set up again in the future.
126− newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
127− newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
128−
129−-- | 'MonadReflexHost' designates monads that can run reflex frames.
130−class ( ReflexHost t
131− , MonadReflexCreateTrigger t m
132− , MonadSubscribeEvent t m
133− , MonadReadEvent t (ReadPhase m)
134− , MonadSample t (ReadPhase m)
135− , MonadHold t (ReadPhase m)
136− ) => MonadReflexHost t m | m -> t where
137− type ReadPhase m :: * -> *
138− -- | Propagate some events firings and read the values of events afterwards.
139− --
140− -- This function will create a new frame to fire the given events. It will
141− -- then update all dependent events and behaviors. After that is done, the
142− -- given callback is executed which allows to read the final values of events
143− -- and check whether they have fired in this frame or not.
144− --
145− -- All events that are given are fired at the same time.
146− --
147− -- This function is typically used in the main loop of a reflex framework
148− -- implementation. The main loop waits for external events to happen (such as
149− -- keyboard input or a mouse click) and then fires the corresponding events
150− -- using this function. The read callback can be used to read output events
151− -- and perform a corresponding response action to the external event.
152− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
153−
154− -- | Run a frame without any events firing.
155− --
156− -- This function should be used when you want to use 'sample' and 'hold' when
157− -- no events are currently firing. Using this function in that case can
158− -- improve performance, since the implementation can assume that no events are
159− -- firing when 'sample' or 'hold' are called.
160− --
161− -- This function is commonly used to set up the basic event network when the
162− -- application starts up.
163− runHostFrame :: HostFrame t a -> m a
164−
165−-- | Like 'fireEventsAndRead', but without reading any events.
166−fireEvents :: MonadReflexHost t m => [DSum (EventTrigger t) Identity] -> m ()
167−fireEvents dm = fireEventsAndRead dm $ return ()
168−{-# INLINE fireEvents #-}
169−
170−-- | Create a new event and store its trigger in an 'IORef' while it's active.
171−--
172−-- An event is only active between the set up (when it's first subscribed to)
173−-- and the teardown phases (when noboby is subscribing the event anymore). This
174−-- function returns an Event and an 'IORef'. As long as the event is active, the
175−-- 'IORef' will contain 'Just' the event trigger to trigger this event. When the
176−-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
177−-- sources to be more efficient, since they don't need to produce events when
178−-- nobody is listening.
179−newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
180−newEventWithTriggerRef = do
181− rt <- newRef Nothing
182− e <- newEventWithTrigger $ \t -> do
183− writeRef rt $ Just t
184− return $ writeRef rt Nothing
185− return (e, rt)
186−{-# INLINE newEventWithTriggerRef #-}
187−
188−-- | Fire the given trigger if it is not 'Nothing'.
189−fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
190−fireEventRef mtRef input = do
191− mt <- readRef mtRef
192− case mt of
193− Nothing -> return ()
194− Just trigger -> fireEvents [trigger :=> Identity input]
195−
196−-- | Fire the given trigger if it is not 'Nothing', and read from the given
197−-- 'EventHandle'.
198−fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
199−fireEventRefAndRead mtRef input e = do
200− mt <- readRef mtRef
201− case mt of
202− Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
203− Just trigger -> fireEventsAndRead [trigger :=> Identity input] $ do
204− mGetValue <- readEvent e
205− case mGetValue of
206− Nothing -> return Nothing
207− Just getValue -> fmap Just getValue
208−
209−
210−--------------------------------------------------------------------------------
211−-- Instances
212−--------------------------------------------------------------------------------
213−
214−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ReaderT r m) where
215− newEventWithTrigger = lift . newEventWithTrigger
216− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
217−
218−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ReaderT r m) where
219− subscribeEvent = lift . subscribeEvent
220−
221−instance MonadReflexHost t m => MonadReflexHost t (ReaderT r m) where
222− type ReadPhase (ReaderT r m) = ReadPhase m
223− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
224− runHostFrame = lift . runHostFrame
225−
226−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (WriterT w m) where
227− newEventWithTrigger = lift . newEventWithTrigger
228− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
229−
230−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (WriterT w m) where
231− subscribeEvent = lift . subscribeEvent
232−
233−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (WriterT w m) where
234− type ReadPhase (WriterT w m) = ReadPhase m
235− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
236− runHostFrame = lift . runHostFrame
237−
238−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StateT s m) where
239− newEventWithTrigger = lift . newEventWithTrigger
240− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
241−
242−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (StateT r m) where
243− subscribeEvent = lift . subscribeEvent
244−
245−instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
246− type ReadPhase (StateT s m) = ReadPhase m
247− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
248− runHostFrame = lift . runHostFrame
249−
250−
251−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
252− newEventWithTrigger = lift . newEventWithTrigger
253− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
254−
255−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
256− subscribeEvent = lift . subscribeEvent
257−
258−instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
259− type ReadPhase (Strict.StateT s m) = ReadPhase m
260− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
261− runHostFrame = lift . runHostFrame
262−
263−
264−
265−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
266− newEventWithTrigger = lift . newEventWithTrigger
267− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
268−
269−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ContT r m) where
270− subscribeEvent = lift . subscribeEvent
271−
272−instance MonadReflexHost t m => MonadReflexHost t (ContT r m) where
273− type ReadPhase (ContT r m) = ReadPhase m
274− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
275− runHostFrame = lift . runHostFrame
276−
277−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ExceptT e m) where
278− newEventWithTrigger = lift . newEventWithTrigger
279− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
280−
281−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ExceptT r m) where
282− subscribeEvent = lift . subscribeEvent
283−
284−instance MonadReflexHost t m => MonadReflexHost t (ExceptT e m) where
285− type ReadPhase (ExceptT e m) = ReadPhase m
286− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
287− runHostFrame = lift . runHostFrame
288−
289−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (RWST r w s m) where
290− newEventWithTrigger = lift . newEventWithTrigger
291− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
292−
293−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (RWST r w s m) where
294− subscribeEvent = lift . subscribeEvent
295−
296−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (RWST r w s m) where
297− type ReadPhase (RWST r w s m) = ReadPhase m
298− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
299− runHostFrame = lift . runHostFrame
src/Reflex/Optimizer.hsdeleted−51
@@ -1,51 +0,0 @@
1−-- | This module provides a GHC plugin designed to improve code that uses
2−-- Reflex. Currently, it just adds an INLINABLE pragma to any top-level
3−-- definition that doesn't have an explicit inlining pragma. In the future,
4−-- additional optimizations are likely to be added.
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE OverloadedStrings #-}
8−module Reflex.Optimizer
9− ( plugin
10− ) where
11−
12−#ifdef ghcjs_HOST_OS
13−import Plugins
14−#else
15−import Control.Arrow
16−import CoreMonad
17−import Data.String
18−import GhcPlugins
19−#endif
20−
21−#ifdef ghcjs_HOST_OS
22−
23−-- | The GHCJS build of Reflex.Optimizer just throws an error; instead, the version built with GHC should be used.
24−plugin :: Plugin
25−plugin = error "The GHCJS build of Reflex.Optimizer cannot be used. Instead, build with GHC and use the result with GHCJS."
26−
27−#else
28−
29−-- | The GHC plugin itself. See "GhcPlugins" for more details.
30−plugin :: Plugin
31−plugin = defaultPlugin { installCoreToDos = install }
32−
33−install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
34−install [] p = return $ makeInlinable : p
35−install options@(_:_) p = do
36− warnMsg $ "Reflex.Optimizer: ignoring " <> fromString (show $ length options) <> " command-line options"
37− install [] p
38−
39−makeInlinable :: CoreToDo
40−makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
41− let f v = setIdInfo v $ let i = idInfo v in
42− setInlinePragInfo i $ let p = inlinePragInfo i in
43− if isDefaultInlinePragma p
44− then defaultInlinePragma { inl_inline = Inlinable }
45− else p
46− newBinds = flip map (mg_binds modGuts) $ \case
47− NonRec b e -> NonRec (f b) e
48− Rec bes -> Rec $ map (first f) bes
49− return $ modGuts { mg_binds = newBinds }
50−
51−#endif
src/Reflex/Patch.hsdeleted−33
@@ -1,33 +0,0 @@
1−-- | This module defines the 'Patch' class, which is used by Reflex to manage
2−-- changes to 'Reflex.Class.Incremental' values.
3−{-# LANGUAGE TypeFamilies #-}
4−module Reflex.Patch
5− ( module Reflex.Patch
6− , module X
7− ) where
8−
9−import Reflex.Patch.Class as X
10−import Reflex.Patch.DMap as X
11−import Reflex.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, patchDMapWithMoveToPatchMapWithMoveWith, mapPatchDMapWithMove, weakenPatchDMapWithMoveWith, traversePatchDMapWithMoveWithKey, unsafePatchDMapWithMove, unPatchDMapWithMove)
12−import Reflex.Patch.Map as X
13−import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements, unsafePatchMapWithMove, unPatchMapWithMove)
14−
15−import Data.Semigroup (Semigroup (..), (<>))
16−
17−---- Patches based on commutative groups
18−
19−-- | A 'Group' is a 'Monoid' where every element has an inverse.
20−class (Semigroup q, Monoid q) => Group q where
21− negateG :: q -> q
22− (~~) :: q -> q -> q
23− r ~~ s = r <> negateG s
24−
25−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
26−class Semigroup q => Additive q where
27−
28−-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
29−newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
30−
31−instance Additive p => Patch (AdditivePatch p) where
32− type PatchTarget (AdditivePatch p) = p
33− apply (AdditivePatch p) q = Just $ p <> q
src/Reflex/Patch/Class.hsdeleted−20
@@ -1,20 +0,0 @@
1−{-# LANGUAGE TypeFamilies #-}
2−module Reflex.Patch.Class where
3−
4−import Control.Monad.Identity
5−import Data.Maybe
6−
7−-- | A 'Patch' type represents a kind of change made to a datastructure.
8−class Patch p where
9− type PatchTarget p :: *
10− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
11− -- 'Nothing'.
12− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
13−
14−applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
15−applyAlways p t = fromMaybe t $ apply p t
16−
17−instance Patch (Identity a) where
18− type PatchTarget (Identity a) = a
19− apply (Identity a) _ = Just a
20−
src/Reflex/Patch/DMap.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
2−{-# LANGUAGE LambdaCase #-}
3−{-# LANGUAGE PolyKinds #-}
4−{-# LANGUAGE Rank2Types #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE StandaloneDeriving #-}
7−{-# LANGUAGE TypeFamilies #-}
8−module Reflex.Patch.DMap where
9−
10−import Reflex.Patch.Class
11−import Reflex.Patch.Map
12−
13−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
14−import qualified Data.Dependent.Map as DMap
15−import Data.Functor.Constant
16−import Data.Functor.Misc
17−import qualified Data.Map as Map
18−import Data.Semigroup (Semigroup (..))
19−import Data.Some (Some)
20−
21−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
22−-- deleted.
23−newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
24−
25−deriving instance GCompare k => Semigroup (PatchDMap k v)
26−
27−deriving instance GCompare k => Monoid (PatchDMap k v)
28−
29−instance GCompare k => Patch (PatchDMap k v) where
30− type PatchTarget (PatchDMap k v) = DMap k v
31− 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?
32− where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
33− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
34− nothingToJust = \case
35− Nothing -> Just $ Constant ()
36− Just _ -> Nothing
37−
38−mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
39−mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
40−
41−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
42−traversePatchDMap f = traversePatchDMapWithKey $ const f
43−
44−traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
45−traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
46−
47−weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
48−weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
49−
50−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
51−patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
52−
53−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
54−const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
55− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
56− g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
57−
58−-- | Get the values that will be deleted if the given patch is applied to the
59−-- given 'DMap'. Includes values that will be replaced.
60−getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
61−getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/Map.hsdeleted−48
@@ -1,48 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE LambdaCase #-}
3−{-# LANGUAGE TypeFamilies #-}
4−module Reflex.Patch.Map where
5−
6−import Reflex.Patch.Class
7−
8−import Data.Semigroup
9−import Data.Map (Map)
10−import qualified Data.Map as Map
11−import Data.Maybe
12−
13−-- | A set of changes to a 'Map'. Any element may be inserted/updated or
14−-- deleted.
15−newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
16−
17−instance Ord k => Patch (PatchMap k v) where
18− type PatchTarget (PatchMap k v) = Map k v
19− 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?
20− where insertions = Map.mapMaybeWithKey (const id) p
21− deletions = Map.mapMaybeWithKey (const nothingToJust) p
22− nothingToJust = \case
23− Nothing -> Just ()
24− Just _ -> Nothing
25−
26−instance Ord k => Semigroup (PatchMap k v) where
27− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
28− -- PatchMap is idempotent, so stimes n is id for every n
29−#if MIN_VERSION_semigroups(0,17,0)
30− stimes = stimesIdempotentMonoid
31−#else
32− times1p n x = case compare n 0 of
33− LT -> error "stimesIdempotentMonoid: negative multiplier"
34− EQ -> mempty
35− GT -> x
36−#endif
37−
38−instance Ord k => Monoid (PatchMap k v) where
39− mempty = PatchMap mempty
40− mappend = (<>)
41−
42−instance Functor (PatchMap k) where
43− fmap f = PatchMap . fmap (fmap f) . unPatchMap
44−
45−-- | Returns all the new elements that will be added to the 'Map'
46−patchMapNewElements :: PatchMap k v -> [v]
47−patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
48−
src/Reflex/Patch/MapWithMove.hsdeleted−156
@@ -1,156 +0,0 @@
1−{-# LANGUAGE DeriveFoldable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveTraversable #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−module Reflex.Patch.MapWithMove where
9−
10−import Reflex.Patch.Class
11−
12−import Control.Arrow
13−import Control.Monad.State
14−import Data.Foldable
15−import Data.Function
16−import Data.List
17−import Data.Map (Map)
18−import qualified Data.Map as Map
19−import Data.Maybe
20−import qualified Data.Set as Set
21−import Data.Tuple
22−
23−-- | Patch a DMap with additions, deletions, and moves.
24−-- Invariants:
25−-- * The same source key may not be moved to two destination keys.
26−-- * Any key that is the source of a Move must also be edited
27−-- (otherwise, the Move would duplicate it)
28−newtype PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
29−
30−data NodeInfo k v = NodeInfo
31− { _nodeInfo_from :: !(From k v)
32− , _nodeInfo_to :: !(To k)
33− }
34− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
35−
36−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
37−unPatchMapWithMove (PatchMapWithMove p) = p
38−
39−-- | Warning: when using this function, you must ensure that the invariants of
40−-- 'PatchMapWithMove' are preserved; they will not be checked.
41−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
42−unsafePatchMapWithMove = PatchMapWithMove
43−
44−data From k v
45− = From_Insert v -- ^ Insert the given value here
46− | From_Delete -- ^ Delete the existing value, if any, from here
47− | From_Move !k -- ^ Move the value here from the given key
48− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
49−
50−type To = Maybe
51−
52−instance Ord k => Patch (PatchMapWithMove k v) where
53− type PatchTarget (PatchMapWithMove k v) = Map k v
54− apply (PatchMapWithMove 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?
55− where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
56− From_Insert v -> Just v
57− From_Move k -> Map.lookup k old
58− From_Delete -> Nothing
59− deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
60− From_Delete -> Just ()
61− _ -> Nothing
62−
63−-- | Returns all the new elements that will be added to the 'Map'
64−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
65−patchMapWithMoveNewElements (PatchMapWithMove p) = catMaybes $ f <$> Map.elems p
66− where f ni = case _nodeInfo_from ni of
67− From_Insert v -> Just v
68− From_Move _ -> Nothing
69− From_Delete -> Nothing
70−
71−validPatchMapWithMove :: Ord k => PatchMapWithMove k v -> Bool
72−validPatchMapWithMove (PatchMapWithMove p) =
73− let f ni = case _nodeInfo_from ni of
74− From_Move k -> Just k
75− _ -> Nothing
76− moveSources = catMaybes $ f <$> Map.elems p
77− in and
78− [ -- All Moves must have unique source keys - a single source key can't be moved to two destinations
79− all (==1) $ Map.fromListWith (+) $ fmap (\k -> (k, 1 :: Integer )) moveSources
80− , -- All Move source keys must also be patched - since the value's being moved elsewhere, the original must either be Deleted or replaced (with Insert or Move)
81− all (isJust . flip Map.lookup p) moveSources
82− ]
83−
84−knownValidPatchMapWithMove :: [PatchMapWithMove Int Int]
85−knownValidPatchMapWithMove =
86− [ PatchMapWithMove mempty
87− , PatchMapWithMove $ Map.fromList
88− [ (1, NodeInfo (From_Move 2) $ Just 1)
89− , (2, NodeInfo (From_Move 1) $ Just 2)
90− ]
91− ]
92−
93−knownInvalidPatchMapWithMove :: [PatchMapWithMove Int Int]
94−knownInvalidPatchMapWithMove =
95− [ -- Invalid because we move 2 to 1, but don't specify what to do with 2
96− PatchMapWithMove $ Map.singleton 1 $ NodeInfo (From_Move 2) Nothing
97− , -- Invalid because we have multiple moves from 3
98− PatchMapWithMove $ Map.fromList
99− [ (1, NodeInfo (From_Move 3) Nothing)
100− , (2, NodeInfo (From_Move 3) Nothing)
101− , (3, NodeInfo From_Delete (Just 1))
102− ]
103− ]
104−
105−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
106−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
107− where unsorted = Map.toList m
108− sorted = sortBy (cmp `on` snd) unsorted
109− f (to, _) (from, _) = if to == from then Nothing else
110− Just (from, to)
111− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
112− g (to, _) (from, _) = if to == from then Nothing else
113− let Just movingTo = Map.lookup from reverseMapping
114− in Just (to, NodeInfo (From_Move from) $ Just movingTo)
115−
116−patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
117−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patch
118− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
119− newList = Map.toList newByIndexUnsorted
120− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
121− insertsAndMoves :: Map k (NodeInfo k v)
122− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
123− let f k v = do
124− remainingValues <- get
125− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
126− then Map.delete v remainingValues
127− else Map.insert v remainingKeys remainingValues
128− case Map.lookup v remainingValues of
129− Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
130− Just fromKs ->
131− if k `Set.member` fromKs
132− then do
133− putRemainingKeys $ Set.delete k fromKs
134− return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
135− else do
136− Just (fromK, remainingKeys) <- return $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
137− putRemainingKeys remainingKeys
138− return $ NodeInfo (From_Move fromK) $ Just undefined
139− Map.traverseWithKey f newByIndex
140− unusedOldKeys = fold unusedValuesByValue
141− pointlessMove k = \case
142− From_Move k' | k == k' -> True
143− _ -> False
144− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
145− then Just undefined
146− else Nothing
147− patch = unsafePatchMapWithMove $ Map.filterWithKey (\k -> not . pointlessMove k . _nodeInfo_from) $ Map.mergeWithKey (\k a _ -> Just $ nodeInfoSetTo (keyWasMoved k) a) (Map.mapWithKey $ \k -> nodeInfoSetTo $ keyWasMoved k) (Map.mapWithKey $ \k _ -> NodeInfo From_Delete $ keyWasMoved k) insertsAndMoves oldByIndex
148−
149−nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
150−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
151−
152−nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
153−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
154−
155−nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
156−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
src/Reflex/PerformEvent/Base.hsdeleted−157
@@ -1,157 +0,0 @@
1−-- | This module provides 'PerformEventT', the standard implementation of
2−-- 'PerformEvent'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE PartialTypeSignatures #-}
11−{-# LANGUAGE PolyKinds #-}
12−{-# LANGUAGE RankNTypes #-}
13−{-# LANGUAGE ScopedTypeVariables #-}
14−{-# LANGUAGE StandaloneDeriving #-}
15−{-# LANGUAGE TypeFamilies #-}
16−{-# LANGUAGE TypeOperators #-}
17−{-# LANGUAGE UndecidableInstances #-}
18−#ifdef USE_REFLEX_OPTIMIZER
19−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
20−#endif
21−module Reflex.PerformEvent.Base
22− ( PerformEventT (..)
23− , FireCommand (..)
24− , hostPerformEventT
25− ) where
26−
27−import Reflex.Class
28−import Reflex.Host.Class
29−import Reflex.PerformEvent.Class
30−import Reflex.Requester.Base
31−import Reflex.Requester.Class
32−
33−import Control.Lens
34−import Control.Monad.Exception
35−import Control.Monad.Identity
36−import Control.Monad.Primitive
37−import Control.Monad.Reader
38−import Control.Monad.Ref
39−import Data.Coerce
40−import Data.Dependent.Map (DMap)
41−import qualified Data.Dependent.Map as DMap
42−import Data.Dependent.Sum
43−
44−-- | A function that fires events for the given 'EventTrigger's and then runs
45−-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
46−-- action will be run once for the initial trigger execution as well as once for
47−-- each followup.
48−newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
49−
50−-- | Provides a basic implementation of 'PerformEvent'. Note that, despite the
51−-- name, 'PerformEventT' is not an instance of 'MonadTrans'.
52−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequesterT t (HostFrame t) Identity (HostFrame t) a }
53−
54−deriving instance ReflexHost t => Functor (PerformEventT t m)
55−deriving instance ReflexHost t => Applicative (PerformEventT t m)
56−deriving instance ReflexHost t => Monad (PerformEventT t m)
57−deriving instance ReflexHost t => MonadFix (PerformEventT t m)
58−deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
59−deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
60−
61−instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
62− type PrimState (PerformEventT t m) = PrimState (HostFrame t)
63− primitive = PerformEventT . lift . primitive
64−
65−instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
66− type Performable (PerformEventT t m) = HostFrame t
67− {-# INLINABLE performEvent_ #-}
68− performEvent_ = PerformEventT . requesting_
69− {-# INLINABLE performEvent #-}
70− performEvent = PerformEventT . requestingIdentity
71−
72−instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
73− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
74− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
75− f a0 a' = do
76− result0 <- lift a0
77− result' <- requestingIdentity a'
78− return (result0, result')
79− traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ sequenceDMapWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElements mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
80− traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ sequenceDMapWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElements mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
81−
82−defaultAdjustBase :: forall t v v2 k' p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
83− => ((forall a. k' a -> v a -> HostFrame t (v2 a)) -> p k' v -> HostFrame t (p k' v2))
84− -> (forall a. k' a -> v a -> HostFrame t (v2 a))
85− -> DMap k' v
86− -> Event t (p k' v)
87− -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' v2, Event t (p k' v2))
88−defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
89− result0 <- lift $ DMap.traverseWithKey f' dm0
90− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
91− return (result0, result')
92−
93−instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
94− {-# INLINABLE newEventWithTrigger #-}
95− newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
96− {-# INLINABLE newFanEventWithTrigger #-}
97− newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
98−
99−-- | Run a 'PerformEventT' action, returning a 'FireCommand' that allows the
100−-- caller to trigger 'Event's while ensuring that 'performEvent' actions are run
101−-- at the appropriate time.
102−{-# INLINABLE hostPerformEventT #-}
103−hostPerformEventT :: forall t m a.
104− ( Monad m
105− , MonadSubscribeEvent t m
106− , MonadReflexHost t m
107− , MonadRef m
108− , Ref m ~ Ref IO
109− )
110− => PerformEventT t m a
111− -> m (a, FireCommand t m)
112−hostPerformEventT a = do
113− (response, responseTrigger) <- newEventWithTriggerRef
114− (result, eventToPerform) <- runHostFrame $ runRequesterT (unPerformEventT a) response
115− eventToPerformHandle <- subscribeEvent eventToPerform
116− return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
117− let go :: [DSum (EventTrigger t) Identity] -> m [a']
118− go ts = do
119− (result', mToPerform) <- fireEventsAndRead ts $ do
120− mToPerform <- sequence =<< readEvent eventToPerformHandle
121− result' <- readPhase
122− return (result', mToPerform)
123− case mToPerform of
124− Nothing -> return [result']
125− Just toPerform -> do
126− responses <- runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
127− mrt <- readRef responseTrigger
128− let followupEventTriggers = case mrt of
129− Just rt -> [rt :=> Identity responses]
130− Nothing -> []
131− (result':) <$> go followupEventTriggers
132− go triggers
133−
134−instance ReflexHost t => MonadSample t (PerformEventT t m) where
135− {-# INLINABLE sample #-}
136− sample = PerformEventT . lift . sample
137−
138−instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
139− {-# INLINABLE hold #-}
140− hold v0 v' = PerformEventT $ lift $ hold v0 v'
141− {-# INLINABLE holdDyn #-}
142− holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
143− {-# INLINABLE holdIncremental #-}
144− holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
145−
146−instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
147− type Ref (PerformEventT t m) = Ref (HostFrame t)
148− {-# INLINABLE newRef #-}
149− newRef = PerformEventT . lift . newRef
150− {-# INLINABLE readRef #-}
151− readRef = PerformEventT . lift . readRef
152− {-# INLINABLE writeRef #-}
153− writeRef r = PerformEventT . lift . writeRef r
154−
155−instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
156− {-# INLINABLE atomicModifyRef #-}
157− atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
src/Reflex/PerformEvent/Class.hsdeleted−64
@@ -1,64 +0,0 @@
1−-- | This module defines 'PerformEvent' and 'TriggerEvent', which mediate the
2−-- interaction between a "Reflex"-based program and the external side-effecting
3−-- actions such as 'IO'.
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.PerformEvent.Class
18− ( PerformEvent (..)
19− , performEventAsync
20− ) where
21−
22−import Reflex.Class
23−import Reflex.TriggerEvent.Class
24−
25−import Control.Monad.Reader
26−
27−-- | 'PerformEvent' represents actions that can trigger other actions based on
28−-- 'Event's.
29−class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
30− -- | The type of action to be triggered; this is often not the same type as
31− -- the triggering action.
32− type Performable m :: * -> *
33− -- | Perform the action contained in the given 'Event' whenever the 'Event'
34− -- fires. Return the result in another 'Event'. Note that the output 'Event'
35− -- will generally occur later than the input 'Event', since most 'Performable'
36− -- actions cannot be performed during 'Event' propagation.
37− performEvent :: Event t (Performable m a) -> m (Event t a)
38− -- | Like 'performEvent', but do not return the result. May have slightly
39− -- better performance.
40− performEvent_ :: Event t (Performable m ()) -> m ()
41−
42−-- | Like 'performEvent', but the resulting 'Event' occurs only when the
43−-- callback (@a -> IO ()@) is called, not when the included action finishes.
44−--
45−-- NOTE: Despite the name, 'performEventAsync' does not run its action in a
46−-- separate thread - although the action is free to invoke forkIO and then call
47−-- the callback whenever it is ready. This will work properly, even in GHCJS
48−-- (which fully implements concurrency even though JavaScript does not have
49−-- built in concurrency).
50−{-# INLINABLE performEventAsync #-}
51−performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
52−performEventAsync e = do
53− (eOut, triggerEOut) <- newTriggerEvent
54− performEvent_ $ fmap ($ triggerEOut) e
55− return eOut
56−
57−instance PerformEvent t m => PerformEvent t (ReaderT r m) where
58− type Performable (ReaderT r m) = ReaderT r (Performable m)
59− performEvent_ e = do
60− r <- ask
61− lift $ performEvent_ $ flip runReaderT r <$> e
62− performEvent e = do
63− r <- ask
64− lift $ performEvent $ flip runReaderT r <$> e
src/Reflex/PostBuild/Base.hsdeleted−142
@@ -1,142 +0,0 @@
1−-- | This module defines 'PostBuildT', the standard implementation of
2−-- 'PostBuild'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DataKinds #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
9−{-# LANGUAGE RecursiveDo #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.PostBuild.Base
18− ( PostBuildT (..)
19− , runPostBuildT
20− ) where
21−
22−import Reflex.Class
23−import Reflex.Host.Class
24−import Reflex.PerformEvent.Class
25−import Reflex.PostBuild.Class
26−import Reflex.TriggerEvent.Class
27−
28−import Control.Monad.Exception
29−import Control.Monad.Identity
30−import Control.Monad.Primitive
31−import Control.Monad.Reader
32−import Control.Monad.Ref
33−import Control.Monad.Trans.Control
34−import Data.Dependent.Map (DMap)
35−import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Compose
37−
38−-- | Provides a basic implementation of 'PostBuild'.
39−newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
40−
41−-- | Run a 'PostBuildT' action. An 'Event' should be provided that fires
42−-- immediately after the action is finished running; no other 'Event's should
43−-- fire first.
44−{-# INLINABLE runPostBuildT #-}
45−runPostBuildT :: PostBuildT t m a -> Event t () -> m a
46−runPostBuildT (PostBuildT a) = runReaderT a
47−
48−instance PrimMonad m => PrimMonad (PostBuildT x m) where
49− type PrimState (PostBuildT x m) = PrimState m
50− primitive = lift . primitive
51−
52−instance MonadTransControl (PostBuildT t) where
53− type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
54− {-# INLINABLE liftWith #-}
55− liftWith = defaultLiftWith PostBuildT unPostBuildT
56− {-# INLINABLE restoreT #-}
57− restoreT = defaultRestoreT PostBuildT
58−
59−instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
60− {-# INLINABLE getPostBuild #-}
61− getPostBuild = PostBuildT ask
62−
63−instance MonadSample t m => MonadSample t (PostBuildT t m) where
64− {-# INLINABLE sample #-}
65− sample = lift . sample
66−
67−instance MonadHold t m => MonadHold t (PostBuildT t m) where
68− {-# INLINABLE hold #-}
69− hold v0 = lift . hold v0
70− {-# INLINABLE holdDyn #-}
71− holdDyn v0 = lift . holdDyn v0
72− {-# INLINABLE holdIncremental #-}
73− holdIncremental v0 = lift . holdIncremental v0
74−
75−instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
76− type Performable (PostBuildT t m) = PostBuildT t (Performable m)
77− {-# INLINABLE performEvent_ #-}
78− performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
79− {-# INLINABLE performEvent #-}
80− performEvent e = liftWith $ \run -> performEvent $ fmap run e
81−
82−instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
83− {-# INLINABLE newEventWithTrigger #-}
84− newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
85− {-# INLINABLE newFanEventWithTrigger #-}
86− newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
87−
88−instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
89− {-# INLINABLE newTriggerEvent #-}
90− newTriggerEvent = lift newTriggerEvent
91− {-# INLINABLE newTriggerEventWithOnComplete #-}
92− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
93− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
94−
95−instance MonadRef m => MonadRef (PostBuildT t m) where
96− type Ref (PostBuildT t m) = Ref m
97− {-# INLINABLE newRef #-}
98− newRef = lift . newRef
99− {-# INLINABLE readRef #-}
100− readRef = lift . readRef
101− {-# INLINABLE writeRef #-}
102− writeRef r = lift . writeRef r
103−
104−instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
105− {-# INLINABLE atomicModifyRef #-}
106− atomicModifyRef r = lift . atomicModifyRef r
107−
108−instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m) => MonadAdjust t (PostBuildT t m) where
109− runWithReplace a0 a' = do
110− postBuild <- getPostBuild
111− lift $ do
112− rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
113− let voidResult' = fmapCheap (\_ -> ()) result'
114− return result
115− traverseDMapWithKeyWithAdjust = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap
116− traverseDMapWithKeyWithAdjustWithMove = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove
117−
118−mapDMapWithAdjustImpl :: forall t m k v v' p. (Reflex t, MonadFix m, MonadHold t m)
119− => ( (forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a))
120− -> DMap k (Compose ((,) (Bool, Event t ())) v)
121− -> Event t (p k (Compose ((,) (Bool, Event t ())) v))
122− -> m (DMap k v', Event t (p k v'))
123− )
124− -> ((forall a. v a -> Compose ((,) (Bool, Event t ())) v a) -> p k v -> p k (Compose ((,) (Bool, Event t ())) v))
125− -> (forall a. k a -> v a -> PostBuildT t m (v' a))
126− -> DMap k v
127− -> Event t (p k v)
128− -> PostBuildT t m (DMap k v', Event t (p k v'))
129−mapDMapWithAdjustImpl base mapPatch f dm0 dm' = do
130− postBuild <- getPostBuild
131− let loweredDm0 = DMap.map (Compose . (,) (False, postBuild)) dm0
132− f' :: forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a)
133− f' k (Compose ((shouldHeadE, e), v)) = do
134− eOnce <- if shouldHeadE
135− then headE e --TODO: Avoid doing this headE so many times; once per loweredDm' firing ought to be OK, but it's not totally trivial to do because result' might be firing at the same time, and we don't want *that* to be the postBuild occurrence
136− else return e
137− runPostBuildT (f k v) eOnce
138− lift $ do
139− rec (result0, result') <- base f' loweredDm0 loweredDm'
140− let voidResult' = fmapCheap (\_ -> ()) result'
141− let loweredDm' = ffor dm' $ mapPatch (Compose . (,) (True, voidResult'))
142− return (result0, result')
src/Reflex/PostBuild/Class.hsdeleted−34
@@ -1,34 +0,0 @@
1−-- | This module defines 'PostBuild', which indicates that an action will be
2−-- notified when it, and any action it's a part of, has finished executing.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE DataKinds #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE FunctionalDependencies #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE TypeOperators #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−#ifdef USE_REFLEX_OPTIMIZER
13−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14−#endif
15−module Reflex.PostBuild.Class
16− ( PostBuild (..)
17− ) where
18−
19−import Reflex.Class
20−
21−import Control.Monad.Reader
22−
23−-- | 'PostBuild' represents an action that is notified via an 'Event' when it
24−-- has finished executing. Note that the specific definition of "finished" is
25−-- determined by the instance of 'PostBuild', but the intent is to allow
26−-- 'Behavior's and 'Dynamic's to be safely sampled, regardless of where they
27−-- were created, when the post-build 'Event' fires. The post-build 'Event' will
28−-- fire exactly once for an given action.
29−class (Reflex t, Monad m) => PostBuild t m | m -> t where
30− -- | Retrieve the post-build 'Event' for this action.
31− getPostBuild :: m (Event t ())
32−
33−instance PostBuild t m => PostBuild t (ReaderT r m) where
34− getPostBuild = lift getPostBuild
src/Reflex/Pure.hsdeleted−189
@@ -1,189 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE EmptyDataDecls #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE InstanceSigs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−#ifdef USE_REFLEX_OPTIMIZER
9−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
10−#endif
11−
12−-- There are two expected orphan instances in this module:
13−-- * MonadSample (Pure t) ((->) t)
14−-- * MonadHold (Pure t) ((->) t)
15−{-# OPTIONS_GHC -fno-warn-orphans #-}
16−
17−-- | This module provides a pure implementation of Reflex, which is intended to
18−-- serve as a reference for the semantics of the Reflex class. All
19−-- implementations of Reflex should produce the same results as this
20−-- implementation, although performance and laziness/strictness may differ.
21−module Reflex.Pure
22− ( Pure
23− , Behavior (..)
24− , Event (..)
25− , Dynamic (..)
26− , Incremental (..)
27− ) where
28−
29−#if !MIN_VERSION_base(4,8,0)
30−import Control.Applicative
31−#endif
32−
33−import Control.Monad
34−import Data.Dependent.Map (DMap, GCompare)
35−import qualified Data.Dependent.Map as DMap
36−import Data.Functor.Identity
37−import Data.Maybe
38−import Data.MemoTrie
39−import Data.Monoid
40−import Data.Type.Coercion
41−import Reflex.Class
42−
43−-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
44−-- with the type @t@.
45−data Pure t
46−
47−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
48−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
49−-- exclusively to memoize functions of t, not for any of its other capabilities.
50−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
51−
52− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
53− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
54− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
55− newtype Incremental (Pure t) p = Incremental { unIncremental :: t -> (PatchTarget p, Maybe p) }
56−
57− type PushM (Pure t) = (->) t
58− type PullM (Pure t) = (->) t
59−
60− never :: Event (Pure t) a
61− never = Event $ \_ -> Nothing
62−
63− constant :: a -> Behavior (Pure t) a
64− constant x = Behavior $ \_ -> x
65−
66− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
67− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
68−
69− pushCheap :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
70− pushCheap = push
71−
72− pull :: PullM (Pure t) a -> Behavior (Pure t) a
73− pull = Behavior . memo
74−
75− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
76− -- warning because the GCompare instance is not used; however, removing the
77− -- GCompare instance produces a different warning, due to that constraint
78− -- being present in the original class definition
79−
80− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
81− merge events = Event $ memo $ \t ->
82− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
83− in if DMap.null currentOccurrences
84− then Nothing
85− else Just currentOccurrences
86−
87− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
88− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
89−
90− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
91− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
92−
93− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
94− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
95−
96− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
97− current d = Behavior $ \t -> fst $ unDynamic d t
98−
99− updated :: Dynamic (Pure t) a -> Event (Pure t) a
100− updated d = Event $ \t -> snd $ unDynamic d t
101−
102− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
103− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
104−
105− -- See UNUSED_CONSTRAINT, above.
106−
107− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
108− --a) -> Incremental (Pure t) p a
109− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
110−
111− mergeIncremental = mergeIncrementalImpl
112− mergeIncrementalWithMove = mergeIncrementalImpl
113−
114− currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
115−
116− updatedIncremental i = Event $ \t -> snd $ unIncremental i t
117−
118− incrementalToDynamic i = Dynamic $ \t ->
119− let (old, mPatch) = unIncremental i t
120− e = case mPatch of
121− Nothing -> Nothing
122− Just patch -> apply patch old
123− in (old, e)
124− behaviorCoercion Coercion = Coercion
125− eventCoercion Coercion = Coercion
126− dynamicCoercion Coercion = Coercion
127−
128−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
129−mergeIncrementalImpl i = Event $ \t ->
130− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
131− in if DMap.null results
132− then Nothing
133− else Just results
134−
135−instance Functor (Dynamic (Pure t)) where
136− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
137− in (f cur, fmap f upd)
138−
139−instance Applicative (Dynamic (Pure t)) where
140− pure a = Dynamic $ \_ -> (a, Nothing)
141− (<*>) = ap
142−
143−instance Monad (Dynamic (Pure t)) where
144− return = pure
145− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
146− let (curX :: a, updX :: Maybe a) = unDynamic x t
147− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
148− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
149− Nothing -> (Nothing, Nothing)
150− Just nextX -> let (c, u) = unDynamic (f nextX) t
151− in (Just c, u)
152− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
153−
154−instance MonadSample (Pure t) ((->) t) where
155−
156− sample :: Behavior (Pure t) a -> (t -> a)
157− sample = unBehavior
158−
159−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
160−
161− hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
162− hold initialValue e initialTime = Behavior f
163− where f = memo $ \sampleTime ->
164− -- Really, the sampleTime should never be prior to the initialTime,
165− -- because that would mean the Behavior is being sampled before
166− -- being created.
167− if sampleTime <= initialTime
168− then initialValue
169− else let lastTime = pred sampleTime
170− in fromMaybe (f lastTime) $ unEvent e lastTime
171−
172− holdDyn :: a -> Event (Pure t) a -> t -> Dynamic (Pure t) a
173− holdDyn initialValue e initialTime =
174− let Behavior f = hold initialValue e initialTime
175− in Dynamic $ \t -> (f t, unEvent e t)
176−
177− holdIncremental :: Patch p => PatchTarget p -> Event (Pure t) p -> t -> Incremental (Pure t) p
178− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
179− where f = memo $ \sampleTime ->
180− -- Really, the sampleTime should never be prior to the initialTime,
181− -- because that would mean the Behavior is being sampled before
182− -- being created.
183− if sampleTime <= initialTime
184− then initialValue
185− else let lastTime = pred sampleTime
186− lastValue = f lastTime
187− in case unEvent e lastTime of
188− Nothing -> lastValue
189− Just x -> fromMaybe lastValue $ apply x lastValue
src/Reflex/Query/Base.hsdeleted−255
@@ -1,255 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−module Reflex.Query.Base
11− ( QueryT (..)
12− , runQueryT
13− , mapQuery
14− , mapQueryResult
15− , dynWithQueryT
16− , withQueryT
17− ) where
18−
19−import Control.Monad.Exception
20−import Control.Monad.Fix
21−import Control.Monad.Reader
22−import Control.Monad.Ref
23−import Control.Monad.State.Strict
24−import Data.Align
25−import Data.Dependent.Map (DMap, DSum (..))
26−import qualified Data.Dependent.Map as DMap
27−import Data.Foldable
28−import Data.Functor.Compose
29−import Data.Functor.Misc
30−import Data.Map (Map)
31−import qualified Data.Map as Map
32−import Data.Monoid
33−import Data.Some (Some)
34−import qualified Data.Some as Some
35−import Data.These
36−
37−import Reflex.Class
38−import Reflex.EventWriter
39−import Reflex.Host.Class
40−import Reflex.PerformEvent.Class
41−import Reflex.PostBuild.Class
42−import Reflex.Query.Class
43−import Reflex.TriggerEvent.Class
44−import qualified Reflex.Patch.MapWithMove as MapWithMove
45−
46−newtype QueryT t q m a = QueryT { unQueryT :: StateT [Behavior t q] (EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m)) a }
47− deriving (Functor, Applicative, Monad, MonadException, MonadFix, MonadIO, MonadHold t, MonadSample t, MonadAtomicRef)
48−
49−runQueryT :: (MonadFix m, Additive q, Group q, Reflex t) => QueryT t q m a -> Dynamic t (QueryResult q) -> m (a, Incremental t (AdditivePatch q))
50−runQueryT (QueryT a) qr = do
51− ((r, bs), es) <- runReaderT (runEventWriterT (runStateT a mempty)) qr
52− return (r, unsafeBuildIncremental (foldlM (\b c -> (b <>) <$> sample c) mempty bs) (fmapCheap AdditivePatch es))
53−
54−newtype QueryTLoweredResult t q v = QueryTLoweredResult (v, [Behavior t q])
55−
56−getQueryTLoweredResultValue :: QueryTLoweredResult t q v -> v
57−getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
58−
59−getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
60−getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
61−
62−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, MonadAdjust t m) => MonadAdjust t (QueryT t q m) where
63− runWithReplace (QueryT a0) a' = do
64− ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
65− tellQueryIncremental $
66− let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
67− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
68− bs' = fmapCheap snd $ r'
69− patches = unsafeBuildIncremental (sampleBs bs0) $
70− flip pushCheap bs' $ \bs -> do
71− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
72− return (Just (AdditivePatch p))
73− in patches
74− return (r0, fmapCheap fst r')
75− traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
76− traverseDMapWithKeyWithAdjust f dm0 dm' = do
77− let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
78− f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
79− (result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjust f' dm0 dm'
80− let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
81− liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
82− mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
83− liftedBs0 :: Map (Some k) [Behavior t q]
84− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
85− liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
86− liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
87− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
88− sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
89− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
90− accumBehaviors :: forall m'. MonadHold t m'
91− => Map (Some k) [Behavior t q]
92− -> PatchMap (Some k) [Behavior t q]
93− -> m' ( Maybe (Map (Some k) [Behavior t q])
94− , Maybe (AdditivePatch q))
95− -- f accumulates the child behavior state we receive from running traverseDMapWithKeyWithAdjust for the underlying monad.
96− -- When an update occurs, it also computes a patch to communicate to the parent QueryT state.
97− -- bs0 is a Map denoting the behaviors of the current children.
98− -- pbs is a PatchMap denoting an update to the behaviors of the current children
99− accumBehaviors bs0 pbs@(PatchMap bs') = do
100− let p k bs = case Map.lookup k bs0 of
101− Nothing -> case bs of
102− -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
103− Nothing -> return mempty
104− -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
105− Just newBs -> sampleBs newBs
106− Just oldBs -> case bs of
107− -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
108− Nothing -> negateG <$> sampleBs oldBs
109− -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
110− -- composed with the sampling the child's new state.
111− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
112− -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
113− -- child patches and wrap them in AdditivePatch.
114− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
115− return (apply pbs bs0, Just patch)
116− (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
117− tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
118− return (liftedResult0, liftedResult')
119− traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
120− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
121− let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
122− f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
123− (result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjustWithMove f' dm0 dm'
124− let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
125− liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
126− liftedBs0 :: Map (Some k) [Behavior t q]
127− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
128− liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
129− liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
130− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
131− sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
132− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
133− accumBehaviors' :: forall m'. MonadHold t m'
134− => Map (Some k) [Behavior t q]
135− -> PatchMapWithMove (Some k) [Behavior t q]
136− -> m' ( Maybe (Map (Some k) [Behavior t q])
137− , Maybe (AdditivePatch q))
138− -- f accumulates the child behavior state we receive from running traverseDMapWithKeyWithAdjustWithMove for the underlying monad.
139− -- When an update occurs, it also computes a patch to communicate to the parent QueryT state.
140− -- bs0 is a Map denoting the behaviors of the current children.
141− -- pbs is a PatchMapWithMove denoting an update to the behaviors of the current children
142− accumBehaviors' bs0 pbs = do
143− let bs' = unPatchMapWithMove pbs
144− p k bs = case Map.lookup k bs0 of
145− Nothing -> case MapWithMove._nodeInfo_from bs of
146− -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
147− MapWithMove.From_Delete -> return mempty
148− -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
149− MapWithMove.From_Insert newBs -> sampleBs newBs
150− MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
151− Nothing -> return mempty
152− Just newBs -> sampleBs newBs
153− Just oldBs -> case MapWithMove._nodeInfo_from bs of
154− -- If the update is to delete the state for a child that already exists, the patch is the negation of the child's current state
155− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
156− -- If the update is to update the state for a child that already exists, the patch is the negation of sampling the child's current state
157− -- composed with the sampling the child's new state.
158− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
159− MapWithMove.From_Move k'
160− | k' == k -> return mempty
161− | otherwise -> case Map.lookup k' bs0 of
162− -- If we are moving from a non-existent key, that is a delete
163− Nothing -> negateG <$> sampleBs oldBs
164− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
165− -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
166− -- child patches and wrap them in AdditivePatch.
167− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
168− return (apply pbs bs0, Just patch)
169− (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
170− tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
171− return (liftedResult0, liftedResult')
172−
173−instance MonadTrans (QueryT t q) where
174− lift = QueryT . lift . lift . lift
175−
176−instance PostBuild t m => PostBuild t (QueryT t q m) where
177− getPostBuild = lift getPostBuild
178−
179−instance (MonadAsyncException m) => MonadAsyncException (QueryT t q m) where
180− mask f = QueryT $ mask $ \unMask -> unQueryT $ f $ QueryT . unMask . unQueryT
181−
182−instance TriggerEvent t m => TriggerEvent t (QueryT t q m) where
183− newTriggerEvent = lift newTriggerEvent
184− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
185− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
186−
187−instance PerformEvent t m => PerformEvent t (QueryT t q m) where
188− type Performable (QueryT t q m) = Performable m
189− performEvent_ = lift . performEvent_
190− performEvent = lift . performEvent
191−
192−instance MonadRef m => MonadRef (QueryT t q m) where
193− type Ref (QueryT t q m) = Ref m
194− newRef = QueryT . newRef
195− readRef = QueryT . readRef
196− writeRef r = QueryT . writeRef r
197−
198−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (QueryT t q m) where
199− newEventWithTrigger = QueryT . newEventWithTrigger
200− newFanEventWithTrigger a = QueryT . lift $ newFanEventWithTrigger a
201−
202−mapQuery :: QueryMorphism q q' -> q -> q'
203−mapQuery = _queryMorphism_mapQuery
204−
205−mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
206−mapQueryResult = _queryMorphism_mapQueryResult
207−
208−-- | withQueryT's QueryMorphism argument needs to be a group homomorphism in order to behave correctly
209−withQueryT :: (MonadFix m, PostBuild t m, Group q, Group q', Additive q, Additive q', Query q')
210− => QueryMorphism q q'
211− -> QueryT t q m a
212− -> QueryT t q' m a
213−withQueryT f a = do
214− r' <- askQueryResult
215− (result, q) <- lift $ runQueryT a $ mapQueryResult f <$> r'
216− tellQueryIncremental $ unsafeBuildIncremental
217− (fmap (mapQuery f) (sample (currentIncremental q)))
218− (fmapCheap (AdditivePatch . mapQuery f . unAdditivePatch) $ updatedIncremental q)
219− return result
220−
221−-- | dynWithQueryT's (Dynamic t QueryMorphism) argument needs to be a group homomorphism at all times in order to behave correctly
222−dynWithQueryT :: (MonadFix m, PostBuild t m, Group q, Additive q, Group q', Additive q', Query q')
223− => Dynamic t (QueryMorphism q q')
224− -> QueryT t q m a
225− -> QueryT t q' m a
226−dynWithQueryT f q = do
227− r' <- askQueryResult
228− (result, q') <- lift $ runQueryT q $ zipDynWith mapQueryResult f r'
229− tellQueryIncremental $ zipDynIncrementalWith mapQuery f q'
230− return result
231− where zipDynIncrementalWith g da ib =
232− let eab = align (updated da) (updatedIncremental ib)
233− ec = flip push eab $ \o -> case o of
234− This a -> do
235− aOld <- sample $ current da
236− b <- sample $ currentIncremental ib
237− return $ Just $ AdditivePatch (g a b ~~ g aOld b)
238− That (AdditivePatch b) -> do
239− a <- sample $ current da
240− return $ Just $ AdditivePatch $ g a b
241− These a (AdditivePatch b) -> do
242− aOld <- sample $ current da
243− bOld <- sample $ currentIncremental ib
244− return $ Just $ AdditivePatch $ mconcat [ g a bOld, negateG (g aOld bOld), g a b]
245− in unsafeBuildIncremental (g <$> sample (current da) <*> sample (currentIncremental ib)) ec
246−
247−instance (Monad m, Group q, Additive q, Query q, Reflex t) => MonadQuery t q (QueryT t q m) where
248− tellQueryIncremental q = do
249− QueryT (modify (currentIncremental q:))
250− QueryT (lift (tellEvent (fmapCheap unAdditivePatch (updatedIncremental q))))
251− askQueryResult = QueryT ask
252− queryIncremental q = do
253− tellQueryIncremental q
254− r <- askQueryResult
255− return $ zipDynWith crop (incrementalToDynamic q) r
src/Reflex/Query/Class.hsdeleted−77
@@ -1,77 +0,0 @@
1−{-# LANGUAGE DeriveDataTypeable #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−module Reflex.Query.Class
10− ( Query (..)
11− , QueryMorphism (..)
12− , SelectedCount (..)
13− , combineSelectedCounts
14−
15− , MonadQuery (..)
16− , tellQueryDyn
17− , queryDyn
18− ) where
19−
20−import Control.Monad.Reader
21−import Data.Bits
22−import Data.Data
23−import Data.Ix
24−import Data.Semigroup
25−import Foreign.Storable
26−
27−import Reflex.Class
28−
29−class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
30− type QueryResult a :: *
31− crop :: a -> QueryResult a -> QueryResult a
32−
33−-- | NB: QueryMorphism's must be group homomorphisms when acting on the query type
34−-- and compatible with the query relationship when acting on the query result
35−data QueryMorphism q q' = QueryMorphism
36− { _queryMorphism_mapQuery :: q -> q'
37− , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
38− }
39−
40−-- | This type keeps track of the multiplicity of elements of the view selector that are being used by the app
41−newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
42− deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
43−
44−instance Semigroup SelectedCount where
45− SelectedCount a <> SelectedCount b = SelectedCount (a + b)
46−
47−instance Monoid SelectedCount where
48− mempty = SelectedCount 0
49− mappend = (<>)
50−
51−instance Group SelectedCount where
52− negateG (SelectedCount a) = SelectedCount (negate a)
53−
54−instance Additive SelectedCount
55−
56−-- | The Semigroup/Monoid/Group instances for a ViewSelector should use this function which returns Nothing if the result is 0. This allows the pruning of leaves that are no longer wanted.
57−combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
58−combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
59−
60−class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
61− tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
62− askQueryResult :: m (Dynamic t (QueryResult q))
63− queryIncremental :: Incremental t (AdditivePatch q) -> m (Dynamic t (QueryResult q))
64−
65−instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
66− tellQueryIncremental = lift . tellQueryIncremental
67− askQueryResult = lift askQueryResult
68− queryIncremental = lift . queryIncremental
69−
70−tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
71−tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
72−
73−queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
74−queryDyn q = do
75− tellQueryDyn q
76− r <- askQueryResult
77− return $ zipDynWith crop q r
src/Reflex/Requester/Base.hsdeleted−156
@@ -1,156 +0,0 @@
1−-- | This module provides 'RequesterT', the standard implementation of
2−-- 'Requester'.
3−{-# LANGUAGE BangPatterns #-}
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE Rank2Types #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14−#ifdef USE_REFLEX_OPTIMIZER
15−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
16−#endif
17−module Reflex.Requester.Base
18− ( RequesterT (..)
19− , runRequesterT
20− , runWithReplaceRequesterTWith
21− , sequenceDMapWithAdjustRequesterTWith
22− ) where
23−
24−import Reflex.Class
25−import Reflex.EventWriter
26−import Reflex.Host.Class
27−import Reflex.PerformEvent.Class
28−import Reflex.PostBuild.Class
29−import Reflex.Requester.Class
30−import Reflex.TriggerEvent.Class
31−
32−import Control.Monad.Exception
33−import Control.Monad.Identity
34−import Control.Monad.Primitive
35−import Control.Monad.Reader
36−import Control.Monad.Ref
37−import Control.Monad.State.Strict
38−import Data.Coerce
39−import Data.Dependent.Map (DMap, DSum (..))
40−import qualified Data.Dependent.Map as DMap
41−import Data.Functor.Misc
42−import Data.Map (Map)
43−import Data.Unique.Tag
44−import Data.Some (Some)
45−
46−-- | A basic implementation of 'Requester'.
47−newtype RequesterT t request response m a = RequesterT { unRequesterT :: EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
48− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
49−-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
50−#if MIN_VERSION_base(4,9,1)
51− , MonadAsyncException
52−#endif
53− )
54−
55−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
56−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
57−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
58−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
59−
60−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
61−-- requests are made, and responses should be provided in the input 'Event'.
62−-- The 'Tag' keys will be used to return the responses to the same place the
63−-- requests were issued.
64−
65−runRequesterT :: (Reflex t, Monad m)
66− => RequesterT t request response m a
67− -> Event t (DMap (Tag (PrimState m)) response)
68− -> m (a, Event t (DMap (Tag (PrimState m)) request))
69−runRequesterT (RequesterT a) responses = do
70− (result, requests) <- runReaderT (runEventWriterT a) $ fan $
71− mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
72− return (result, requests)
73−
74−instance (Reflex t, PrimMonad m) => Requester t (RequesterT t request response m) where
75− type Request (RequesterT t request response m) = request
76− type Response (RequesterT t request response m) = response
77− withRequesting a = do
78− !t <- lift newTag --TODO: Fix this upstream
79− s <- RequesterT ask
80− (req, result) <- a $ select s $ WrapArg t
81− RequesterT $ tellEvent $ fmapCheap (DMap.singleton t) req
82− return result
83−
84−instance MonadTrans (RequesterT t request response) where
85− lift = RequesterT . lift . lift
86−
87−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
88− type Performable (RequesterT t request response m) = Performable m
89− performEvent_ = lift . performEvent_
90− performEvent = lift . performEvent
91−
92−instance MonadRef m => MonadRef (RequesterT t request response m) where
93− type Ref (RequesterT t request response m) = Ref m
94− newRef = lift . newRef
95− readRef = lift . readRef
96− writeRef r = lift . writeRef r
97−
98−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
99− newEventWithTrigger = lift . newEventWithTrigger
100− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
101−
102−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
103− ask = lift ask
104− local f (RequesterT (EventWriterT a)) = RequesterT $ EventWriterT $ mapStateT (mapReaderT $ local f) a
105− reader = lift . reader
106−
107−instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequesterT t request response m) where
108− runWithReplace (RequesterT a) em = RequesterT $ runWithReplace a (coerceEvent em)
109− traverseDMapWithKeyWithAdjust f dm edm = RequesterT $ traverseDMapWithKeyWithAdjust (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
110− traverseDMapWithKeyWithAdjustWithMove f dm edm = RequesterT $ traverseDMapWithKeyWithAdjustWithMove (\k v -> unRequesterT $ f k v) (coerce dm) (coerceEvent edm)
111−
112−runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
113− => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
114− -> RequesterT t request response m a
115− -> Event t (RequesterT t request response m b)
116− -> RequesterT t request response m (a, Event t b)
117−runWithReplaceRequesterTWith f a0 a' =
118− let f' :: forall a' b'. ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m a'
119− -> Event t (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m b')
120− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (a', Event t b')
121− f' x y = do
122− r <- EventWriterT $ ask
123− unRequesterT (f (runReaderT x r) (fmapCheap (`runReaderT` r) y))
124− in RequesterT $ runWithReplaceEventWriterTWith f' (coerce a0) (coerceEvent a')
125−
126−sequenceDMapWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
127− ( GCompare k
128− , Reflex t
129− , MonadHold t m
130− , PatchTarget (p' (Some k) (Event t (DMap (Tag (PrimState m)) request))) ~ Map (Some k) (Event t (DMap (Tag (PrimState m)) request))
131− , Patch (p' (Some k) (Event t (DMap (Tag (PrimState m)) request)))
132− )
133− => (forall k' v1 v2. GCompare k'
134− => (forall a. k' a -> v1 a -> m (v2 a))
135− -> DMap k' v1
136− -> Event t (p k' v1)
137− -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
138− )
139− -> (forall v1. (forall a. v1 a -> v' a) -> p k v1 -> p k v')
140− -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
141− -> (forall v2. p' (Some k) v2 -> [v2])
142− -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
143− -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
144− -> DMap k v
145− -> Event t (p k v)
146− -> RequesterT t request response m (DMap k v', Event t (p k v'))
147−sequenceDMapWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' =
148− let base' :: forall v1 v2.
149− (forall a. k a -> v1 a -> ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m (v2 a))
150− -> DMap k v1
151− -> Event t (p k v1)
152− -> EventWriterT t (DMap (Tag (PrimState m)) request) (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) (DMap k v2, Event t (p k v2))
153− base' f' x y = do
154− r <- EventWriterT ask
155− unRequesterT $ base (\k v -> runReaderT (f' k v) r) x y
156− in RequesterT $ sequenceDMapWithAdjustEventWriterTWith base' mapPatch weakenPatchWith patchNewElements mergePatchIncremental (\k v -> unRequesterT $ f k v) (coerce dm0) (coerceEvent dm')
src/Reflex/Requester/Class.hsdeleted−87
@@ -1,87 +0,0 @@
1−-- | This module defines 'Requester', which indicates that an action can make
2−-- requests and receive responses to them. Typically, this is used for things
3−-- like a WebSocket, where it's desirable to collect many potential sources of
4−-- events and send them over a single channel, then distribute the results back
5−-- out efficiently to their original request sites.
6−{-# LANGUAGE CPP #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−#ifdef USE_REFLEX_OPTIMIZER
13−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14−#endif
15−module Reflex.Requester.Class
16− ( Requester (..)
17− , requesting
18− , requesting_
19− , requestingIdentity
20− ) where
21−
22−import Control.Monad.Identity
23−import Control.Monad.Reader
24−import qualified Control.Monad.State.Lazy as Lazy
25−import Control.Monad.State.Strict
26−import Reflex.Class
27−
28−-- | A 'Requester' action can trigger requests of type @Request m a@ based on
29−-- 'Event's, and receive responses of type @Response m a@ in return. Note that
30−-- the @a@ type can vary within the 'Requester' action, but will be linked for a
31−-- given request. For example, if @Request m@ is 'IO' and @Response m@ is
32−-- 'Identity', then 'requestingIdentity' has the same type as
33−-- 'Reflex.PerformEvent.Class.performEvent'.
34−class (Reflex t, Monad m) => Requester t m | m -> t where
35− -- | The type of requests that this 'Requester' can emit
36− type Request m :: * -> *
37− -- | The type of responses that this 'Requester' can receive
38− type Response m :: * -> *
39− -- | Usually, end-user code should use 'requesting', 'requesting_', or
40− -- 'requestingIdentity'. For implementers of 'Requester', this function
41− -- provides a convenient way to define instances of the typeclass that
42− -- efficiently support the underlying operations without needing to use
43− -- 'Control.Monad.Fix.MonadFix'.
44− withRequesting :: (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
45−
46−instance Requester t m => Requester t (ReaderT r m) where
47− type Request (ReaderT r m) = Request m
48− type Response (ReaderT r m) = Response m
49− withRequesting f = do
50− r <- ask
51− lift $ withRequesting $ (`runReaderT` r) . f
52−
53−instance Requester t m => Requester t (StateT s m) where
54− type Request (StateT s m) = Request m
55− type Response (StateT s m) = Response m
56− withRequesting f = do
57− old <- get
58− (result, new) <- lift $ withRequesting $ \x -> do
59− ((request, result), new) <- runStateT (f x) old
60− return (request, (result, new))
61− put new
62− return result
63−
64−instance Requester t m => Requester t (Lazy.StateT s m) where
65− type Request (Lazy.StateT s m) = Request m
66− type Response (Lazy.StateT s m) = Response m
67− withRequesting f = do
68− old <- Lazy.get
69− (result, new) <- lift $ withRequesting $ \x -> do
70− ((request, result), new) <- Lazy.runStateT (f x) old
71− return (request, (result, new))
72− Lazy.put new
73− return result
74−
75−-- | Emit a request whenever the given 'Event' fires, and return responses in
76−-- the resulting 'Event'.
77−requesting :: Requester t m => Event t (Request m a) -> m (Event t (Response m a))
78−requesting req = withRequesting $ return . (,) req
79−
80−-- | Emit a request whenever the given 'Event' fires, and ignore all responses.
81−requesting_ :: Requester t m => Event t (Request m a) -> m ()
82−requesting_ req = withRequesting $ \_ -> return (req, ())
83−
84−-- | Emit a request whenever the given 'Event' fires, and unwrap the responses
85−-- before returning them. @Response m@ must be 'Identity'.
86−requestingIdentity :: (Requester t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
87−requestingIdentity = fmap coerceEvent . requesting
src/Reflex/Spider.hsdeleted−22
@@ -1,22 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−-- | This module exports all of the user-facing functionality of the 'Spider'
3−-- 'Reflex' engine
4−module Reflex.Spider
5− ( Spider
6− , SpiderTimeline
7− , Global
8− , SpiderHost
9− , runSpiderHost
10−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
11− , runSpiderHostForTimeline
12− , newSpiderTimeline
13− , withSpiderTimeline
14−#endif
15− -- * Deprecated
16− , SpiderEnv
17− ) where
18−
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−import Reflex.Class
21−#endif
22−import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hsdeleted−2192
@@ -1,2192 +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−#if defined(DEBUG) || defined(DEBUG_CYCLES)
12−{-# LANGUAGE LambdaCase #-}
13−#endif
14−{-# LANGUAGE MagicHash #-}
15−{-# LANGUAGE MultiParamTypeClasses #-}
16−{-# LANGUAGE MultiWayIf #-}
17−{-# LANGUAGE RankNTypes #-}
18−{-# LANGUAGE RoleAnnotations #-}
19−{-# LANGUAGE ScopedTypeVariables #-}
20−{-# LANGUAGE TypeFamilies #-}
21−{-# LANGUAGE TypeOperators #-}
22−{-# LANGUAGE UndecidableInstances #-}
23−#ifdef USE_REFLEX_OPTIMIZER
24−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
25−#endif
26−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
27−{-# OPTIONS_GHC -fno-warn-orphans #-}
28−#endif
29−-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
30−-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
31−module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
32−
33−import Control.Applicative
34−import Control.Concurrent
35−import Control.Exception
36−import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
37−import Control.Monad.Exception
38−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
39−import Control.Monad.Primitive
40−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
41−import Control.Monad.Ref
42−import Data.Align
43−import Data.Coerce
44−import Data.Dependent.Map (DMap, DSum (..))
45−import qualified Data.Dependent.Map as DMap
46−import Data.Foldable hiding (concat, elem, sequence_)
47−import Data.Function
48−import Data.Functor.Constant
49−import Data.Functor.Misc
50−import Data.Functor.Product
51−import Data.GADT.Compare
52−import Data.IntMap.Strict (IntMap)
53−import qualified Data.IntMap.Strict as IntMap
54−import Data.IORef
55−import Data.List hiding (foldl')
56−import Data.Maybe
57−import Data.Monoid ((<>))
58−import Data.Proxy
59−import Data.These
60−import Data.Traversable
61−import Data.Word
62−import GHC.Exts
63−import GHC.IORef (IORef (..))
64−import GHC.Stack
65−import Reflex.FunctorMaybe
66−import System.IO.Unsafe
67−import System.Mem.Weak
68−import Unsafe.Coerce
69−
70−#ifdef DEBUG_CYCLES
71−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
72−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
73−import qualified Data.List.NonEmpty as NonEmpty
74−import Data.Monoid (mempty)
75−import Data.Tree (Forest, Tree (..), drawForest)
76−#endif
77−
78−import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
79−import qualified Data.WeakBag as WeakBag
80−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
81−import Data.Reflection
82−import Data.Some (Some)
83−import qualified Data.Some as Some
84−import Data.Type.Coercion
85−import qualified Reflex.Class
86−import qualified Reflex.Class as R
87−import qualified Reflex.Host.Class
88−#endif
89−import Reflex.Patch
90−import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
91−
92−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
93−import Prelude hiding (any, concat, mapM, mapM_, sequence)
94−
95−#ifdef DEBUG_TRACE_EVENTS
96−import System.IO (stderr)
97−import qualified Data.ByteString.Char8 as BS8
98−#endif
99−
100−#ifdef DEBUG_TRACE_EVENTS
101−
102−withStackOneLine :: (BS8.ByteString -> a) -> a
103−withStackOneLine expr = unsafePerformIO $ do
104− stack <- currentCallStack
105− return (expr (BS8.pack (unwords (reverse stack))))
106−
107−#endif
108−
109−debugPropagate :: Bool
110−
111−debugInvalidateHeight :: Bool
112−
113−debugInvalidate :: Bool
114−
115−#ifdef DEBUG
116−
117−#define DEBUG_NODEIDS
118−
119−debugPropagate = True
120−
121−debugInvalidateHeight = False
122−
123−debugInvalidate = True
124−
125−class HasNodeId a where
126− getNodeId :: a -> Int
127−
128−instance HasNodeId (Hold x p a) where
129− getNodeId = holdNodeId
130−
131−instance HasNodeId (PushSubscribed x a b) where
132− getNodeId = pushSubscribedNodeId
133−
134−instance HasNodeId (SwitchSubscribed x a) where
135− getNodeId = switchSubscribedNodeId
136−
137−instance HasNodeId (FanSubscribed x a) where
138− getNodeId = fanSubscribedNodeId
139−
140−instance HasNodeId (CoincidenceSubscribed x a) where
141− getNodeId = coincidenceSubscribedNodeId
142−
143−instance HasNodeId (RootSubscribed x a) where
144− getNodeId = rootSubscribedNodeId
145−
146−instance HasNodeId (Pull x a) where
147− getNodeId = pullNodeId
148−
149−{-# INLINE showNodeId #-}
150−showNodeId :: HasNodeId a => a -> String
151−showNodeId = ("#"<>) . show . getNodeId
152−
153−showSubscriberType :: Subscriber x a -> String
154−showSubscriberType = \case
155− SubscriberPush _ _ -> "SubscriberPush"
156− SubscriberMerge _ _ -> "SubscriberMerge"
157− SubscriberFan _ -> "SubscriberFan"
158− SubscriberMergeChange _ -> "SubscriberMergeChange"
159− SubscriberHold _ -> "SubscriberHold"
160− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
161− SubscriberSwitch _ -> "SubscriberSwitch"
162− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
163− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
164− SubscriberHandle -> "SubscriberHandle"
165−
166−showEventType :: Event x a -> String
167−showEventType = \case
168− EventRoot _ _ -> "EventRoot"
169− EventNever -> "EventNever"
170− EventPush _ -> "EventPush"
171− EventMerge _ -> "EventMerge"
172− EventFan _ _ -> "EventFan"
173− EventSwitch _ -> "EventSwitch"
174− EventCoincidence _ -> "EventCoincidence"
175− EventHold _ -> "EventHold"
176− EventDyn _ -> "EventDyn"
177− EventHoldIdentity _ -> "EventHoldIdentity"
178− EventDynIdentity _ -> "EventDynIdentity"
179−
180−#else
181−
182−debugPropagate = False
183−
184−debugInvalidateHeight = False
185−
186−debugInvalidate = False
187−
188−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
189−{-# INLINE showNodeId #-}
190−showNodeId :: a -> String
191−showNodeId _ = ""
192−
193−#endif
194−
195−#ifdef DEBUG_NODEIDS
196−{-# NOINLINE nextNodeIdRef #-}
197−nextNodeIdRef :: IORef Int
198−nextNodeIdRef = unsafePerformIO $ newIORef 1
199−
200−newNodeId :: IO Int
201−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
202−
203−{-# NOINLINE unsafeNodeId #-}
204−unsafeNodeId :: a -> Int
205−unsafeNodeId a = unsafePerformIO $ do
206− touch a
207− newNodeId
208−#endif
209−
210−--------------------------------------------------------------------------------
211−-- EventSubscription
212−--------------------------------------------------------------------------------
213−
214−--NB: Once you subscribe to an Event, you must always hold on the the WHOLE EventSubscription you get back
215−-- If you do not retain the subscription, you may be prematurely unsubscribed from the parent event.
216−data EventSubscription x = EventSubscription
217− { _eventSubscription_unsubscribe :: !(IO ())
218− , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
219− }
220−
221−unsubscribe :: EventSubscription x -> IO ()
222−unsubscribe (EventSubscription u _) = u
223−
224−--------------------------------------------------------------------------------
225−-- Event
226−--------------------------------------------------------------------------------
227−
228−newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
229−
230−{-# INLINE subscribeAndRead #-}
231−subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
232−subscribeAndRead = unEvent
233−
234−{-# RULES
235−"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
236−"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
237−"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
238− #-}
239−
240−-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
241−-- caching; if the computation function is very cheap, this is (much) more
242−-- efficient than 'push'
243−{-# NOINLINE pushCheap #-}
244−pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
245−pushCheap f e = Event $ \sub -> do
246− (subscription, occ) <- subscribeAndRead e $ sub
247− { subscriberPropagate = \a -> do
248− mb <- runComputeM $ f a
249− mapM_ (subscriberPropagate sub) mb
250− }
251− occ' <- join <$> mapM (runComputeM . f) occ
252− return (subscription, occ')
253−
254−data CacheSubscribed x a
255− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
256− , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
257− , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
258− , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
259− , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
260− }
261−
262−-- | Construct an 'Event' whose value is guaranteed not to be recomputed
263−-- repeatedly
264−--
265−--TODO: Try a caching strategy where we subscribe directly to the parent when
266−--there's only one subscriber, and then build our own WeakBag only when a second
267−--subscriber joins
268−{-# NOINLINE cacheEvent #-}
269−cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
270−cacheEvent e =
271−#ifdef DEBUG_TRACE_EVENTS
272− withStackOneLine $ \callSite -> Event $
273−#else
274− Event $
275−#endif
276− let !mSubscribedRef = unsafeNewIORef e Nothing
277− in \sub -> {-# SCC "cacheEvent" #-} do
278−#ifdef DEBUG_TRACE_EVENTS
279− unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
280−#endif
281− mSubscribed <- liftIO $ readIORef mSubscribedRef
282− case mSubscribed of
283− Just subscribed -> liftIO $ do
284− ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
285− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
286− let es = EventSubscribed
287− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
288− , eventSubscribedRetained = toAny subscribed
289− }
290− return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
291− Nothing -> do
292− weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
293− (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
294− occRef <- liftIO $ newIORef undefined
295− (parentSub, occ) <- subscribeAndRead e $ Subscriber
296− { subscriberPropagate = \a -> do
297− liftIO $ writeIORef occRef $ Just a
298− scheduleClear occRef
299− propagate a subscribers
300− , subscriberInvalidateHeight = \old -> do
301− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
302− , subscriberRecalculateHeight = \new -> do
303− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
304− }
305− liftIO $ writeIORef occRef occ
306− when (isJust occ) $ scheduleClear occRef
307− let !subscribed = CacheSubscribed
308− { _cacheSubscribed_subscribers = subscribers
309− , _cacheSubscribed_weakSelf = weakSelf
310− , _cacheSubscribed_parent = parentSub
311− , _cacheSubscribed_cached = mSubscribedRef
312− , _cacheSubscribed_occurrence = occRef
313− }
314− liftIO $ writeIORef mSubscribedRef $ Just subscribed
315− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
316− return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
317−
318−cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
319−cleanupCacheSubscribed self = do
320− unsubscribe $ _cacheSubscribed_parent self
321− writeIORef (_cacheSubscribed_cached self) Nothing
322−
323−subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
324−subscribe e s = fst <$> subscribeAndRead e s
325−
326−{-# INLINE wrap #-}
327−wrap :: MonadIO m => (t -> EventSubscribed x) -> (Subscriber x a -> m (WeakBagTicket, t, Maybe a)) -> Subscriber x a -> m (EventSubscription x, Maybe a)
328−wrap tag getSpecificSubscribed sub = do
329− (sln, subd, occ) <- getSpecificSubscribed sub
330− let es = tag subd
331− return (EventSubscription (WeakBag.remove sln >> touch sln) es, occ)
332−
333−eventRoot :: GCompare k => k a -> Root x k -> Event x a
334−eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
335−
336−eventNever :: Event x a
337−eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
338−
339−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
340−eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
341−
342−eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
343−eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
344−
345−eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
346−eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
347−
348−eventHold :: HasSpiderTimeline x => Hold x p -> Event x p
349−eventHold !h = Event $ subscribeHoldEvent h
350−
351−eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
352−eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
353−
354−{-# INLINE subscribeCoincidenceInner #-}
355−subscribeCoincidenceInner :: CanSubscribe x m => Event x a -> Height -> CoincidenceSubscribed x a -> m (Maybe a, Height, EventSubscribed x)
356−subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
357− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
358− (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
359− innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
360− let height = max innerHeight outerHeight
361− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
362− return (innerOcc, height, innerSubd)
363−
364−--------------------------------------------------------------------------------
365−-- Subscriber
366−--------------------------------------------------------------------------------
367−
368−data Subscriber x a = Subscriber
369− { subscriberPropagate :: !(a -> EventM x ())
370− , subscriberInvalidateHeight :: !(Height -> IO ())
371− , subscriberRecalculateHeight :: !(Height -> IO ())
372− }
373−
374−--TODO: Move this comment to WeakBag
375−-- These function are constructor functions that are marked NOINLINE so they are
376−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
377−-- so any weak references that are attached to the constructors will have their
378−-- finalizer run. Using the opaque constructor, does not see the
379−-- constructor application, so it behaves like an IORef and cannot be fused away.
380−--
381−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
382−-- the weak pointer to it in some cases.
383−
384−newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
385−newSubscriberHold h = return $ Subscriber
386− { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
387− , subscriberInvalidateHeight = \_ -> return ()
388− , subscriberRecalculateHeight = \_ -> return ()
389− }
390−
391−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
392−newSubscriberFan subscribed = return $ Subscriber
393− { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
394− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
395− tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
396− liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
397− scheduleClear $ fanSubscribedOccurrence subscribed
398− let f _ (Pair (Identity v) subsubs) = do
399− propagate v $ _fanSubscribedChildren_list subsubs
400− return $ Constant ()
401− _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
402− return ()
403− , subscriberInvalidateHeight = \old -> do
404− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
405− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
406− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
407− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
408− , subscriberRecalculateHeight = \new -> do
409− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
410− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
411− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
412− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
413− }
414−
415−newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
416−newSubscriberSwitch subscribed = return $ Subscriber
417− { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
418− tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
419− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
420− scheduleClear $ switchSubscribedOccurrence subscribed
421− propagate a $ switchSubscribedSubscribers subscribed
422− , subscriberInvalidateHeight = \_ -> do
423− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
424− oldHeight <- readIORef $ switchSubscribedHeight subscribed
425− when (oldHeight /= invalidHeight) $ do
426− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
427− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
428− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
429− , subscriberRecalculateHeight = \new -> do
430− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
431− updateSwitchHeight new subscribed
432− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
433− }
434−
435−newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
436−newSubscriberCoincidenceOuter subscribed = return $ Subscriber
437− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
438− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
439− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
440− tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
441− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
442− tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
443− tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
444− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
445− scheduleClear $ coincidenceSubscribedInnerParent subscribed
446− case occ of
447− Nothing -> do
448− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
449− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
450− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
451− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
452− Just o -> do -- Since it's already firing, no need to adjust height
453− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
454− scheduleClear $ coincidenceSubscribedOccurrence subscribed
455− propagate o $ coincidenceSubscribedSubscribers subscribed
456− , subscriberInvalidateHeight = \_ -> do
457− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
458− invalidateCoincidenceHeight subscribed
459− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
460− , subscriberRecalculateHeight = \_ -> do
461− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
462− recalculateCoincidenceHeight subscribed
463− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
464− }
465−
466−newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
467−newSubscriberCoincidenceInner subscribed = return $ Subscriber
468− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
469− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
470− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
471− case occ of
472− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
473− Nothing -> do
474− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
475− scheduleClear $ coincidenceSubscribedOccurrence subscribed
476− propagate a $ coincidenceSubscribedSubscribers subscribed
477− , subscriberInvalidateHeight = \_ -> do
478− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
479− invalidateCoincidenceHeight subscribed
480− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
481− , subscriberRecalculateHeight = \_ -> do
482− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
483− recalculateCoincidenceHeight subscribed
484− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
485− }
486−
487−invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
488−invalidateSubscriberHeight = flip subscriberInvalidateHeight
489−
490−recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
491−recalculateSubscriberHeight = flip subscriberRecalculateHeight
492−
493−-- | Propagate everything at the current height
494−propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
495−propagate a subscribers = withIncreasedDepth $ do
496− -- 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
497− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
498− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
499−
500−--------------------------------------------------------------------------------
501−-- EventSubscribed
502−--------------------------------------------------------------------------------
503−
504−toAny :: a -> Any
505−toAny = unsafeCoerce
506−
507−data EventSubscribed x = EventSubscribed
508− { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
509− , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
510−#ifdef DEBUG_CYCLES
511− , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
512− , eventSubscribedHasOwnHeightRef :: !Bool
513− , eventSubscribedWhoCreated :: !(IO [String])
514−#endif
515− }
516−
517−eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
518−eventSubscribedRoot !r = EventSubscribed
519− { eventSubscribedHeightRef = zeroRef
520− , eventSubscribedRetained = toAny r
521−#ifdef DEBUG_CYCLES
522− , eventSubscribedGetParents = return []
523− , eventSubscribedHasOwnHeightRef = False
524− , eventSubscribedWhoCreated = return ["root"]
525−#endif
526− }
527−
528−eventSubscribedNever :: EventSubscribed x
529−eventSubscribedNever = EventSubscribed
530− { eventSubscribedHeightRef = zeroRef
531− , eventSubscribedRetained = toAny ()
532−#ifdef DEBUG_CYCLES
533− , eventSubscribedGetParents = return []
534− , eventSubscribedHasOwnHeightRef = False
535− , eventSubscribedWhoCreated = return ["never"]
536−#endif
537− }
538−
539−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
540−eventSubscribedFan !subscribed = EventSubscribed
541− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
542− , eventSubscribedRetained = toAny subscribed
543−#ifdef DEBUG_CYCLES
544− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
545− , eventSubscribedHasOwnHeightRef = False
546− , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
547−#endif
548− }
549−
550−eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
551−eventSubscribedSwitch !subscribed = EventSubscribed
552− { eventSubscribedHeightRef = switchSubscribedHeight subscribed
553− , eventSubscribedRetained = toAny subscribed
554−#ifdef DEBUG_CYCLES
555− , eventSubscribedGetParents = do
556− s <- readIORef $ switchSubscribedCurrentParent subscribed
557− return [Some.This $ _eventSubscription_subscribed s]
558− , eventSubscribedHasOwnHeightRef = True
559− , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
560−#endif
561− }
562−
563−eventSubscribedCoincidence :: CoincidenceSubscribed x a -> EventSubscribed x
564−eventSubscribedCoincidence !subscribed = EventSubscribed
565− { eventSubscribedHeightRef = coincidenceSubscribedHeight subscribed
566− , eventSubscribedRetained = toAny subscribed
567−#ifdef DEBUG_CYCLES
568− , eventSubscribedGetParents = do
569− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
570− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
571− innerParents = maybeToList $ fmap Some.This innerSubscription
572− return $ outerParent : innerParents
573− , eventSubscribedHasOwnHeightRef = True
574− , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
575−#endif
576− }
577−
578−getEventSubscribedHeight :: EventSubscribed x -> IO Height
579−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
580−
581−#ifdef DEBUG_CYCLES
582−whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
583−whoCreatedEventSubscribed = eventSubscribedWhoCreated
584−
585−walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
586−walkInvalidHeightParents s0 = do
587− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
588− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
589− when (h == invalidHeight) $ do
590− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
591− modify (Some.This s :)
592− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
593− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
594− return subscribers
595−#endif
596−
597−{-# INLINE subscribeHoldEvent #-}
598−subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
599−subscribeHoldEvent = subscribeAndRead . holdEvent
600−
601−--------------------------------------------------------------------------------
602−-- Behavior
603−--------------------------------------------------------------------------------
604−
605−newtype Behavior x a = Behavior { readBehaviorTracked :: BehaviorM x a }
606−
607−behaviorHold :: Hold x p -> Behavior x (PatchTarget p)
608−behaviorHold !h = Behavior $ readHoldTracked h
609−
610−behaviorHoldIdentity :: Hold x (Identity a) -> Behavior x a
611−behaviorHoldIdentity = behaviorHold
612−
613−behaviorConst :: a -> Behavior x a
614−behaviorConst !a = Behavior $ return a
615−
616−behaviorPull :: Pull x a -> Behavior x a
617−behaviorPull !p = Behavior $ do
618− val <- liftIO $ readIORef $ pullValue p
619− case val of
620− Just subscribed -> do
621− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
622− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
623− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
624− return $ pullSubscribedValue subscribed
625− Nothing -> do
626− i <- liftIO $ newInvalidatorPull p
627− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
628− parentsRef <- liftIO $ newIORef []
629− holdInits <- askBehaviorHoldInits
630− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
631− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
632− parents <- liftIO $ readIORef parentsRef
633− let subscribed = PullSubscribed
634− { pullSubscribedValue = a
635− , pullSubscribedInvalidators = invsRef
636− , pullSubscribedOwnInvalidator = i
637− , pullSubscribedParents = parents
638− }
639− liftIO $ writeIORef (pullValue p) $ Just subscribed
640− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
641− return a
642−
643−behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
644−behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
645−
646−{-# INLINE readHoldTracked #-}
647−readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
648−readHoldTracked h = do
649− result <- liftIO $ readIORef $ holdValue h
650− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
651− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
652− return result
653−
654−{-# INLINABLE readBehaviorUntracked #-}
655−readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
656−readBehaviorUntracked b = do
657− holdInits <- getDeferralQueue
658− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
659−
660−--------------------------------------------------------------------------------
661−-- Dynamic
662−--------------------------------------------------------------------------------
663−
664−data Dynamic x p = Dynamic
665− { dynamicCurrent :: !(Behavior x (PatchTarget p))
666− , dynamicUpdated :: !(Event x p)
667− }
668−
669−dynamicHold :: HasSpiderTimeline x => Hold x p -> Dynamic x p
670−dynamicHold !h = Dynamic
671− { dynamicCurrent = behaviorHold h
672− , dynamicUpdated = eventHold h
673− }
674−
675−dynamicHoldIdentity :: HasSpiderTimeline x => Hold x (Identity a) -> Dynamic x (Identity a)
676−dynamicHoldIdentity = dynamicHold
677−
678−dynamicConst :: PatchTarget p -> Dynamic x p
679−dynamicConst !a = Dynamic
680− { dynamicCurrent = behaviorConst a
681− , dynamicUpdated = eventNever
682− }
683−
684−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
685−dynamicDyn !d = Dynamic
686− { dynamicCurrent = behaviorDyn d
687− , dynamicUpdated = eventDyn d
688− }
689−
690−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
691−dynamicDynIdentity = dynamicDyn
692−
693−--------------------------------------------------------------------------------
694−-- Combinators
695−--------------------------------------------------------------------------------
696−
697−--TODO: Figure out why certain things are not 'representational', then make them
698−--representational so we can use coerce
699−
700−--type role Hold representational
701−data Hold x p
702− = Hold { holdValue :: !(IORef (PatchTarget p))
703− , holdInvalidators :: !(IORef [Weak (Invalidator x)])
704− , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
705− , holdParent :: !(IORef (Maybe (EventSubscription x))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
706−#ifdef DEBUG_NODEIDS
707− , holdNodeId :: Int
708−#endif
709− }
710−
711−-- | A statically allocated 'SpiderTimeline'; this is the only instance of
712−-- 'SpiderTimeline' that can used when using
713−-- -fspecialize-to-spidertimeline-global.
714−data Global
715−
716−{-# NOINLINE globalSpiderTimelineEnv #-}
717−globalSpiderTimelineEnv :: SpiderTimelineEnv Global
718−globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
719−
720−-- | Stores all global data relevant to a particular Spider timeline; only one
721−-- value should exist for each type @x@
722−data SpiderTimelineEnv x = SpiderTimelineEnv
723− { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
724− , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
725−#ifdef DEBUG
726− , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
727−#endif
728− }
729−type role SpiderTimelineEnv nominal
730−
731−instance Eq (SpiderTimelineEnv x) where
732− _ == _ = True -- Since only one exists of each type
733−
734−instance GEq SpiderTimelineEnv where
735−#ifdef EXPERIMENTAL_DEPENDENT_SUM_INSTANCES
736− geqToEq _ = id
737− geqUnify (a :: SpiderTimelineEnv a) (b :: SpiderTimelineEnv b) different same = if _spiderTimeline_lock a == _spiderTimeline_lock b
738− then case unsafeCoerce Refl :: a :~: b of
739− Refl -> same -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
740− else different
741−#else
742− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
743− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
744− else Nothing
745−#endif
746−
747−data EventEnv x
748− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
749− , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
750− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
751− , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
752− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
753− , eventEnvRootClears :: !(IORef [SomeRootClear])
754− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
755− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
756− , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
757− }
758−
759−{-# INLINE runEventM #-}
760−runEventM :: EventM x a -> IO a
761−runEventM = unEventM
762−
763−asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
764−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
765−
766−class MonadIO m => Defer a m where
767− getDeferralQueue :: m (IORef [a])
768−
769−{-# INLINE defer #-}
770−defer :: Defer a m => a -> m ()
771−defer a = do
772− q <- getDeferralQueue
773− liftIO $ modifyIORef' q (a:)
774−
775−instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
776− {-# INLINE getDeferralQueue #-}
777− getDeferralQueue = asksEventEnv eventEnvAssignments
778−
779−instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
780− {-# INLINE getDeferralQueue #-}
781− getDeferralQueue = asksEventEnv eventEnvHoldInits
782−
783−instance Defer (SomeHoldInit x) (BehaviorM x) where
784− {-# INLINE getDeferralQueue #-}
785− getDeferralQueue = BehaviorM $ asks snd
786−
787−instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
788− {-# INLINE getDeferralQueue #-}
789− getDeferralQueue = asksEventEnv eventEnvMergeUpdates
790−
791−instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
792− {-# INLINE getDeferralQueue #-}
793− getDeferralQueue = asksEventEnv eventEnvMergeInits
794−
795−class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
796− getCurrentHeight :: m Height
797− scheduleMerge :: Height -> EventM x () -> m ()
798−
799−instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
800− {-# INLINE getCurrentHeight #-}
801− getCurrentHeight = do
802− heightRef <- asksEventEnv eventEnvCurrentHeight
803− liftIO $ readIORef heightRef
804− {-# INLINE scheduleMerge #-}
805− scheduleMerge height subscribed = do
806− delayedRef <- asksEventEnv eventEnvDelayedMerges
807− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
808−
809−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
810−class x ~ Global => HasSpiderTimeline x where
811−#else
812−class HasSpiderTimeline x where
813−#endif
814− -- | Retrieve the current SpiderTimelineEnv
815− spiderTimeline :: SpiderTimelineEnv x
816−
817−instance HasSpiderTimeline Global where
818− spiderTimeline = globalSpiderTimelineEnv
819−
820−putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
821−putCurrentHeight h = do
822− heightRef <- asksEventEnv eventEnvCurrentHeight
823− liftIO $ writeIORef heightRef $! h
824−
825−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
826− {-# INLINE getDeferralQueue #-}
827− getDeferralQueue = asksEventEnv eventEnvClears
828−
829−{-# INLINE scheduleClear #-}
830−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
831−scheduleClear r = defer $ SomeClear r
832−
833−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
834− {-# INLINE getDeferralQueue #-}
835− getDeferralQueue = asksEventEnv eventEnvRootClears
836−
837−{-# INLINE scheduleRootClear #-}
838−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
839−scheduleRootClear r = defer $ SomeRootClear r
840−
841−instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
842− {-# INLINE getDeferralQueue #-}
843− getDeferralQueue = asksEventEnv eventEnvResetCoincidences
844−
845−-- Note: hold cannot examine its event until after the phase is over
846−{-# INLINE [1] hold #-}
847−hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
848−hold v0 e = do
849− valRef <- liftIO $ newIORef v0
850− invsRef <- liftIO $ newIORef []
851− parentRef <- liftIO $ newIORef Nothing
852−#ifdef DEBUG_NODEIDS
853− nodeId <- liftIO newNodeId
854−#endif
855− let h = Hold
856− { holdValue = valRef
857− , holdInvalidators = invsRef
858− , holdEvent = e
859− , holdParent = parentRef
860−#ifdef DEBUG_NODEIDS
861− , holdNodeId = nodeId
862−#endif
863− }
864− defer $ SomeHoldInit h
865− return h
866−
867−{-# INLINE getHoldEventSubscription #-}
868−getHoldEventSubscription :: forall m p x. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer SomeClear m, Patch p, HasCurrentHeight x m, Defer (SomeMergeInit x) m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Hold x p -> m (EventSubscription x)
869−getHoldEventSubscription h = do
870− ep <- liftIO $ readIORef $ holdParent h
871− case ep of
872− Just subd -> return subd
873− Nothing -> do
874− let e = holdEvent h
875− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
876− (subscription@(EventSubscription _ _), occ) <- subscribeAndRead e =<< liftIO (newSubscriberHold h)
877− liftIO $ writeIORef subscriptionRef $! subscription
878− case occ of
879− Nothing -> return ()
880− Just o -> do
881− old <- liftIO $ readIORef $ holdValue h
882− case apply o old of
883− Nothing -> return ()
884− Just new -> do
885− -- Need to evaluate these so that we don't retain the Hold itself
886− v <- liftIO $ evaluate $ holdValue h
887− i <- liftIO $ evaluate $ holdInvalidators h
888− defer $ SomeAssignment v i new
889− liftIO $ writeIORef (holdParent h) $ Just subscription
890− return subscription
891−
892−type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
893−
894−--type role BehaviorM representational
895−-- BehaviorM can sample behaviors
896−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
897−
898−instance Monad (BehaviorM x) where
899− {-# INLINE (>>=) #-}
900− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
901− {-# INLINE (>>) #-}
902− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
903− {-# INLINE return #-}
904− return x = BehaviorM $ return x
905− {-# INLINE fail #-}
906− fail s = BehaviorM $ fail s
907−
908−data BehaviorSubscribed x a
909− = forall p. BehaviorSubscribedHold (Hold x p)
910− | BehaviorSubscribedPull (PullSubscribed x a)
911−
912−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
913−
914−--type role PullSubscribed representational
915−data PullSubscribed x a
916− = PullSubscribed { pullSubscribedValue :: !a
917− , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
918− , pullSubscribedOwnInvalidator :: !(Invalidator x)
919− , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
920− }
921−
922−--type role Pull representational
923−data Pull x a
924− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
925− , pullCompute :: !(BehaviorM x a)
926−#ifdef DEBUG_NODEIDS
927− , pullNodeId :: Int
928−#endif
929− }
930−
931−data Invalidator x
932− = forall a. InvalidatorPull (Pull x a)
933− | forall a. InvalidatorSwitch (SwitchSubscribed x a)
934−
935−data RootSubscribed x a = forall k. GCompare k => RootSubscribed
936− { rootSubscribedKey :: !(k a)
937− , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
938− , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
939− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
940− , rootSubscribedUninit :: IO ()
941− , rootSubscribedWeakSelf :: !(IORef (Weak (RootSubscribed x a))) --TODO: Can we make this a lazy non-IORef and then force it manually to avoid an indirection each time we use it?
942−#ifdef DEBUG_NODEIDS
943− , rootSubscribedNodeId :: Int
944−#endif
945− }
946−
947−data Root x (k :: * -> *)
948− = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
949− , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
950− , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
951− }
952−
953−data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
954−
955−data SomeMergeUpdate x = SomeMergeUpdate
956− { _someMergeUpdate_update :: !(EventM x [EventSubscription x])
957− , _someMergeUpdate_invalidateHeight :: !(IO ())
958− , _someMergeUpdate_recalculateHeight :: !(IO ())
959− }
960−
961−newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
962−
963−-- EventM can do everything BehaviorM can, plus create holds
964−newtype EventM x a = EventM { unEventM :: 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
965−
966−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
967−
968−instance Defer (SomeHoldInit x) (ComputeM x) where
969− getDeferralQueue = ComputeM ask
970−
971−{-# INLINABLE runComputeM #-}
972−runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
973−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
974−
975−newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
976−
977−data MergeSubscribedParentWithMove x k a = MergeSubscribedParentWithMove
978− { _mergeSubscribedParentWithMove_subscription :: !(EventSubscription x)
979− , _mergeSubscribedParentWithMove_key :: !(IORef (k a))
980− }
981−
982−data HeightBag = HeightBag
983− { _heightBag_size :: {-# UNPACK #-} !Int
984− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
985− }
986− deriving (Show, Read, Eq, Ord)
987−
988−heightBagEmpty :: HeightBag
989−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
990−
991−heightBagSize :: HeightBag -> Int
992−heightBagSize = _heightBag_size
993−
994−heightBagFromList :: [Height] -> HeightBag
995−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
996−
997−heightBagAdd :: Height -> HeightBag -> HeightBag
998−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
999−
1000−heightBagRemove :: Height -> HeightBag -> HeightBag
1001−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
1002− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
1003− Just old -> HeightBag (pred s) $ case old of
1004− 0 -> IntMap.delete h c
1005− _ -> IntMap.insert h (pred old) c
1006−
1007−heightBagMax :: HeightBag -> Height
1008−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
1009− Just ((h, _), _) -> Height h
1010− Nothing -> zeroHeight
1011−
1012−heightBagVerify :: HeightBag -> HeightBag
1013−#ifdef DEBUG
1014−heightBagVerify b@(HeightBag s c) = if
1015− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
1016− -> error $ "heightBagVerify: size doesn't match: " <> show b
1017− | unHeight invalidHeight `IntMap.member` c
1018− -> error $ "heightBagVerify: contains invalid height: " <> show b
1019− | otherwise -> b
1020−#else
1021−heightBagVerify = id
1022−#endif
1023−
1024−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1025− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1026− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1027− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1028− }
1029−
1030−data FanSubscribed (x :: *) k
1031− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1032− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1033− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1034− , fanSubscribedParent :: !(EventSubscription x)
1035−#ifdef DEBUG_NODEIDS
1036− , fanSubscribedNodeId :: Int
1037−#endif
1038− }
1039−
1040−data Fan x k
1041− = Fan { fanParent :: !(Event x (DMap k Identity))
1042− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1043− }
1044−
1045−data SwitchSubscribed x a
1046− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1047− , switchSubscribedOccurrence :: !(IORef (Maybe a))
1048− , switchSubscribedHeight :: !(IORef Height)
1049− , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1050− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
1051− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
1052− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
1053− , switchSubscribedParent :: !(Behavior x (Event x a))
1054− , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
1055− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
1056−#ifdef DEBUG_NODEIDS
1057− , switchSubscribedNodeId :: Int
1058−#endif
1059− }
1060−
1061−data Switch x a
1062− = Switch { switchParent :: !(Behavior x (Event x a))
1063− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1064− }
1065−
1066−#ifdef USE_TEMPLATE_HASKELL
1067−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1068−#endif
1069−data CoincidenceSubscribed x a
1070− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1071− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1072− , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1073− , coincidenceSubscribedHeight :: !(IORef Height)
1074− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
1075− , coincidenceSubscribedOuterParent :: !(EventSubscription x)
1076− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
1077− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
1078−#ifdef DEBUG_NODEIDS
1079− , coincidenceSubscribedNodeId :: Int
1080−#endif
1081− }
1082−
1083−data Coincidence x a
1084− = Coincidence { coincidenceParent :: !(Event x (Event x a))
1085− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1086− }
1087−
1088−{-# NOINLINE newInvalidatorSwitch #-}
1089−newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
1090−newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1091−
1092−{-# NOINLINE newInvalidatorPull #-}
1093−newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1094−newInvalidatorPull p = return $! InvalidatorPull p
1095−
1096−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1097− fmapMaybe f = push $ return . f
1098−
1099−instance HasSpiderTimeline x => Align (Event x) where
1100− nil = eventNever
1101− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1102−
1103−newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
1104−
1105−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1106−newMapDyn f d = dynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1107−
1108−--TODO: Avoid the duplication between this and R.zipDynWith
1109−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1110−zipDynWith f da db =
1111− let eab = align (dynamicUpdated da) (dynamicUpdated db)
1112− ec = flip push eab $ \o -> do
1113− (a, b) <- case o of
1114− This (Identity a) -> do
1115− b <- readBehaviorUntracked $ dynamicCurrent db
1116− return (a, b)
1117− That (Identity b) -> do
1118− a <- readBehaviorUntracked $ dynamicCurrent da
1119− return (a, b)
1120− These (Identity a) (Identity b) -> return (a, b)
1121− return $ Just $ Identity $ f a b
1122− in dynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (dynamicCurrent da) <*> readBehaviorUntracked (dynamicCurrent db)) ec
1123−
1124−unsafeDyn :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1125−unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
1126− where x = (readV0, v')
1127−
1128−-- ResultM can read behaviors and events
1129−type ResultM = EventM
1130−
1131−{-# NOINLINE unsafeNewIORef #-}
1132−unsafeNewIORef :: a -> b -> IORef b
1133−unsafeNewIORef _ b = unsafePerformIO $ newIORef b
1134−
1135−instance HasSpiderTimeline x => Functor (Event x) where
1136− fmap f = push $ return . Just . f
1137−
1138−instance Functor (Behavior x) where
1139− fmap f = pull . fmap f . readBehaviorTracked
1140−
1141−{-# INLINE push #-}
1142−push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
1143−push f e = cacheEvent (pushCheap f e)
1144−
1145−{-# INLINABLE pull #-}
1146−pull :: BehaviorM x a -> Behavior x a
1147−pull a = behaviorPull $ Pull
1148− { pullCompute = a
1149− , pullValue = unsafeNewIORef a Nothing
1150−#ifdef DEBUG_NODEIDS
1151− , pullNodeId = unsafeNodeId a
1152−#endif
1153− }
1154−
1155−{-# INLINABLE switch #-}
1156−switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1157−switch a = eventSwitch $ Switch
1158− { switchParent = a
1159− , switchSubscribed = unsafeNewIORef a Nothing
1160− }
1161−
1162−coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1163−coincidence a = eventCoincidence $ Coincidence
1164− { coincidenceParent = a
1165− , coincidenceSubscribed = unsafeNewIORef a Nothing
1166− }
1167−
1168−-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1169−run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1170−run roots after = do
1171− tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1172− t <- SpiderHost ask
1173− result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
1174− rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1175− occBefore <- liftIO $ do
1176− occBefore <- readIORef occRef
1177− writeIORef occRef $! DMap.insert k a occBefore
1178− return occBefore
1179− if DMap.null occBefore
1180− then do scheduleRootClear occRef
1181− return $ Just r
1182− else return Nothing
1183− forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1184− propagate a subscribersRef
1185− delayedRef <- asksEventEnv eventEnvDelayedMerges
1186− let go = do
1187− delayed <- liftIO $ readIORef delayedRef
1188− case IntMap.minViewWithKey delayed of
1189− Nothing -> return ()
1190− Just ((currentHeight, cur), future) -> do
1191− tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
1192− putCurrentHeight $ Height currentHeight
1193− liftIO $ writeIORef delayedRef $! future
1194− sequence_ cur
1195− go
1196− go
1197− putCurrentHeight maxBound
1198− after
1199− tracePropagate (Proxy :: Proxy x) "Done running an event frame"
1200− return result
1201−
1202−scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
1203−scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1204− height <- liftIO $ readIORef heightRef
1205− currentHeight <- getCurrentHeight
1206− case height `compare` currentHeight of
1207− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
1208− GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1209− EQ -> a
1210−
1211−data SomeClear = forall a. SomeClear (IORef (Maybe a))
1212−
1213−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1214−
1215−data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1216−
1217−debugFinalize :: Bool
1218−debugFinalize = False
1219−
1220−mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1221−mkWeakPtrWithDebug x debugNote = do
1222− x' <- evaluate x
1223− mkWeakPtr x' $
1224− if debugFinalize
1225− then Just $ putStrLn $ "finalizing: " ++ debugNote
1226− else Nothing
1227−
1228−type WeakList a = [Weak a]
1229−
1230−{-# INLINE withIncreasedDepth #-}
1231−#ifdef DEBUG
1232−withIncreasedDepth :: CanTrace x m => m a -> m a
1233−withIncreasedDepth a = do
1234− spiderTimeline <- askSpiderTimelineEnv
1235− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1236− result <- a
1237− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1238− return result
1239−#else
1240−withIncreasedDepth :: m a -> m a
1241−withIncreasedDepth = id
1242−#endif
1243−
1244−type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
1245−
1246−{-# INLINE tracePropagate #-}
1247−tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1248−tracePropagate p = traceWhen p debugPropagate
1249−
1250−{-# INLINE traceInvalidate #-}
1251−traceInvalidate :: String -> IO ()
1252−traceInvalidate = when debugInvalidate . liftIO . putStrLn
1253−
1254−{-# INLINE traceWhen #-}
1255−traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1256−traceWhen p b message = traceMWhen p b $ return message
1257−
1258−{-# INLINE traceMWhen #-}
1259−traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1260−traceMWhen _ b getMessage = when b $ do
1261− message <- getMessage
1262−#ifdef DEBUG
1263− spiderTimeline <- askSpiderTimelineEnv
1264− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1265−#else
1266− let d = 0
1267−#endif
1268− liftIO $ putStrLn $ replicate d ' ' <> message
1269−
1270−whoCreatedIORef :: IORef a -> IO [String]
1271−whoCreatedIORef (IORef a) = whoCreated $! a
1272−
1273−#ifdef DEBUG_CYCLES
1274−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1275−groupByHead = \case
1276− [] -> []
1277− (x :| xs) : t -> case groupByHead t of
1278− [] -> [(x, xs :| [])]
1279− l@((y, yss) : t')
1280− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
1281− | otherwise -> (x, xs :| []) : l
1282−
1283−listsToForest :: Eq a => [[a]] -> Forest a
1284−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1285−#endif
1286−
1287−{-# INLINE propagateSubscriberHold #-}
1288−propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
1289−propagateSubscriberHold h a = do
1290− {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
1291− invalidators <- liftIO $ readIORef $ holdInvalidators h
1292− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1293− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1294− case {-# SCC "apply" #-} apply a v of
1295− Nothing -> return ()
1296− Just v' -> do
1297− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1298− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1299− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1300− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1301−
1302−data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1303−
1304−runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1305−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1306−
1307−askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1308−askInvalidator = do
1309− (!m, _) <- BehaviorM ask
1310− case m of
1311− Nothing -> return Nothing
1312− Just (!wi, _) -> return $ Just wi
1313−
1314−askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1315−askParentsRef = do
1316− (!m, _) <- BehaviorM ask
1317− case m of
1318− Nothing -> return Nothing
1319− Just (_, !p) -> return $ Just p
1320−
1321−askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1322−askBehaviorHoldInits = do
1323− (_, !his) <- BehaviorM ask
1324− return his
1325−
1326−{-# INLINE getDynHold #-}
1327−getDynHold :: (Defer (SomeHoldInit x) m, Patch p) => Dyn x p -> m (Hold x p)
1328−getDynHold d = do
1329− mh <- liftIO $ readIORef $ unDyn d
1330− case mh of
1331− Right h -> return h
1332− Left (readV0, v') -> do
1333− holdInits <- getDeferralQueue
1334− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1335− h <- hold v0 v'
1336− liftIO $ writeIORef (unDyn d) $ Right h
1337− return h
1338−
1339−-- Always refers to 0
1340−{-# NOINLINE zeroRef #-}
1341−zeroRef :: IORef Height
1342−zeroRef = unsafePerformIO $ newIORef zeroHeight
1343−
1344−type CanSubscribe x m =
1345− ( HasSpiderTimeline x
1346− , HasCurrentHeight x m
1347− , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
1348− , Defer SomeClear m -- Wiping out event firings
1349− , Defer (SomeAssignment x) m -- Updating state
1350− , Defer (SomeResetCoincidence x) m
1351− , m ~ EventM x
1352− )
1353−
1354−getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
1355−getRootSubscribed k r sub = do
1356− mSubscribed <- readIORef $ rootSubscribed r
1357− let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1358− case DMap.lookup k mSubscribed of
1359− Just subscribed -> {-# SCC "hitRoot" #-} do
1360− sln <- subscribeRootSubscribed subscribed sub
1361− occ <- getOcc
1362− return (sln, subscribed, occ)
1363− Nothing -> {-# SCC "missRoot" #-} do
1364− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1365− let !cached = rootSubscribed r
1366− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1367− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1368− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1369− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1370− writeIORef uninitRef $! uninit
1371− let !subscribed = RootSubscribed
1372− { rootSubscribedKey = k
1373− , rootSubscribedCachedSubscribed = cached
1374− , rootSubscribedOccurrence = getOcc
1375− , rootSubscribedSubscribers = subs
1376− , rootSubscribedUninit = uninit
1377− , rootSubscribedWeakSelf = weakSelf
1378−#ifdef DEBUG_NODEIDS
1379− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1380−#endif
1381− }
1382− -- If we die at the same moment that all our children die, they will
1383− -- try to clean us up but will fail because their Weak reference to us
1384− -- will also be dead. So, if we are dying, check if there are any
1385− -- children; since children don't bother cleaning themselves up if
1386− -- their parents are already dead, I don't think there's a race
1387− -- condition here. However, if there are any children, then we can
1388− -- infer that we need to clean ourselves up, so we do.
1389− finalCleanup = do
1390− cs <- readIORef $ _weakBag_children subs
1391− when (not $ IntMap.null cs) (cleanupRootSubscribed subscribed)
1392− writeIORef weakSelf =<< evaluate =<< mkWeakPtr subscribed (Just finalCleanup)
1393− 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
1394− occ <- getOcc
1395− return (sln, subscribed, occ)
1396−
1397−#ifdef USE_TEMPLATE_HASKELL
1398−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1399−#endif
1400−cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1401−cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached } = do
1402− rootSubscribedUninit self
1403− modifyIORef' cached $ DMap.delete k
1404−
1405−{-# INLINE subscribeRootSubscribed #-}
1406−subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1407−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1408−
1409−{-# INLINABLE getFanSubscribed #-}
1410−getFanSubscribed :: (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x, GCompare k) => k a -> Fan x k -> Subscriber x a -> m (WeakBagTicket, FanSubscribed x k, Maybe a)
1411−getFanSubscribed k f sub = do
1412− mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1413− case mSubscribed of
1414− Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1415− sln <- subscribeFanSubscribed k subscribed sub
1416− occ <- readIORef $ fanSubscribedOccurrence subscribed
1417− return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
1418− Nothing -> {-# SCC "missFan" #-} do
1419− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1420− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1421− s <- liftIO $ newSubscriberFan subscribedUnsafe
1422− (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
1423− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1424− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1425− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1426− occRef <- liftIO $ newIORef parentOcc
1427− when (isJust parentOcc) $ scheduleClear occRef
1428− let subscribed = FanSubscribed
1429− { fanSubscribedCachedSubscribed = fanSubscribed f
1430− , fanSubscribedOccurrence = occRef
1431− , fanSubscribedParent = subscription
1432− , fanSubscribedSubscribers = subscribersRef
1433−#ifdef DEBUG_NODEIDS
1434− , fanSubscribedNodeId = unsafeNodeId f
1435−#endif
1436− }
1437− let !self = (k, subscribed)
1438− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1439− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1440− liftIO $ writeIORef subscribedRef $! subscribed
1441− liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1442− return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1443−
1444−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1445−cleanupFanSubscribed (k, subscribed) = do
1446− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1447− let reducedSubscribers = DMap.delete k subscribers
1448− if DMap.null reducedSubscribers
1449− then do
1450− unsubscribe $ fanSubscribedParent subscribed
1451− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1452− writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1453− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1454−
1455−{-# INLINE subscribeFanSubscribed #-}
1456−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1457−subscribeFanSubscribed k subscribed sub = do
1458− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1459− case DMap.lookup k subscribers of
1460− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1461− let !self = (k, subscribed)
1462− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1463− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1464− writeIORef (fanSubscribedSubscribers subscribed) $! DMap.insertWith (error "subscribeFanSubscribed: key that we just failed to find is present - should be impossible") k (FanSubscribedChildren list self weakSelf) subscribers
1465− return sln
1466− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1467−
1468−{-# INLINABLE getSwitchSubscribed #-}
1469−getSwitchSubscribed :: (Defer (SomeHoldInit x) m, Defer (SomeAssignment x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, Defer (SomeResetCoincidence x) m, HasCurrentHeight x m, HasSpiderTimeline x, m ~ EventM x) => Switch x a -> Subscriber x a -> m (WeakBagTicket, SwitchSubscribed x a, Maybe a)
1470−getSwitchSubscribed s sub = do
1471− mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1472− case mSubscribed of
1473− Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1474− sln <- subscribeSwitchSubscribed subscribed sub
1475− occ <- readIORef $ switchSubscribedOccurrence subscribed
1476− return (sln, subscribed, occ)
1477− Nothing -> {-# SCC "missSwitch" #-} do
1478− subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1479− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1480− i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1481− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1482− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1483− wiRef <- liftIO $ newIORef wi
1484− parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1485− holdInits <- getDeferralQueue
1486− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1487− (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
1488− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1489− subscriptionRef <- liftIO $ newIORef subscription
1490− occRef <- liftIO $ newIORef parentOcc
1491− when (isJust parentOcc) $ scheduleClear occRef
1492− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1493− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1494− let !subscribed = SwitchSubscribed
1495− { switchSubscribedCachedSubscribed = switchSubscribed s
1496− , switchSubscribedOccurrence = occRef
1497− , switchSubscribedHeight = heightRef
1498− , switchSubscribedSubscribers = subs
1499− , switchSubscribedOwnInvalidator = i
1500− , switchSubscribedOwnWeakInvalidator = wiRef
1501− , switchSubscribedBehaviorParents = parentsRef
1502− , switchSubscribedParent = switchParent s
1503− , switchSubscribedCurrentParent = subscriptionRef
1504− , switchSubscribedWeakSelf = weakSelf
1505−#ifdef DEBUG_NODEIDS
1506− , switchSubscribedNodeId = unsafeNodeId s
1507−#endif
1508− }
1509− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1510− liftIO $ writeIORef subscribedRef $! subscribed
1511− liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1512− return (slnForSub, subscribed, parentOcc)
1513−
1514−cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
1515−cleanupSwitchSubscribed subscribed = do
1516− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1517− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1518− writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1519−
1520−{-# INLINE subscribeSwitchSubscribed #-}
1521−subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1522−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1523−
1524−{-# INLINABLE getCoincidenceSubscribed #-}
1525−getCoincidenceSubscribed :: forall x a m. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Coincidence x a -> Subscriber x a -> m (WeakBagTicket, CoincidenceSubscribed x a, Maybe a)
1526−getCoincidenceSubscribed c sub = do
1527− mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1528− case mSubscribed of
1529− Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1530− sln <- subscribeCoincidenceSubscribed subscribed sub
1531− occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1532− return (sln, subscribed, occ)
1533− Nothing -> {-# SCC "missCoincidence" #-} do
1534− subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1535− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1536− subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1537− (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1538− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1539− (occ, height, mInnerSubd) <- case outerOcc of
1540− Nothing -> return (Nothing, outerHeight, Nothing)
1541− Just o -> do
1542− (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
1543− return (occ, height, Just innerSubd)
1544− occRef <- liftIO $ newIORef occ
1545− when (isJust occ) $ scheduleClear occRef
1546− heightRef <- liftIO $ newIORef height
1547− innerSubdRef <- liftIO $ newIORef mInnerSubd
1548− scheduleClear innerSubdRef
1549− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1550− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1551− let subscribed = CoincidenceSubscribed
1552− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1553− , coincidenceSubscribedOccurrence = occRef
1554− , coincidenceSubscribedHeight = heightRef
1555− , coincidenceSubscribedSubscribers = subs
1556− , coincidenceSubscribedOuter = subOuter
1557− , coincidenceSubscribedOuterParent = outerSubscription
1558− , coincidenceSubscribedInnerParent = innerSubdRef
1559− , coincidenceSubscribedWeakSelf = weakSelf
1560−#ifdef DEBUG_NODEIDS
1561− , coincidenceSubscribedNodeId = unsafeNodeId c
1562−#endif
1563− }
1564− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1565− liftIO $ writeIORef subscribedRef $! subscribed
1566− liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1567− return (slnForSub, subscribed, occ)
1568−
1569−cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
1570−cleanupCoincidenceSubscribed subscribed = do
1571− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1572− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1573−
1574−{-# INLINE subscribeCoincidenceSubscribed #-}
1575−subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1576−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1577−
1578−{-# INLINE merge #-}
1579−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1580−merge d = cacheEvent (mergeCheap d)
1581−
1582−{-# INLINE mergeWithMove #-}
1583−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1584−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1585−
1586−{-# INLINE [1] mergeCheap #-}
1587−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1588−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1589− where
1590− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1591− updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1592− let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1593− (mOldSubd, newPs) <- case me of
1594− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1595− Just e -> do
1596− let s = subscriber $ return k
1597− subscription@(EventSubscription _ subd) <- subscribe e s
1598− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1599− let newParent = MergeSubscribedParent subscription
1600− liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1601− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1602− forM_ mOldSubd $ \oldSubd -> do
1603− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ unMergeSubscribedParent oldSubd
1604− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1605− return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1606− foldM f ([], oldParents) $ DMap.toList p
1607− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1608− getInitialSubscribers initialParents subscriber = do
1609− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1610− let s = subscriber $ return k
1611− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1612− height <- liftIO $ getEventSubscribedHeight parentSubd
1613− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1614− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1615− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1616− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1617− )
1618− destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1619− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1620−
1621−{-# INLINE [1] mergeCheapWithMove #-}
1622−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1623−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy DMap.size
1624− where
1625− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1626− updateMe subscriber heightBagRef oldParents p = do
1627− -- Prepare new parents for insertion
1628− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1629− subscribeParent k e = do
1630− keyRef <- liftIO $ newIORef k
1631− let s = subscriber $ liftIO $ readIORef keyRef
1632− subscription@(EventSubscription _ subd) <- subscribe e s
1633− liftIO $ do
1634− newParentHeight <- getEventSubscribedHeight subd
1635− modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1636− return $ MergeSubscribedParentWithMove subscription keyRef
1637− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1638− -- Collect old parents for deletion and update the keys of moved parents
1639− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1640− moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1641− Nothing -> do
1642− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
1643− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1644− return $ Just $ _mergeSubscribedParentWithMove_subscription parent
1645− Just toKey -> do
1646− liftIO $ writeIORef (_mergeSubscribedParentWithMove_key parent) $! toKey
1647− return Nothing
1648− toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1649− return (toDelete, applyAlways p' oldParents)
1650− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1651− getInitialSubscribers initialParents subscriber = do
1652− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1653− keyRef <- liftIO $ newIORef k
1654− let s = subscriber $ liftIO $ readIORef keyRef
1655− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1656− height <- liftIO $ getEventSubscribedHeight parentSubd
1657− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1658− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1659− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1660− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1661− )
1662− destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1663− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1664−
1665−type MergeUpdateFunc k x p s
1666− = (forall a. EventM x (k a) -> Subscriber x a)
1667− -> IORef HeightBag
1668− -> DMap k s
1669− -> p
1670− -> EventM x ([EventSubscription x], DMap k s)
1671−
1672−type MergeInitFunc k x s
1673− = DMap k (Event x)
1674− -> (forall a. EventM x (k a) -> Subscriber x a)
1675− -> EventM x (DMap k Identity, [Height], DMap k s)
1676−
1677−type MergeDestroyFunc k s
1678− = DMap k s
1679− -> IO ()
1680−
1681−{-# INLINE mergeCheap' #-}
1682−mergeCheap' :: forall k x p s. (HasSpiderTimeline x, GCompare k, PatchTarget p ~ DMap k (Event x)) => MergeInitFunc k x s -> MergeUpdateFunc k x p s -> MergeDestroyFunc k s -> (DMap k s -> Int) -> Dynamic x p -> Event x (DMap k Identity)
1683−mergeCheap' getInitialSubscribers updateFunc destroy numParents d = Event $ \sub -> do
1684− initialParents <- readBehaviorUntracked $ dynamicCurrent d
1685− accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1686− heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1687− heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
1688− parentsRef :: IORef (DMap k s) <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1689− let scheduleSelf height = scheduleMerge' height heightRef $ do
1690− m <- liftIO $ readIORef accumRef
1691− liftIO $ writeIORef accumRef $! DMap.empty
1692− --TODO: Assert that m is not empty
1693− subscriberPropagate sub m
1694− invalidateMyHeight = do
1695− oldHeight <- readIORef heightRef
1696− when (oldHeight /= invalidHeight) $ do -- If the height used to be valid, it must be invalid now; we should never have *more* heights than we have parents
1697− writeIORef heightRef $! invalidHeight
1698− subscriberInvalidateHeight sub oldHeight
1699− revalidateMyHeight = do
1700− currentHeight <- readIORef heightRef
1701− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1702− heights <- readIORef heightBagRef
1703− parents <- readIORef parentsRef
1704− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1705− case heightBagSize heights `compare` numParents parents of
1706− LT -> return ()
1707− EQ -> do
1708− let height = succHeight $ heightBagMax heights
1709− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1710− writeIORef heightRef $! height
1711− subscriberRecalculateHeight sub height
1712− GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (numParents parents) <> ") for Merge"
1713− subscriber :: forall a. EventM x (k a) -> Subscriber x a
1714− subscriber getKey = Subscriber
1715− { subscriberPropagate = \a -> do
1716− oldM <- liftIO $ readIORef accumRef
1717− k <- getKey
1718− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1719− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1720− liftIO $ writeIORef accumRef $! newM
1721− when (DMap.null oldM) $ do -- Only schedule the firing once
1722− height <- liftIO $ readIORef heightRef
1723− --TODO: assertions about height
1724− currentHeight <- getCurrentHeight
1725− when (height <= currentHeight) $ do
1726− if height /= invalidHeight
1727− then do
1728− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1729− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1730− else liftIO $ do
1731−#ifdef DEBUG_CYCLES
1732− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1733− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1734− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1735−#else
1736− let cycleInfo = ""
1737−#endif
1738− error $ "Causality loop found" <> cycleInfo
1739− scheduleSelf height
1740− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1741− modifyIORef' heightBagRef $ heightBagRemove old
1742− invalidateMyHeight
1743− , subscriberRecalculateHeight = \new -> do
1744− modifyIORef' heightBagRef $ heightBagAdd new
1745− revalidateMyHeight
1746− }
1747− let updateMe x = do
1748− oldParents <- liftIO $ readIORef parentsRef
1749− (subscriptionsToKill, newParents) <- updateFunc subscriber heightBagRef oldParents x
1750− liftIO $ writeIORef parentsRef $! newParents
1751− return subscriptionsToKill
1752− (dm, heights, initialParentState) <- getInitialSubscribers initialParents subscriber
1753− let myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1754− myHeight = if invalidHeight `elem` heights
1755− then invalidHeight
1756− else succHeight $ heightBagMax myHeightBag
1757− currentHeight <- getCurrentHeight
1758− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1759− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1760− else (Nothing, dm)
1761− unless (DMap.null accum) $ do
1762− scheduleSelf myHeight
1763− occRef <- liftIO $ newIORef occ
1764− when (isJust occ) $ scheduleClear occRef
1765− liftIO $ writeIORef accumRef $! accum
1766− liftIO $ writeIORef heightRef $! myHeight
1767− liftIO $ writeIORef heightBagRef $! myHeightBag
1768− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1769− liftIO $ writeIORef parentsRef $! initialParentState
1770− defer $ SomeMergeInit $ do
1771− let s = Subscriber
1772− { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1773− tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1774− defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1775− , subscriberInvalidateHeight = \_ -> return ()
1776− , subscriberRecalculateHeight = \_ -> return ()
1777− }
1778− (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1779− forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1780− -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
1781− -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
1782− -- its subscription.
1783− liftIO $ writeIORef changeSubdRef (s, changeSubscription)
1784− let unsubscribeAll = destroy =<< readIORef parentsRef
1785− return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parentsRef, changeSubdRef)
1786− , occ
1787− )
1788−
1789−newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
1790−
1791−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
1792−fan e =
1793− let f = Fan
1794− { fanParent = e
1795− , fanSubscribed = unsafeNewIORef e Nothing
1796− }
1797− in EventSelector $ \k -> eventFan k f
1798−
1799−runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
1800−runHoldInits holdInitRef mergeInitRef = do
1801− holdInits <- liftIO $ readIORef holdInitRef
1802− mergeInits <- liftIO $ readIORef mergeInitRef
1803− unless (null holdInits && null mergeInits) $ do
1804− liftIO $ writeIORef holdInitRef []
1805− liftIO $ writeIORef mergeInitRef []
1806− mapM_ initHold holdInits
1807− mapM_ unSomeMergeInit mergeInits
1808− runHoldInits holdInitRef mergeInitRef
1809−
1810−initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
1811−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1812−
1813−newEventEnv :: IO (EventEnv x)
1814−newEventEnv = do
1815− toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1816− holdInitRef <- newIORef []
1817− mergeUpdateRef <- newIORef []
1818− mergeInitRef <- newIORef []
1819− heightRef <- newIORef zeroHeight
1820− toClearRef <- newIORef []
1821− toClearRootRef <- newIORef []
1822− coincidenceInfosRef <- newIORef []
1823− delayedRef <- newIORef IntMap.empty
1824− return $ EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1825−
1826−clearEventEnv :: EventEnv x -> IO ()
1827−clearEventEnv (EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
1828− writeIORef toAssignRef []
1829− writeIORef holdInitRef []
1830− writeIORef mergeUpdateRef []
1831− writeIORef mergeInitRef []
1832− writeIORef heightRef zeroHeight
1833− writeIORef toClearRef []
1834− writeIORef toClearRootRef []
1835− writeIORef coincidenceInfosRef []
1836− writeIORef delayedRef IntMap.empty
1837−
1838−-- | Run an event action outside of a frame
1839−runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1840−runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
1841− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
1842− let go = do
1843− result <- a
1844− runHoldInits (eventEnvHoldInits env) (eventEnvMergeInits env) -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1845− return result
1846− result <- runEventM go
1847− toClear <- readIORef $ eventEnvClears env
1848− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1849− toClearRoot <- readIORef $ eventEnvRootClears env
1850− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1851− toAssign <- readIORef $ eventEnvAssignments env
1852− toReconnectRef <- newIORef []
1853− coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
1854− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1855− writeIORef vRef v
1856− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1857− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1858− mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1859− writeIORef (eventEnvMergeUpdates env) []
1860− when debugPropagate $ putStrLn "Updating merges"
1861− mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
1862− when debugPropagate $ putStrLn "Updating merges done"
1863− toReconnect <- readIORef toReconnectRef
1864− clearEventEnv env
1865− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1866− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1867− wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1868− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1869− finalize wi
1870− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1871− wi' <- mkWeakPtrWithDebug i "wi'"
1872− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1873− writeIORef (switchSubscribedBehaviorParents subscribed) []
1874− writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1875− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
1876− runEventM $ runHoldInits (eventEnvHoldInits env) (eventEnvMergeInits 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
1877− --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1878− sub <- newSubscriberSwitch subscribed
1879− subscription <- runReaderT (unSpiderHost (runFrame ({-# SCC "subscribeSwitch" #-} subscribe e sub))) spiderTimeline --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
1880− {-
1881− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1882− liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1883− EventRoot _ -> "EventRoot"
1884− EventNever -> "EventNever"
1885− _ -> "something else"
1886− -}
1887− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1888− return oldSubscription
1889− liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
1890− liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
1891− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1892− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1893− parentHeight <- getEventSubscribedHeight subd'
1894− myHeight <- readIORef $ switchSubscribedHeight subscribed
1895− when (parentHeight /= myHeight) $ do
1896− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1897− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1898− mapM_ _someMergeUpdate_invalidateHeight mergeUpdates --TODO: In addition to when the patch is completely empty, we should also not run this if it has some Nothing values, but none of them have actually had any effect; potentially, we could even check for Just values with no effect (e.g. by comparing their IORefs and ignoring them if they are unchanged); actually, we could just check if the new height is different
1899− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1900− unsubscribe subscription
1901− mapM_ invalidateCoincidenceHeight mcs
1902− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1903− mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
1904− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1905− height <- calculateSwitchHeight subscribed
1906− updateSwitchHeight height subscribed
1907− return result
1908−
1909−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1910−
1911−{-# INLINE zeroHeight #-}
1912−zeroHeight :: Height
1913−zeroHeight = Height 0
1914−
1915−{-# INLINE invalidHeight #-}
1916−invalidHeight :: Height
1917−invalidHeight = Height (-1000)
1918−
1919−#ifdef DEBUG_CYCLES
1920−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1921−{-# INLINE invalidHeightBeingTraversed #-}
1922−invalidHeightBeingTraversed :: Height
1923−invalidHeightBeingTraversed = Height (-1001)
1924−#endif
1925−
1926−{-# INLINE succHeight #-}
1927−succHeight :: Height -> Height
1928−succHeight h@(Height a) =
1929− if h == invalidHeight
1930− then invalidHeight
1931− else Height $ succ a
1932−
1933−invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1934−invalidateCoincidenceHeight subscribed = do
1935− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1936− when (oldHeight /= invalidHeight) $ do
1937− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1938− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1939−
1940−updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
1941−updateSwitchHeight new subscribed = do
1942− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1943− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1944− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1945− writeIORef (switchSubscribedHeight subscribed) $! new
1946− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1947−
1948−recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1949−recalculateCoincidenceHeight subscribed = do
1950− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1951− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1952− height <- calculateCoincidenceHeight subscribed
1953− when (height /= invalidHeight) $ do
1954− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1955− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1956−
1957−calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
1958−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1959−
1960−calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
1961−calculateCoincidenceHeight subscribed = do
1962− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1963− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1964− return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1965−
1966−data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
1967−
1968−invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
1969−invalidate toReconnectRef wis = do
1970− forM_ wis $ \wi -> do
1971− mi <- deRefWeak wi
1972− case mi of
1973− Nothing -> do
1974− traceInvalidate "invalidate Dead"
1975− return () --TODO: Should we clean this up here?
1976− Just i -> do
1977− finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1978− case i of
1979− InvalidatorPull p -> do
1980− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1981− mVal <- readIORef $ pullValue p
1982− forM_ mVal $ \val -> do
1983− writeIORef (pullValue p) Nothing
1984− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1985− InvalidatorSwitch subscribed -> do
1986− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1987− modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1988− 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
1989−
1990−--------------------------------------------------------------------------------
1991−-- Reflex integration
1992−--------------------------------------------------------------------------------
1993−
1994−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1995−-- When we're specializing, we include this from Reflex.Class instead, because
1996−-- it needs access to these types; when we're not, we want them here, so our
1997−-- instance isn't an orphan instance.
1998−#include "SpiderTimeline.include.hs"
1999−#include "SpiderTimelineHost.include.hs"
2000−#endif
2001−
2002−unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
2003−unsafeNewSpiderTimelineEnv = do
2004− lock <- newMVar ()
2005− env <- newEventEnv
2006−#ifdef DEBUG
2007− depthRef <- newIORef 0
2008−#endif
2009− return $ SpiderTimelineEnv
2010− { _spiderTimeline_lock = lock
2011− , _spiderTimeline_eventEnv = env
2012−#ifdef DEBUG
2013− , _spiderTimeline_depth = depthRef
2014−#endif
2015− }
2016−
2017−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2018−-- | Create a new SpiderTimelineEnv
2019−newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2020−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2021−
2022−data LocalSpiderTimeline x s
2023−
2024−instance Reifies s (SpiderTimelineEnv x) =>
2025− HasSpiderTimeline (LocalSpiderTimeline x s) where
2026− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2027−
2028−localSpiderTimeline
2029− :: Proxy s
2030− -> SpiderTimelineEnv x
2031− -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2032−localSpiderTimeline _ = unsafeCoerce
2033−
2034−-- | Pass a new timeline to the given function.
2035−withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2036−withSpiderTimeline k = do
2037− env <- unsafeNewSpiderTimelineEnv
2038− reify env $ \s -> k $ localSpiderTimeline s env
2039−#endif
2040−
2041−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2042−
2043−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2044−
2045−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2046−instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2047− {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2048− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2049− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2050− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2051− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2052− type PullM (SpiderTimeline x) = SpiderPullM x
2053− type PushM (SpiderTimeline x) = SpiderPushM x
2054− {-# INLINABLE never #-}
2055− never = SpiderEvent eventNever
2056− {-# INLINABLE constant #-}
2057− constant = SpiderBehavior . behaviorConst
2058− {-# INLINE push #-}
2059− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2060− {-# INLINE pushCheap #-}
2061− pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2062− {-# INLINABLE pull #-}
2063− pull = SpiderBehavior . pull . coerce
2064− {-# INLINABLE merge #-}
2065− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2066− {-# INLINABLE fan #-}
2067− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2068− {-# INLINABLE switch #-}
2069− switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2070− {-# INLINABLE coincidence #-}
2071− coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
2072− {-# INLINABLE current #-}
2073− current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2074− {-# INLINABLE updated #-}
2075− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2076− {-# INLINABLE unsafeBuildDynamic #-}
2077− unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
2078− {-# INLINABLE unsafeBuildIncremental #-}
2079− unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
2080− {-# INLINABLE mergeIncremental #-}
2081− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2082− {-# INLINABLE mergeIncrementalWithMove #-}
2083− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2084− {-# INLINABLE currentIncremental #-}
2085− currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2086− {-# INLINABLE updatedIncremental #-}
2087− updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
2088− {-# INLINABLE incrementalToDynamic #-}
2089− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
2090− c <- readBehaviorUntracked $ dynamicCurrent i
2091− return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2092− eventCoercion Coercion = Coercion
2093− behaviorCoercion Coercion = Coercion
2094− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2095−#endif
2096−
2097−data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
2098−
2099−data SpiderEventHandle x a = SpiderEventHandle
2100− { spiderEventHandleSubscription :: EventSubscription x
2101− , spiderEventHandleValue :: IORef (Maybe a)
2102− }
2103−
2104−instance MonadRef (EventM x) where
2105− type Ref (EventM x) = Ref IO
2106− {-# INLINABLE newRef #-}
2107− {-# INLINABLE readRef #-}
2108− {-# INLINABLE writeRef #-}
2109− newRef = liftIO . newRef
2110− readRef = liftIO . readRef
2111− writeRef r a = liftIO $ writeRef r a
2112−
2113−instance MonadAtomicRef (EventM x) where
2114− {-# INLINABLE atomicModifyRef #-}
2115− atomicModifyRef r f = liftIO $ atomicModifyRef r f
2116−
2117−-- | The monad for actions that manipulate a Spider timeline identified by @x@
2118−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimelineEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2119−
2120−instance Monad (SpiderHost x) where
2121− {-# INLINABLE (>>=) #-}
2122− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2123− {-# INLINABLE (>>) #-}
2124− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2125− {-# INLINABLE return #-}
2126− return x = SpiderHost $ return x
2127− {-# INLINABLE fail #-}
2128− fail s = SpiderHost $ fail s
2129−
2130−-- | Run an action affecting the global Spider timeline; this will be guarded by
2131−-- a mutex for that timeline
2132−runSpiderHost :: SpiderHost Global a -> IO a
2133−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimelineEnv
2134−
2135−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
2136−-- | Run an action affecting a given Spider timeline; this will be guarded by a
2137−-- mutex for that timeline
2138−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2139−runSpiderHostForTimeline (SpiderHost a) = runReaderT a
2140−#endif
2141−
2142−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2143−
2144−instance Monad (SpiderHostFrame x) where
2145− {-# INLINABLE (>>=) #-}
2146− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2147− {-# INLINABLE (>>) #-}
2148− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2149− {-# INLINABLE return #-}
2150− return x = SpiderHostFrame $ return x
2151− {-# INLINABLE fail #-}
2152− fail s = SpiderHostFrame $ fail s
2153−
2154−newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
2155−newEventWithTriggerIO f = do
2156− es <- newFanEventWithTriggerIO $ \Refl -> f
2157− return $ select es Refl
2158−
2159−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger x a -> IO (IO ())) -> IO (EventSelector x k)
2160−newFanEventWithTriggerIO f = do
2161− occRef <- newIORef DMap.empty
2162− subscribedRef <- newIORef DMap.empty
2163− let !r = Root
2164− { rootOccurrence = occRef
2165− , rootSubscribed = subscribedRef
2166− , rootInit = f
2167− }
2168− return $ EventSelector $ \k -> eventRoot k r
2169−
2170−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2171−
2172−instance MonadRef (SpiderHost x) where
2173− type Ref (SpiderHost x) = Ref IO
2174− newRef = SpiderHost . newRef
2175− readRef = SpiderHost . readRef
2176− writeRef r = SpiderHost . writeRef r
2177−
2178−instance MonadAtomicRef (SpiderHost x) where
2179− atomicModifyRef r = SpiderHost . atomicModifyRef r
2180−
2181−instance MonadRef (SpiderHostFrame x) where
2182− type Ref (SpiderHostFrame x) = Ref IO
2183− newRef = SpiderHostFrame . newRef
2184− readRef = SpiderHostFrame . readRef
2185− writeRef r = SpiderHostFrame . writeRef r
2186−
2187−instance MonadAtomicRef (SpiderHostFrame x) where
2188− atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2189−
2190−instance PrimMonad (SpiderHostFrame x) where
2191− type PrimState (SpiderHostFrame x) = PrimState IO
2192− primitive = SpiderHostFrame . EventM . primitive
src/Reflex/Spider/SpiderTimeline.include.hsdeleted−122
@@ -1,122 +0,0 @@
1−-- | Designates the default, global Spider timeline
2−data SpiderTimeline x
3−type role SpiderTimeline nominal
4−
5−-- | The default, global Spider environment
6−type Spider = SpiderTimeline Global
7−
8−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (EventM x) where
9− {-# INLINABLE sample #-}
10− sample (SpiderBehavior b) = readBehaviorUntracked b
11−
12−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (EventM x) where
13− {-# INLINABLE hold #-}
14− hold = holdSpiderEventM
15− {-# INLINABLE holdDyn #-}
16− holdDyn = holdDynSpiderEventM
17− {-# INLINABLE holdIncremental #-}
18− holdIncremental = holdIncrementalSpiderEventM
19−
20−instance (
21−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22− x ~ Global
23−#endif
24− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25− {-# INLINABLE sample #-}
26− sample = coerce . readBehaviorTracked . unSpiderBehavior
27−
28−instance (
29−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
30− x ~ Global
31−#endif
32− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
33− {-# INLINABLE sample #-}
34− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
35−
36−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderPushM x) where
37− {-# INLINABLE hold #-}
38− hold v0 e = Reflex.Class.current <$> Reflex.Class.holdDyn v0 e
39− {-# INLINABLE holdDyn #-}
40− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce e
41− {-# INLINABLE holdIncremental #-}
42− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . dynamicHold <$> Reflex.Spider.Internal.hold v0 e
43−
44−instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
45− {-# INLINE return #-}
46− return = pure
47− {-# INLINE (>>=) #-}
48− x >>= f = SpiderDynamic $ dynamicDynIdentity $ newJoinDyn $ newMapDyn (unSpiderDynamic . f) $ unSpiderDynamic x
49− {-# INLINE (>>) #-}
50− _ >> y = y
51− {-# INLINE fail #-}
52− fail _ = error "Dynamic does not support 'fail'"
53−
54−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
55−newJoinDyn d =
56− let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
57− eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
58− eInner = Reflex.Spider.Internal.switch $ dynamicUpdated <$> dynamicCurrent d
59− eBoth = Reflex.Spider.Internal.coincidence $ dynamicUpdated . runIdentity <$> dynamicUpdated d
60− v' = unSpiderEvent $ Reflex.Class.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
61− in unsafeDyn readV0 v'
62−
63−instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
64− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
65−
66−instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
67− pure = SpiderDynamic . dynamicConst
68− SpiderDynamic a <*> SpiderDynamic b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith ($) a b
69− _ *> b = b
70− a <* _ = a
71−
72−holdSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Behavior (SpiderTimeline x) a)
73−holdSpiderEventM v0 e = fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
74−
75−holdDynSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
76−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
77−
78−holdIncrementalSpiderEventM :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Reflex.Class.Event (SpiderTimeline x) p -> EventM x (Reflex.Class.Incremental (SpiderTimeline x) p)
79−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
80−
81−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHost x) where
82− {-# INLINABLE hold #-}
83− hold v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.hold v0 e
84− {-# INLINABLE holdDyn #-}
85− holdDyn v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdDyn v0 e
86− {-# INLINABLE holdIncremental #-}
87− holdIncremental v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdIncremental v0 e
88−
89−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
90− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
91−
92−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
93− {-# INLINABLE hold #-}
94− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
95− {-# INLINABLE holdDyn #-}
96− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
97− {-# INLINABLE holdIncremental #-}
98− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
99−
100−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
101− {-# INLINABLE sample #-}
102− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
103−
104−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
105− {-# INLINABLE sample #-}
106− sample = Reflex.Spider.Internal.ReadPhase . Reflex.Class.sample
107−
108−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
109− {-# INLINABLE hold #-}
110− hold v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.hold v0 e
111− {-# INLINABLE holdDyn #-}
112− holdDyn v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdDyn v0 e
113− {-# INLINABLE holdIncremental #-}
114− holdIncremental v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdIncremental v0 e
115−
116−--------------------------------------------------------------------------------
117−-- Deprecated items
118−--------------------------------------------------------------------------------
119−
120−-- | 'SpiderEnv' is the old name for 'SpiderTimeline'
121−{-# DEPRECATED SpiderEnv "Use 'SpiderTimelineEnv' instead" #-}
122−type SpiderEnv = SpiderTimeline
src/Reflex/Spider/SpiderTimelineHost.include.hsdeleted−57
@@ -1,57 +0,0 @@
1−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2− {-# INLINABLE subscribeEvent #-}
3− subscribeEvent e = SpiderHostFrame $ do
4− --TODO: Unsubscribe eventually (manually and/or with weak ref)
5− val <- liftIO $ newIORef Nothing
6− subscription <- subscribe (unSpiderEvent e) $ Subscriber
7− { subscriberPropagate = \a -> do
8− liftIO $ writeIORef val $ Just a
9− scheduleClear val
10− , subscriberInvalidateHeight = \_ -> return ()
11− , subscriberRecalculateHeight = \_ -> return ()
12− }
13− return $ SpiderEventHandle
14− { spiderEventHandleSubscription = subscription
15− , spiderEventHandleValue = val
16− }
17−
18−instance HasSpiderTimeline x => Reflex.Host.Class.ReflexHost (SpiderTimeline x) where
19− type EventTrigger (SpiderTimeline x) = RootTrigger x
20− type EventHandle (SpiderTimeline x) = SpiderEventHandle x
21− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
22−
23−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReadEvent (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
24− {-# NOINLINE readEvent #-}
25− readEvent h = Reflex.Spider.Internal.ReadPhase $ fmap (fmap return) $ liftIO $ do
26− result <- readIORef $ spiderEventHandleValue h
27− touch h
28− return result
29−
30−instance (
31−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
32− x ~ Global
33−#endif
34− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
35− newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
36− newFanEventWithTrigger f = SpiderHost $ lift $ do
37− es <- newFanEventWithTriggerIO f
38− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
39−
40−instance (
41−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
42− x ~ Global
43−#endif
44− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
45− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
46− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
47− es <- newFanEventWithTriggerIO f
48− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
49−
50−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
51− {-# INLINABLE subscribeEvent #-}
52− subscribeEvent = runFrame . runSpiderHostFrame . Reflex.Host.Class.subscribeEvent
53−
54−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
55− type ReadPhase (SpiderHost x) = Reflex.Spider.Internal.ReadPhase x
56− fireEventsAndRead es (Reflex.Spider.Internal.ReadPhase a) = run es a
57− runHostFrame = runFrame . runSpiderHostFrame
src/Reflex/TriggerEvent/Base.hsdeleted−129
@@ -1,129 +0,0 @@
1−-- | This module defines 'TriggerEventT', the standard implementation of
2−-- 'TriggerEvent'.
3−{-# LANGUAGE DeriveFunctor #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE TypeFamilies #-}
8−module Reflex.TriggerEvent.Base
9− ( TriggerEventT (..)
10− , runTriggerEventT
11− , askEvents
12− , TriggerInvocation (..)
13− , EventTriggerRef (..)
14− ) where
15−
16−import Control.Concurrent
17−import Control.Monad.Exception
18−import Control.Monad.Primitive
19−import Control.Monad.Reader
20−import Control.Monad.Ref
21−import Data.Coerce
22−import Data.Dependent.Sum
23−import Data.IORef
24−import Reflex.Class
25−import Reflex.Host.Class
26−import Reflex.PerformEvent.Class
27−import Reflex.PostBuild.Class
28−import Reflex.TriggerEvent.Class
29−
30−-- | A value with which to fire an 'Event', as well as a callback to invoke
31−-- after its propagation has completed.
32−data TriggerInvocation a = TriggerInvocation a (IO ())
33−
34−-- | A reference to an 'EventTrigger' suitable for firing with 'TriggerEventT'.
35−newtype EventTriggerRef t a = EventTriggerRef { unEventTriggerRef :: IORef (Maybe (EventTrigger t a)) }
36−
37−-- | A basic implementation of 'TriggerEvent'.
38−newtype TriggerEventT t m a = TriggerEventT { unTriggerEventT :: ReaderT (Chan [DSum (EventTriggerRef t) TriggerInvocation]) m a }
39− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
40−
41−-- | Run a 'TriggerEventT' action. The argument should be a 'Chan' into which
42−-- 'TriggerInvocation's can be passed; it is expected that some other thread
43−-- will be responsible for popping values out of the 'Chan' and firing their
44−-- 'EventTrigger's.
45−runTriggerEventT :: TriggerEventT t m a -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
46−runTriggerEventT = runReaderT . unTriggerEventT
47−
48−instance MonadTrans (TriggerEventT t) where
49− {-# INLINABLE lift #-}
50− lift = TriggerEventT . lift
51−
52−instance PrimMonad m => PrimMonad (TriggerEventT t m) where
53− type PrimState (TriggerEventT t m) = PrimState m
54− {-# INLINABLE primitive #-}
55− primitive = lift . primitive
56−
57−instance PerformEvent t m => PerformEvent t (TriggerEventT t m) where
58− type Performable (TriggerEventT t m) = Performable m
59− {-# INLINABLE performEvent_ #-}
60− performEvent_ e = lift $ performEvent_ e
61− {-# INLINABLE performEvent #-}
62− performEvent e = lift $ performEvent e
63−
64−instance PostBuild t m => PostBuild t (TriggerEventT t m) where
65− {-# INLINABLE getPostBuild #-}
66− getPostBuild = lift getPostBuild
67−
68−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (TriggerEventT t m) where
69− {-# INLINABLE newEventWithTrigger #-}
70− newEventWithTrigger = lift . newEventWithTrigger
71− {-# INLINABLE newFanEventWithTrigger #-}
72− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
73−
74−instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (TriggerEventT t m) where
75− {-# INLINABLE newTriggerEvent #-}
76− newTriggerEvent = do
77− (e, t) <- newTriggerEventWithOnComplete
78− return (e, \a -> t a $ return ())
79− {-# INLINABLE newTriggerEventWithOnComplete #-}
80− newTriggerEventWithOnComplete = do
81− events <- askEvents
82− (eResult, reResultTrigger) <- lift newEventWithTriggerRef
83− return . (,) eResult $ \a cb ->
84− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
85− {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
86− newEventWithLazyTriggerWithOnComplete f = do
87− events <- askEvents
88− lift . newEventWithTrigger $ \t ->
89− f $ \a cb -> do
90− reResultTrigger <- newRef $ Just t
91− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
92−
93−instance MonadRef m => MonadRef (TriggerEventT t m) where
94− type Ref (TriggerEventT t m) = Ref m
95− {-# INLINABLE newRef #-}
96− newRef = lift . newRef
97− {-# INLINABLE readRef #-}
98− readRef = lift . readRef
99− {-# INLINABLE writeRef #-}
100− writeRef r = lift . writeRef r
101−
102−instance MonadAtomicRef m => MonadAtomicRef (TriggerEventT t m) where
103− {-# INLINABLE atomicModifyRef #-}
104− atomicModifyRef r = lift . atomicModifyRef r
105−
106−instance MonadSample t m => MonadSample t (TriggerEventT t m) where
107− {-# INLINABLE sample #-}
108− sample = lift . sample
109−
110−instance MonadHold t m => MonadHold t (TriggerEventT t m) where
111− {-# INLINABLE hold #-}
112− hold v0 v' = lift $ hold v0 v'
113− {-# INLINABLE holdDyn #-}
114− holdDyn v0 v' = lift $ holdDyn v0 v'
115− {-# INLINABLE holdIncremental #-}
116− holdIncremental v0 v' = lift $ holdIncremental v0 v'
117−
118−instance MonadAdjust t m => MonadAdjust t (TriggerEventT t m) where
119− {-# INLINABLE runWithReplace #-}
120− runWithReplace (TriggerEventT a0) a' = TriggerEventT $ runWithReplace a0 (coerceEvent a')
121− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
122− traverseDMapWithKeyWithAdjust f dm0 dm' = TriggerEventT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
123− {-# INLINABLE traverseDMapWithKeyWithAdjustWithMove #-}
124− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = TriggerEventT $ traverseDMapWithKeyWithAdjustWithMove (coerce . f) dm0 dm'
125−
126−-- | Retrieve the current 'Chan'; event trigger invocations pushed into it will
127−-- be fired.
128−askEvents :: Monad m => TriggerEventT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
129−askEvents = TriggerEventT ask
src/Reflex/TriggerEvent/Class.hsdeleted−38
@@ -1,38 +0,0 @@
1−-- | This module defines 'TriggerEvent', which describes actions that may create
2−-- new 'Event's that can be triggered from 'IO'.
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE UndecidableInstances #-}
7−module Reflex.TriggerEvent.Class
8− ( TriggerEvent (..)
9− ) where
10−
11−import Reflex.Class
12−
13−import Control.Monad.Reader
14−
15−--TODO: Shouldn't have IO hard-coded
16−-- | 'TriggerEvent' represents actions that can create 'Event's that can be
17−-- triggered by 'IO' actions.
18−class Monad m => TriggerEvent t m | m -> t where
19− -- | Create a triggerable 'Event'. Whenever the resulting function is called,
20− -- the resulting 'Event' will fire at some point in the future. Note that
21− -- this may not be synchronous.
22− newTriggerEvent :: m (Event t a, a -> IO ())
23− -- | Like 'newTriggerEvent', but the callback itself takes another callback,
24− -- to be invoked once the requested 'Event' occurrence has finished firing.
25− -- This allows synchronous operation.
26− newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
27− -- | Like 'newTriggerEventWithOnComplete', but with setup and teardown. This
28− -- relatively complex type signature allows any external listeners to be
29− -- subscribed lazily and then removed whenever the returned 'Event' is no
30− -- longer being listened to. Note that the setup/teardown may happen multiple
31− -- times, and there is no guarantee that the teardown will be executed
32− -- promptly, or even at all, in the case of program termination.
33− newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
34−
35−instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
36− newTriggerEvent = lift newTriggerEvent
37− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
38− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
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/EventWriterT.hsdeleted−61
@@ -1,61 +0,0 @@
1−{-# LANGUAGE FlexibleContexts, GADTs #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import Data.Dependent.Sum
8−import Data.IORef
9−import Data.List
10−import Data.Functor.Identity
11−
12−import Reflex
13−import Reflex.EventWriter
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
16−
17−import System.Exit (exitFailure)
18−
19−main :: IO ()
20−main = do
21− (Just out1, Nothing) <- runApp app1
22− print out1
23− (Just out2, Just (out3, out4)) <- runApp app2
24− print out2
25− print out3
26− print out4
27− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
28−
29−runApp :: (Show a, t ~ SpiderTimeline Global, m ~ SpiderHost Global)
30− => (Event t () -> EventWriterT t [a] (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
31− -> IO (Maybe [a], Maybe ([a], [a]))
32−runApp x = runSpiderHost $ do
33− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
34− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runEventWriterT (x pulseE)
35− hnd <- subscribeEvent e
36− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
37− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
38− let readPhase = do
39− mGetValue <- readEvent hnd
40− case mGetValue of
41− Nothing -> return Nothing
42− Just getValue -> fmap Just getValue
43− mactuate <- readRef actuateRef
44− mpulse <- readRef pulseTriggerRef
45− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
46− _ <- fire [actuate :=> Identity ()] $ return ()
47− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
48− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
49− return (a,b)
50− return (out1, out2)
51−
52−app1 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
53−app1 e = do
54− forM_ [1..10] $ \i -> tellEvent ([i] <$ e)
55− return Nothing
56−
57−app2 :: (Reflex t, Ref m ~ IORef, EventWriter t [Int] m, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
58−app2 e = do
59− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
60− forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e)) $ ffor pulse $ \_ -> tellEvent ([i+1] <$ e)
61− return (Just pulseTriggerRef)
test/GC.hsdeleted−95
@@ -1,95 +0,0 @@
1−{-# LANGUAGE OverloadedStrings, FlexibleContexts, GADTs, TypeFamilies, RankNTypes, ScopedTypeVariables #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import Data.Align
8−import Data.Dependent.Map (DMap)
9−import qualified Data.Dependent.Map as DMap
10−import Data.Dependent.Sum
11−import Data.Functor.Identity
12−import Data.GADT.Compare
13−import Data.IORef
14−import Data.Semigroup
15−import Data.These
16−
17−import Data.Functor.Misc
18−import Reflex.Patch
19−
20−import qualified Reflex.Spider.Internal as S
21−import qualified Reflex.Host.Class as Host
22−
23−import System.Mem
24−import System.Exit
25−
26−main :: IO ()
27−main = do
28− ref <- newIORef Nothing
29− hostPerf ref
30− readIORef ref >>= maybe exitFailure (putStrLn . ("Result " <>) . show)
31−
32−hostPerf :: IORef (Maybe Int) -> IO ()
33−hostPerf ref = S.runSpiderHost $ do
34− ---- body
35− liftIO $ putStrLn "#creating triggers"
36− (response', responseTrigger) <- Host.newEventWithTriggerRef
37− (eadd', addTriggerRef) <- Host.newEventWithTriggerRef
38− let response = S.unSpiderEvent response'
39− eadd = S.unSpiderEvent eadd'
40− liftIO $ putStrLn "#creating event graph"
41− eventToPerform <- Host.runHostFrame $ do
42− (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
43− <- S.SpiderHostFrame
44− $ fmap ( S.merge
45− . S.dynamicHold)
46− $ S.hold DMap.empty
47− -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
48− $ (\e -> S.Event $ \sub -> do
49− (s, o) <- S.subscribeAndRead e $ sub
50− { S.subscriberPropagate = S.subscriberPropagate sub
51− }
52− return (s, o))
53− $ runIdentity <$> S.select
54− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
55− (WrapArg Request)
56− return $ alignWith (mergeThese (<>))
57− (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
58− liftIO $ putStrLn "#eadd fired"
59− return $ PatchDMap $ DMap.singleton (Const2 (1 :: Int)) $ ComposeMaybe $ Just
60− $ S.pushCheap (return . Just . DMap.singleton Action . (\_ -> liftIO (writeIORef ref (Just 1)))) $ eadd)
61− (flip S.pushCheap reqMap $ \m -> return $ Just $ mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m)
62− ---- epilogue
63− eventToPerformHandle <- Host.subscribeEvent (S.SpiderEvent eventToPerform)
64− liftIO $ putStrLn "#performing GC" >> performMajorGC
65− liftIO $ putStrLn "#attempting to fire eadd"
66− mAddTrigger <- readRef addTriggerRef
67− forM_ mAddTrigger $ \t -> replicateM_ 2 $ do
68− liftIO $ putStrLn "#firing eadd"
69− mToPerform <- Host.fireEventsAndRead [t :=> Identity ()] $ sequence =<< Host.readEvent eventToPerformHandle
70− case mToPerform of
71− Nothing -> return ()
72− Just toPerform -> do
73− responses <- Host.runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
74− mrt <- readRef responseTrigger
75− let followupEventTriggers = case mrt of
76− Just rt -> [rt :=> Identity responses]
77− Nothing -> []
78− Host.fireEventsAndRead followupEventTriggers $ return ()
79− return ()
80− return ()
81−
82−data Tell a where
83− Action :: Tell ()
84− Request :: Tell (PatchDMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) (S.Event S.Global))
85−
86−instance GEq Tell where
87− geq Action Action = Just Refl
88− geq Request Request = Just Refl
89− geq _ _ = Nothing
90−
91−instance GCompare Tell where
92− gcompare Action Action = GEQ
93− gcompare Request Request = GEQ
94− gcompare Action Request = GLT
95− gcompare Request Action = GGT
test/Reflex/Bench/Focused.hsdeleted−369
@@ -1,369 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE RecursiveDo #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE TypeSynonymInstances #-}
11−
12−module Reflex.Bench.Focused where
13−
14−import Reflex
15−import Reflex.TestPlan
16−
17−import Control.Applicative
18−import Control.Monad
19−import Control.Monad.Fix
20−import Control.Monad.Identity
21−import Data.Traversable (for, traverse)
22−
23−import qualified Data.Dependent.Map as DMap
24−import Data.Map (Map)
25−import qualified Data.Map as Map
26−import Data.Maybe
27−
28−import Data.Functor.Misc
29−
30−import Data.List
31−import Data.List.Split
32−import Data.Tuple
33−
34−import Data.Monoid
35−import Data.Word
36−
37−import Control.DeepSeq
38−
39−import Prelude hiding (concat, sum)
40−
41−
42−mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
43−mergeTree n es | length es <= n = sum' es
44− | otherwise = mergeTree n subTrees
45− where
46− merges = chunksOf n es
47− subTrees = map sum' merges
48− sum' = fmap sum . mconcat . fmap (fmap (:[]) )
49−
50−mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51−mergeListDyn = mconcat . fmap (fmap pure)
52−
53−mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
54−mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55− | otherwise = mergeTreeDyn n $ fmap sumDyns merges
56− where
57− merges = chunksOf n dyns
58− sumDyns = fmap sum . mergeListDyn
59−
60−
61−
62−-- N events all firing for N frames
63−continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
64−continuous n frames = for [1..n] $ \i -> plan $ (, i) <$> [1..frames]
65−
66−-- Fire N events once every P frames for total N frames (interspersed evenly)
67−occasional :: TestPlan t m => Word -> Word -> Word -> m [Event t Word]
68−occasional n period frames = traverse plan $ zipWith occs [1..] phases
69− where
70− phases = take (fromIntegral n) $ concat (repeat [1..period])
71− occs i phase = (, i) <$> [1, phase+1..frames]
72−
73−
74−
75−-- Event which never fires (but isn't 'never')
76−never' :: TestPlan t m => m (Event t Word)
77−never' = plan []
78−
79−
80−-- Event which fires once on first frame
81−event :: TestPlan t m => m (Event t Word)
82−event = plan [(1, 0)]
83−
84−-- Event which fires constantly over N frames
85−events :: TestPlan t m => Word -> m (Event t Word)
86−events n = plan $ (\i -> (i, i)) <$> [1..n]
87−
88−
89−-- N events all originating from one event
90−fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
91−fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
92−
93−
94−-- Make a fan using the value in the event converting it to a singleton Map
95−-- use a fan of size n (key is the value mod n)
96−fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
97−fanMerge n e = leftmost $ s <$> [0..n - 1] where
98− s k = select f (Const2 k)
99− f = fan (toMap <$> e)
100− toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
101−
102−-- Dumb version of the above using simple fmapMaybe to filter
103−fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
104−fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
105− s k = flip fmapMaybe e $ \j -> if k == j `mod` n
106− then Just k
107− else Nothing
108−
109−
110−
111−-- Create an Event with a Map which is dense to size N
112−denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
113−denseMap n = plan [(1, m)] where
114− m = Map.fromList $ zip [1..n] [1..n]
115−
116−iter :: (a -> a) -> Word -> a -> a
117−iter _ 0 a = a
118−iter f n a = iter f (n - 1) (f a)
119−
120−iterM :: Monad m => (a -> m a) -> Word -> a -> m a
121−iterM _ 0 a = return a
122−iterM f n a = iterM f (n - 1) =<< f a
123−
124−fmapChain :: (Functor f) => Word -> f Word -> f Word
125−fmapChain = iter (fmap (+1))
126−
127−mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
128−mapDynChain = iterM (return . fmap (+1))
129−
130−joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131−joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132−
133−combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134−combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135−
136−pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
137−pingPong e1 e2 = do
138− rec
139− d <- foldDyn (const swap) (e1, e2) e
140− let e = switch (fst <$> current d)
141− return e
142−
143−switchFactors :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
144−switchFactors 0 e = return e
145−switchFactors i e = do
146− b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
147− switchFactors (i - 1) (switch b)
148−
149−
150−switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
151−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
152−
153−
154−switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
155−switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
156−
157−switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
158−switchPromptlyChain = iterM $ \e ->
159− switchPromptlyDyn <$> holdDyn e (e <$ e)
160−
161−
162−coinChain :: Reflex t => Word -> Event t Word -> Event t Word
163−coinChain = iter $ \e -> coincidence (e <$ e)
164−
165−fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
166−fanMergeChain width = iter $ fanMerge width
167−
168−fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
169−fmapFanMergeChain width = iter $ fmapFanMerge width
170−
171−-- | Data type for a collection (a map) which is updated by providing differences
172−data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
173−
174−-- This will hopefully become a primitive (faster!)
175−switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
176−switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
177−
178−-- switchMergeMaybe :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
179−-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
180−
181−switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
182−switchMergeBehaviors mapChanges = pull . joinMap <$> holdMap mapChanges
183− where joinMap m = traverse sample =<< sample m
184−
185−-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
186−holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
187−holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
188−
189− where
190− modify k Nothing items = Map.delete k items
191− modify k (Just item) items = Map.insert k item items
192−
193−
194−-- | Hold an UpdatedMap as a behavior by applying differences to the initial value
195−holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
196−holdMap = (current <$>) . holdMapDyn
197−
198−
199−increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
200−increasingMerge xs = do
201− e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
202− return (UpdatedMap Map.empty e)
203− where
204− reset = Map.fromList $ zip [1..] (const Nothing <$> xs)
205−
206−decreasingMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
207−decreasingMerge xs = do
208− e <- planList (zipWith Map.singleton [1..] (const Nothing <$> xs) ++ [reset])
209− return (UpdatedMap initial e)
210− where
211− initial = Map.fromList $ zip [1..] xs
212− reset = Just <$> initial
213−
214−modifyMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
215−modifyMerge xs = do
216− e <- planList $ zipWith Map.singleton [1..] (Just <$> xs)
217− return (UpdatedMap initial e)
218− where
219− initial = Map.fromList $ zip [1..] xs
220−
221−
222−countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
223−countMany = traverse (fmap current . count)
224−
225−countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
226−countManyDyn = traverse count
227−
228−
229−-- Given a function which creates a sub-network (from a single event)
230−-- Create a plan which uses a switch to substitute it
231−switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
232−switches numFrames f = do
233− es <- events numFrames
234− switch <$> hold never (makeEvents es)
235−
236− where
237− makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
238−
239−
240−
241−-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
242−-- the other, which we're only interested in time for running frames
243−subscribing :: Word -> Word -> [(String, TestCase)]
244−subscribing n frames =
245− [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
246− , testSub "fanMerge" $ return . fanMerge n
247− , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
248− , testSub "fmapChain" $ return . fmapChain n
249− , testSub "switchChain" $ switchChain n
250− , testSub "switchPromptlyChain" $ switchPromptlyChain n
251− , testSub "switchFactors" $ switchFactors n
252− , testSub "coincidenceChain" $ return . coinChain n
253− ]
254−
255− where
256− testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
257− testSub name test = testE name (switches frames test)
258−
259−-- Set of benchmark to test specifically merging dynamic collections
260−-- a pattern which occurs frequently in reflex-dom collections
261−merging :: Word -> [(String, TestCase)]
262−merging n =
263− [ testE "static events" $ mergeList <$> sparse
264− , testE "increasing events" $ switchMergeEvents =<< increasingMerge =<< sparse
265− , testE "decreasing events" $ switchMergeEvents =<< decreasingMerge =<< sparse
266− , testE "modify events" $ switchMergeEvents =<< modifyMerge =<< sparse
267−
268− , testB "static behaviors" $ pull . traverse sample <$> counters
269− , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
270− , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
271− , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
272−
273−
274− -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
275− -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
276− -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
277− ]
278−
279− where
280− sparse :: TestPlan t m => m [Event t Word]
281− sparse = fmap (fmap (+1)) <$> occasional n 8 n
282−
283− counters :: TestPlan t m => m [Behavior t Int]
284− counters = countMany =<< sparse
285−
286−
287−
288−fans :: Word -> [(String, TestCase)]
289−fans n =
290− [ testE "fanMapChain" $ fanMergeChain n 10 <$> e
291− , testE "fmapFanMapChain" $ fmapFanMergeChain n 10 <$> e
292−
293− , testE "fanMerge " $ fanMerge n <$> events n
294−
295− , testE "fmapFanMerge " $ fmapFanMerge n <$> events n
296− , testE "triggerMerge" $ leftmost <$> for [1..n] (\i -> plan [(i, i)])
297−
298− ]
299− where
300−
301− e :: TestPlan t m => m (Event t Word)
302− e = events 100
303−
304−
305−
306−
307−dynamics :: Word -> [(String, TestCase)]
308−dynamics n =
309− [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310− , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
311− , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312− , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313− , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
314− , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
315− ] where
316−
317− d :: TestPlan t m => m (Dynamic t Word)
318− d = count =<< events 10
319−
320− sparse, dense :: TestPlan t m => m [Dynamic t Int]
321− sparse = countManyDyn =<< occasional n 8 16
322− dense = countManyDyn =<< continuous n 2
323−
324−
325−firing :: Word -> [(String, TestCase)]
326−firing n =
327− [ testE "fmapChain" $ fmapChain n <$> e
328− , testE "switchChain2" $ switchChain2 n =<< e
329− , testE "switchPromptlyChain" $ switchPromptlyChain n =<< e
330− , testE "switchFactors" $ switchFactors n =<< e
331− , testE "coincidenceChain" $ coinChain n <$> e
332−
333− , testE "dense mergeTree" $ mergeTree 8 <$> dense
334− , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
335−
336− -- Triggers test failure in Spider
337− --, testE "runFrame" $ events n
338−
339− , testB "sum counters" $ do
340− counts <- counters
341− return $ pull $ sum <$> traverse sample counts
342−
343− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
344− , testB "pullChain2" $ do
345− es <- events 4
346− e' <- plan [(0, ())]
347−
348− b <- hold (constant 0) $
349− pushAlways (const $ fmapChain n <$> hold 0 es) e'
350−
351− return (join b)
352− , testB "counters/pullTree" $ mergeTree 8 <$> counters
353−
354− ]
355− where
356− e :: TestPlan t m => m (Event t Word)
357− e = events 10
358−
359− dense, sparse :: TestPlan t m => m [Event t Word]
360− dense = continuous n 2
361− sparse = occasional n 8 16
362−
363− counters :: TestPlan t m => m [Behavior t Int]
364− counters = countMany =<< sparse
365−
366−
367−
368−
369−
test/Reflex/Plan/Pure.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−
9−module Reflex.Plan.Pure where
10−
11−import Reflex
12−import Reflex.Pure
13−import Reflex.TestPlan
14−
15−import Control.Applicative
16−import Control.Monad.Fix
17−import Control.Monad.State
18−import Data.IntMap (IntMap)
19−import qualified Data.IntMap as IntMap
20−import Data.IntSet (IntSet)
21−import qualified Data.IntSet as IntSet
22−
23−import Data.Bifunctor
24−import Data.Maybe
25−import Data.Monoid
26−import Prelude
27−
28−
29−mapToPureEvent :: IntMap a -> Event (Pure Int) a
30−mapToPureEvent m = Event $ flip IntMap.lookup m
31−
32−type TimeM = (->) Int
33−newtype PurePlan a = PurePlan { unPlan :: StateT IntSet TimeM a } deriving (Functor, Applicative, Monad, MonadFix)
34−
35−liftPlan :: TimeM a -> PurePlan a
36−liftPlan = PurePlan . lift
37−
38−instance MonadHold (Pure Int) PurePlan where
39− hold initial = liftPlan . hold initial
40− holdDyn initial = liftPlan . holdDyn initial
41− holdIncremental initial = liftPlan . holdIncremental initial
42−
43−instance MonadSample (Pure Int) PurePlan where
44− sample = liftPlan . sample
45−
46−
47−instance TestPlan (Pure Int) PurePlan where
48− plan occs = do
49− PurePlan . modify $ IntSet.union (IntMap.keysSet m)
50− return $ mapToPureEvent m
51− where m = IntMap.fromList (first fromIntegral <$> occs)
52−
53−runPure :: PurePlan a -> (a, IntSet)
54−runPure (PurePlan p) = runStateT p mempty $ 0
55−
56−relavantTimes :: IntSet -> IntSet
57−relavantTimes occs = IntSet.fromList [0..l + 1]
58− where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
59−
60−testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
61−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
62−
63−testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
64−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
65−
66−
67−
68−
69−
70−
test/Reflex/Plan/Reflex.hsdeleted−135
@@ -1,135 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE StandaloneDeriving #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−module Reflex.Plan.Reflex
12− ( TestPlan(..)
13− , runPlan
14− , Plan (..)
15− , Schedule
16− , Firing (..)
17− , MonadIORef
18−
19−
20− , readSchedule
21− , readSchedule_
22− , testSchedule
23− , readEvent'
24− , makeDense
25−
26− , runTestE
27− , runTestB
28−
29− ) where
30−
31−import Reflex.Class
32−import Reflex.Host.Class
33−import Reflex.TestPlan
34−
35−import Control.Applicative
36−import Control.Monad
37−import Control.Monad.Identity
38−import Control.Monad.State.Strict
39−
40−import Control.Monad.Ref
41−import Data.Dependent.Sum (DSum (..))
42−import qualified Data.IntMap.Strict as IntMap
43−import Data.Maybe
44−import Data.Monoid
45−import Data.Traversable (sequenceA, traverse)
46−
47−import Control.DeepSeq
48−import Control.Exception
49−import Data.IntMap.Strict (IntMap)
50−import Data.IORef
51−import System.Mem
52−
53−-- Note: this import must come last to silence warnings from AMP
54−import Prelude
55−
56−type MonadIORef m = (MonadIO m, MonadRef m, Ref m ~ Ref IO)
57−
58−data Firing t where
59− Firing :: IORef (Maybe (EventTrigger t a)) -> a -> Firing t
60−
61−
62−type Schedule t = IntMap [Firing t]
63−
64−-- Implementation of a TestPlan in terms of ReflexHost
65−newtype Plan t a = Plan (StateT (Schedule t) (HostFrame t) a)
66−
67−deriving instance ReflexHost t => Functor (Plan t)
68−deriving instance ReflexHost t => Applicative (Plan t)
69−deriving instance ReflexHost t => Monad (Plan t)
70−
71−deriving instance ReflexHost t => MonadSample t (Plan t)
72−deriving instance ReflexHost t => MonadHold t (Plan t)
73−deriving instance ReflexHost t => MonadFix (Plan t)
74−
75−
76−instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => TestPlan t (Plan t) where
77− plan occurances = Plan $ do
78− (e, ref) <- newEventWithTriggerRef
79− modify (IntMap.unionWith mappend (firings ref))
80− return e
81−
82− where
83− firings ref = IntMap.fromList (makeFiring ref <$> occurances)
84− makeFiring ref (t, a) = (fromIntegral t, [Firing ref a])
85−
86−
87−firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
88−firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
89−
90−runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
91−runPlan (Plan p) = runHostFrame $ runStateT p mempty
92−
93−
94−makeDense :: Schedule t -> Schedule t
95−makeDense s = fromMaybe (emptyRange 0) $ do
96− (end, _) <- fst <$> IntMap.maxViewWithKey s
97− return $ IntMap.union s (emptyRange end)
98− where
99− emptyRange end = IntMap.fromList (zip [0..end + 1] (repeat []))
100−
101−
102−-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
103−-- after the last event)
104−-- performGC is called at each frame to test for GC issues
105−testSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
106−testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
107−
108−readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
109−readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
110−
111−readSchedule_ :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m ()
112−readSchedule_ schedule readResult = mapM_ (uncurry $ triggerFrame readResult) $ IntMap.toList schedule
113−
114−triggerFrame :: (MonadReflexHost t m, MonadIORef m, NFData a) => ReadPhase m a -> Int -> [Firing t] -> m a
115−triggerFrame readResult _ occs = do
116− triggers <- catMaybes <$> traverse firingTrigger occs
117− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
118−
119−readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
120−readEvent' = readEvent >=> sequenceA
121−
122−
123−-- Convenience functions for running tests producing Events/Behaviors
124−runTestB :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Behavior t a) -> m (IntMap a)
125−runTestB p = do
126− (b, s) <- runPlan p
127− testSchedule s $ sample b
128−
129−runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
130−runTestE p = do
131− (e, s) <- runPlan p
132− h <- subscribeEvent e
133− testSchedule s (readEvent' h)
134−
135−
test/Reflex/Test.hsdeleted−66
@@ -1,66 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Reflex.Test
9− ( testAgreement
10− , compareResult
11− , runTests
12−
13− , module Reflex.TestPlan
14−
15− ) where
16−
17−import Reflex.Spider
18−
19−import Reflex.TestPlan
20−
21−import Reflex.Plan.Pure
22−import Reflex.Plan.Reflex
23−
24−import Control.Monad
25−import Data.Monoid
26−
27−import Data.IntMap (IntMap)
28−
29−--import Data.Foldable
30−import System.Exit
31−
32−import Prelude
33−
34−
35−testAgreement :: TestCase -> IO Bool
36−testAgreement (TestE p) = do
37− spider <- runSpiderHost $ runTestE p
38− let results = [("spider", spider)]
39−
40− compareResult results (testEvent $ runPure p)
41−
42−testAgreement (TestB p) = do
43− spider <- runSpiderHost $ runTestB p
44− let results = [("spider", spider)]
45−
46− compareResult results (testBehavior $ runPure p)
47−
48−
49−compareResult :: (Show a, Eq a) => [(String, IntMap a)] -> IntMap a -> IO Bool
50−compareResult results expected = fmap and $ forM results $ \(name, r) -> do
51−
52− when (r /= expected) $ do
53− putStrLn ("Got: " ++ show (name, r))
54− putStrLn ("Expected: " ++ show expected)
55− return (r == expected)
56−
57−
58−runTests :: [(String, TestCase)] -> IO ()
59−runTests testCases = do
60− results <- forM testCases $ \(name, test) -> do
61− putStrLn $ "Test: " <> name
62− testAgreement test
63− exitWith $ if and results
64− then ExitSuccess
65− else ExitFailure 1
66−
test/Reflex/Test/CrossImpl.hsdeleted−251
@@ -1,251 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE TypeOperators #-}
13−{-# LANGUAGE UndecidableInstances #-}
14−module Reflex.Test.CrossImpl (test) where
15−
16−import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
17−
18−import Control.Monad.Ref
19−import Reflex.Class
20−import Reflex.Dynamic
21−import Reflex.Host.Class
22−import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
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 (DMap, DSum (..))
31−import qualified Data.Dependent.Map as DMap
32−import Data.Foldable
33−import Data.Functor.Misc
34−import Data.Map.Strict (Map)
35−import qualified Data.Map.Strict as Map
36−import qualified Data.Set as Set
37−import Data.Traversable
38−import System.Exit
39−import System.Mem
40−
41−import System.IO.Unsafe
42−
43−mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
44−mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
45− Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
46− Just (_, v) -> v
47−
48−mapToPureEvent :: Map Int a -> Event PureReflexDomain a
49−mapToPureEvent m = P.Event $ flip Map.lookup m
50−
51−relevantTestingTimes :: (Map Int a, Map Int b) -> [Int]
52−relevantTestingTimes (b, e) = case Set.minView &&& Set.maxView $ Map.keysSet b `Set.union` Map.keysSet e of
53− (Just (t0, _), Just (t1, _)) -> [t0..t1+1] -- We need to go to b+1 to see the result of the final event
54− _ -> [] -- Doesn't actually make much sense
55−
56−type PureReflexDomain = P.Pure Int
57−type TimeM = (->) Int
58−
59−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)
60−testPure builder (b, e) =
61− let (P.Behavior b', P.Event e') = ($ 0) $ builder (mapToPureBehavior b, mapToPureEvent e)
62− relevantTimes = relevantTestingTimes (b, e)
63− e'' = Map.mapMaybe id $ Map.fromList $ map (id &&& e') relevantTimes
64− b'' = Map.fromList $ map (id &&& b') relevantTimes
65− in (b'', e'')
66−
67−class MapMSignals a a' t t' | a -> t, a' -> t', a t' -> a', a' t -> a where
68− mapMSignals :: Monad m => (forall b. Behavior t b -> m (Behavior t' b)) -> (forall b. Event t b -> m (Event t' b)) -> a -> m a'
69−
70−instance MapMSignals (Behavior t a) (Behavior t' a) t t' where
71− mapMSignals fb _ = fb
72−
73−instance MapMSignals (Event t a) (Event t' a) t t' where
74− mapMSignals _ fe = fe
75−
76−instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
77− mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
78−
79−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)
80−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
81− (re, reTrigger) <- newEventWithTriggerRef
82− (rb, rbTrigger) <- newEventWithTriggerRef
83− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
84− (b', e') <- runHostFrame $ builder (b, re)
85− e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
86− let times = relevantTestingTimes (bMap, eMap)
87− liftIO performGC
88− outputs <- forM times $ \t -> do
89− forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
90− bOutput <- sample b'
91− eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
92− mret <- readRef reTrigger
93− let firing = case mret of
94− Just ret -> [ret :=> Identity val]
95− Nothing -> []
96− fireEventsAndRead firing $ sequence =<< readEvent e'Handle
97− liftIO performGC
98− return (t, (bOutput, eOutput))
99− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
100−
101−tracePerf :: Show a => a -> b -> b
102−tracePerf = flip const
103−
104−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
105−testAgreement builder inputs = do
106− let identityResult = testPure builder inputs
107− tracePerf "---------" $ return ()
108− let spiderResult = testSpider builder inputs
109− tracePerf "---------" $ return ()
110− let resultsAgree = identityResult == spiderResult
111− if resultsAgree
112− then do --putStrLn "Success:"
113− --print identityResult
114− return ()
115− else do putStrLn "Failure:"
116− putStrLn $ "Pure result: " <> show identityResult
117− putStrLn $ "Spider result: " <> show spiderResult
118− return resultsAgree
119−
120−type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
121−
122−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))
123−
124−testCases :: [(String, TestCase)]
125−testCases =
126− [ (,) "hold" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
127− b' <- hold "123" e
128− return (b', e)
129− , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
130− e' <- updated <$> count e
131− b' <- hold (0 :: Int) e'
132− return (b', e')
133− , (,) "headE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134− e' <- headE $ leftmost [e, e]
135− return (b, e')
136− , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
137− let e' = fmap (const e) e
138− b' <- hold never e'
139− let e'' = switch b'
140− return (b, e'')
141− , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
142− let e' = flip pushAlways e $ const $ do
143− let eab = splitRecombineEvent e
144− switch <$> hold eab never
145− e'' = coincidence e'
146− return (b, e'')
147− , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
148− let e' = flip pushAlways e $ const $ do
149− let eab = splitRecombineEvent e
150− switch <$> hold eab (fmap (const e) e)
151− e'' = coincidence e'
152− return (b, e'')
153− , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
154− let e' = leftmost [e, e]
155− e'' <- switch <$> hold e' (fmap (const e) e)
156− return (b, e'')
157− , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
158− let e' = fmap (const e) e
159− e'' <- switchPromptly never e'
160− return (b, e'')
161− , (,) "switchPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
162− let e' = fmap (const e) e
163− e'' <- switchPromptly never $ leftmost [e', e']
164− return (b, e'')
165− , (,) "switchPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
166− let e' = leftmost [e, e]
167− e'' <- switchPromptly never (fmap (const e) e')
168− return (b, e'')
169− , (,) "switchPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
170− let e' = leftmost [e, e]
171− e'' <- switchPromptly never (fmap (const e') e)
172− return (b, e'')
173− , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
174− let e' = leftmost [e, e]
175− e'' <- switch <$> hold never (fmap (const e') e)
176− return (b, e'')
177− , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
178− let e' = flip push e $ \_ -> do
179− Just <$> headE e
180− e'' <- switchPromptly never e'
181− return (b, e'')
182− , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
183− let e' = flip pushAlways e $ \_ -> do
184− switchPromptly e never
185− e'' <- switchPromptly never e'
186− return (b, e'')
187− , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
188− let e' = flip pushAlways e $ \_ -> return e
189− e'' = coincidence e'
190− return (b, e'')
191− , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
192− let e' = flip pushAlways e $ \_ -> return $ leftmost [e, e]
193− e'' = coincidence e'
194− return (b, e'')
195− , (,) "coincidence-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
196− let e' = flip pushAlways e $ \_ -> return $ coincidence $ fmap (const e) e
197− e'' = coincidence e'
198− return (b, e'')
199− , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
200− let e' = flip pushAlways e $ \_ -> headE e
201− e'' = coincidence e'
202− return (b, e'')
203− , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
204− let eChild = flip pushAlways e $ const $ do
205− let eNewValues = leftmost [e, e]
206− return $ coincidence $ fmap (const eNewValues) eNewValues
207− e' = coincidence eChild
208− return (b, e')
209− , (,) "coincidence-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
210− let eChild = flip pushAlways e $ const $ do
211− let e' = coincidence $ fmap (const e) e
212− return $ leftmost [e', e']
213− e'' = coincidence eChild
214− return (b, e'')
215− , (,) "coincidence-7" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
216− let e' = leftmost [e, e]
217− eCoincidences = coincidence $ fmap (const e') e
218− return (b, eCoincidences)
219− , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
220− eo <- headE e
221− bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
222− let b' = pull $ sample =<< sample bb
223− return (b', e)
224− , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
225− bb <- hold "b" e
226− bd <- hold never . fmap (const e) =<< headE e
227− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
228− let eInner = switch bd
229− e' = leftmost [eOuter, eInner]
230− return (b, e')
231− , (,) "mergeIncrementalWithMove" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, PatchDMapWithMove.moveDMapKey LeftTag RightTag), (2, mempty)]) $ \(b, e :: Event t (PatchDMapWithMove (EitherTag () ()) (Const2 () ()))) -> do
232− x <- holdIncremental (DMap.singleton LeftTag $ void e) $ PatchDMapWithMove.mapPatchDMapWithMove (\(Const2 _) -> void e) <$> e
233− let e' = mergeIncrementalWithMove x :: Event t (DMap (EitherTag () ()) Identity)
234− return (b, e')
235− ]
236−
237−splitRecombineEvent :: Reflex t => Event t a -> Event t String
238−splitRecombineEvent e =
239− let ea = "a" <$ e
240− eb = "b" <$ e
241− in leftmost [ea, eb]
242−
243−
244−test :: IO ()
245−test = do
246− results <- forM testCases $ \(name, TestCase inputs builder) -> do
247− putStrLn $ "Test: " <> name
248− testAgreement builder inputs
249− exitWith $ if and results
250− then ExitSuccess
251− else ExitFailure 1
test/Reflex/Test/Micro.hsdeleted−285
@@ -1,285 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE RecursiveDo #-}
9−
10−module Reflex.Test.Micro (testCases) where
11−
12−import Reflex
13−import Reflex.TestPlan
14−
15−import Control.Applicative
16−import Control.Monad
17−import Control.Monad.Fix
18−import Data.Char
19−import Data.Foldable
20−import Data.Functor.Misc
21−import qualified Data.Map as Map
22−import Data.Monoid
23−
24−import Prelude
25−
26−{-# ANN testCases "HLint: ignore Functor law" #-}
27−testCases :: [(String, TestCase)]
28−testCases =
29− [ testB "hold" $ hold "0" =<< events1
30−
31− , testB "count" $ do
32− b <- current <$> (count =<< events2)
33− return $ (+ (0::Int)) <$> b
34−
35− , testB "pull-1" $ do
36− b <- hold "0" =<< events1
37− return (pull $ sample $ pull $ sample b)
38−
39− , testB "pull-2" $ do
40− b1 <- behavior1
41− return (pull $ liftA2 (<>) (sample b1) (sample b1))
42−
43− , testB "pull-3" $ do
44− b1 <- behavior1
45− b2 <- behavior2
46− return (pull $ liftA2 (<>) (sample b1) (sample b2))
47−
48− , testB "pull-4" $ do
49− es <- planList ["a", "b", "c"]
50− e <- plan [(0, ())]
51− b <- hold (constant "") $
52− pushAlways (const $ hold "z" es) e
53− return (join b)
54−
55− , testE "id" $ do
56− events2
57−
58− , testE "fmap-id" $ do
59− e <- events2
60− return $ fmap id e
61−
62− , testE "tag-1" $ do
63− b1 <- behavior1
64− e <- events2
65− return (tag b1 e)
66−
67− , testE "tag-2" $ do
68− b1 <- behavior1
69− e <- events2
70− return (tag (map toUpper <$> b1) e)
71−
72− , testE "attach-1" $ do
73− b1 <- behavior1
74− e <- events2
75− return (attachWith (++) (map toUpper <$> b1) e)
76−
77− , testE "leftmost" $ liftA2 leftmost2 events1 events2
78−
79− , testE "appendEvents-1" $ liftA2 mappend events1 events2
80−
81− , testE "appendEvents-2" $ liftA2 mappend events3 events2
82−
83− , testE "merge-1" $ do
84− e <- events1
85− return $ leftmost ["x" <$ e, "y" <$ e]
86−
87− , testE "merge-2" $ do
88− e <- events1
89− let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
90− let ee = flip pushAlways e $ const $ return m
91− return $ coincidence ee
92−
93− , testE "headE-1" $ do
94− e <- events1
95− headE $ leftmost [e, e]
96−
97− , testE "headE-2" $ do
98− e <- events1
99− b <- hold never (e <$ e)
100− headE $ switch b
101−
102− , testE "switch-1" $ do
103− e <- events1
104− b <- hold never (e <$ e)
105− return $ switch b
106−
107− , testE "switch-2" $ do
108− e <- events1
109− return $ coincidence $ flip pushAlways e $ const $ do
110− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e)
111−
112− , testE "switch-3" $ do
113− e <- events1
114− return $ coincidence $ flip pushAlways e $ const $ do
115− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never
116−
117− , testE "switch-4" $ do
118− e <- events1
119− switch <$> hold (deep e) (e <$ e)
120−
121− , testE "switch-5" $ do
122− e <- events1
123− return $ coincidence $ flip pushAlways e $ const $
124− return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e]
125−
126− , testE "switch-6" $ do
127− e <- events1
128− return $ coincidence $ flip pushAlways e $ const $ do
129− switch <$> hold ("x" <$ e) (e <$ e)
130−
131− , testE "switchPromptly-1" $ do
132− e <- events1
133− let e' = e <$ e
134− switchPromptly never $ e <$ e'
135−
136− , testE "switchPromptly-2" $ do
137− e <- events1
138− switchPromptly never $ deep (e <$ e)
139−
140− , testE "switchPromptly-3" $ do
141− e <- events1
142− switchPromptly never $ (e <$ deep e)
143−
144− , testE "switchPromptly-4" $ do
145− e <- events1
146− switchPromptly never $ (deep e <$ e)
147−
148− , testE "switch-5" $ do
149− e <- events1
150− switch <$> hold never (deep e <$ e)
151−
152− , testE "switchPromptly-5" $ do
153− e <- events1
154− switchPromptly never $ flip push e $
155− const (Just <$> headE e)
156−
157− , testE "switchPromptly-6" $ do
158− e <- events1
159− switchPromptly never $ flip pushAlways e $
160− const (switchPromptly e never)
161−
162− , testE "coincidence-1" $ do
163− e <- events1
164− return $ coincidence $ flip pushAlways e $
165− const $ return e
166−
167− , testE "coincidence-2" $ do
168− e <- events1
169− return $ coincidence $ flip pushAlways e $
170− const $ return (deep e)
171−
172− , testE "coincidence-3" $ do
173− e <- events1
174− return $ coincidence $ flip pushAlways e $
175− const $ return (coincidence (e <$ e))
176−
177− , testE "coincidence-4" $ do
178− e <- events1
179− return $ coincidence $ flip pushAlways e $
180− const (headE e)
181−
182− , testE "coincidence-5" $ do
183− e <- events1
184− return $ coincidence $ flip pushAlways e $ const $ do
185− let e' = deep e
186− return (coincidence (e' <$ e'))
187−
188− , testE "coincidence-6" $ do
189− e <- events1
190− return $ coincidence $ flip pushAlways e $ const $ do
191− let e' = coincidence (e <$ e)
192− return $ deep e'
193−
194− , testE "coincidence-7" $ do
195− e <- events1
196− return $ coincidence (deep e <$ e)
197−
198− , testB "holdWhileFiring" $ do
199− e <- events1
200− eo <- headE e
201− bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
202− return $ pull $ sample =<< sample bb
203−
204− , testE "joinDyn" $ do
205− e <- events1
206− bb <- hold "b" e
207− bd <- hold never . fmap (const e) =<< headE e
208−
209− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
210− let eInner = switch bd
211− return $ leftmost [eOuter, eInner]
212−
213− , testB "foldDyn" $ do
214− d <- foldDyn (++) "0" =<< events1
215− return (current d)
216−
217− , testB "mapDyn" $ do
218− d <- foldDyn (++) "0" =<< events1
219− return $ current $ fmap (map toUpper) d
220−
221− , testB "combineDyn" $ do
222− d1 <- foldDyn (++) "0" =<< events1
223− d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
224−
225− return $ current $ zipDynWith (<>) d1 d2
226− , testE "fan-1" $ do
227− e <- fmap toMap <$> events1
228− let es = select (fanMap e) . Const2 <$> values
229−
230− return (mergeList es)
231−
232− , testE "fan-2" $ do
233− e <- fmap toMap <$> events3
234− let es = select (fanMap e) . Const2 <$> values
235−
236− return (mergeList es)
237−
238− , testE "fan-3" $ do
239− f <- fanMap . fmap toMap <$> events3
240− return $ select f (Const2 'c')
241−
242− , testE "fan-4" $ do
243− e <- fmap toMap <$> events1
244− return $ toUpper <$> select (fanMap e) (Const2 'a')
245−
246− , testE "fan-5" $ do
247− e <- fmap toMap <$> events2
248− return $ toUpper <$> select (fanMap e) (Const2 'c')
249−
250− , testE "fan-6" $ do
251− f <- fanMap . fmap toMap <$> events1
252− return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
253−
254− , testE "difference" $ do
255− e1 <- events1
256− e2 <- events2
257− return $ e1 `difference ` e2
258−
259− , testE "lazy-hold" $ do
260− let lazyHold :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => m (Event t ())
261− lazyHold = do
262− rec !b <- hold never e
263− let e = never <$ switch b
264− return $ void e
265− lazyHold
266−
267− ] where
268−
269− events1, events2, events3 :: TestPlan t m => m (Event t String)
270− events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
271− events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
272−
273− events3 = liftA2 mappend events1 events2
274−
275− values = "abcde"
276− toMap str = Map.fromList $ map (\c -> (c, c)) str
277−
278− behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
279− behavior1 = hold "1" =<< events1
280− behavior2 = hold "2" =<< events2
281−
282− deep e = leftmost [e, e]
283− leftmost2 e1 e2 = leftmost [e1, e2]
284−
285−
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/RequesterT.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE FlexibleContexts, GADTs #-}
2−module Main where
3−
4−import Control.Monad
5−import Control.Monad.IO.Class
6−import Control.Monad.Ref
7−import qualified Data.Dependent.Map as DMap
8−import Data.Dependent.Sum
9−import Data.IORef
10−import Data.List
11−import Data.Functor.Identity
12−
13−import Reflex
14−import Reflex.Host.Class
15−import Reflex.PerformEvent.Base
16−import Reflex.Requester.Base
17−import Reflex.Requester.Class
18−
19−import System.Exit (exitFailure, exitSuccess)
20−
21−data RequestInt a where
22− RequestInt :: Int -> RequestInt Int
23−
24−main :: IO ()
25−main = do
26− (Just out1, Nothing) <- runApp app1
27− print out1
28− (Just out2, Just (out3, out4)) <- runApp app2
29− print out2
30− print out3
31− print out4
32− when (sort out1 /= out1 || map (+1) out2 /= out3 || out3 /= out4) exitFailure
33−
34−runApp :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
35− => (Event t () -> RequesterT t RequestInt Identity (PerformEventT t m) (Maybe (Ref m (Maybe (EventTrigger t ())))))
36− -> IO (Maybe [Int], Maybe ([Int], [Int]))
37−runApp x = runSpiderHost $ do
38− (pulseE, pulseTriggerRef) <- newEventWithTriggerRef
39− ((mactuateRef, e), FireCommand fire) <- hostPerformEventT $ runRequesterT (x pulseE) never
40− hnd <- subscribeEvent (map unwrapRequest . DMap.toList <$> e)
41− out1 <- fireEventRefAndRead pulseTriggerRef () hnd
42− out2 <- fmap join $ forM mactuateRef $ \actuateRef -> do
43− let readPhase = do
44− mGetValue <- readEvent hnd
45− case mGetValue of
46− Nothing -> return Nothing
47− Just getValue -> fmap Just getValue
48− mactuate <- readRef actuateRef
49− mpulse <- readRef pulseTriggerRef
50− forM ((,) <$> mactuate <*> mpulse) $ \(actuate, pulse) -> do
51− _ <- fire [actuate :=> Identity ()] $ return ()
52− [Just a] <- fire [pulse :=> Identity ()] $ readPhase
53− [Just b, Nothing] <- fire [actuate :=> Identity (), pulse :=> Identity ()] $ readPhase
54− return (a,b)
55− return (out1, out2)
56−
57−unwrapRequest :: DSum tag RequestInt -> Int
58−unwrapRequest (_ :=> RequestInt i) = i
59−
60−app1 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
61−app1 e = do
62− forM_ [1..10] $ \i -> requestingIdentity (RequestInt i <$ e)
63− return Nothing
64−
65−app2 :: (Reflex t, Ref m ~ IORef, Requester t m, Response m ~ Identity, Request m ~ RequestInt, MonadAdjust t m, MonadReflexCreateTrigger t m, MonadRef m) => Event t () -> m (Maybe (Ref m (Maybe (EventTrigger t ()))))
66−app2 e = do
67− (pulse, pulseTriggerRef) <- newEventWithTriggerRef
68− forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ e)) $ ffor pulse $ \_ -> requestingIdentity (RequestInt (i+1) <$ e)
69− return (Just pulseTriggerRef)
70−
test/hlint.hs+34−14
@@ -1,19 +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 &&&"
13− , "--ignore=Use const"
14− , "--ignore=Use >=>"
15− , "--ignore=Use ."
16− , "--ignore=Use unless"
17− , "--cpp-define=USE_TEMPLATE_HASKELL"
18− ]
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
1939 if null ideas then exitSuccess else exitFailure
test/rootCleanup.hsdeleted−25
@@ -1,25 +0,0 @@
1−import Control.Monad
2−import Control.Concurrent
3−import Reflex
4−import Reflex.Host.Class
5−import System.Mem
6−import Data.IORef
7−import System.Exit
8−
9−main :: IO ()
10−main = do
11− numSubscriptions <- newIORef 0
12− replicateM_ 1000 $ do
13− runSpiderHost $ do
14− e <- newEventWithTrigger $ \_ -> do
15− modifyIORef' numSubscriptions succ
16− return $ modifyIORef' numSubscriptions pred
17− _ <- hold () e
18− return ()
19− replicateM_ 100 $ do
20− performMajorGC
21− threadDelay 1
22− n <- readIORef numSubscriptions
23− if n == 0
24− then putStrLn "Succeeded"
25− else putStrLn "Failed" >> exitFailure
test/semantics.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Main (main) where
9−
10−import Reflex.Test
11−
12−import Data.Bifunctor
13−import Data.Functor
14−import Data.List
15−import qualified Reflex.Bench.Focused as Focused
16−import qualified Reflex.Test.Micro as Micro
17−
18−import System.Environment
19−import System.Exit
20−
21−import Prelude
22−
23−matchPrefixes :: [String] -> (String -> Bool)
24−matchPrefixes [] = const True
25−matchPrefixes args = \name -> any (`isPrefixOf` name) args
26−
27−
28−main :: IO ()
29−main = do
30− args <- getArgs
31−
32− case args of
33− ["--list"] -> mapM_ putStrLn (fst <$> allTests) >> exitWith (ExitFailure 1)
34− _ -> case filter (matchPrefixes args . fst) allTests of
35− [] -> putStrLn "filter did not match any tests" >> exitWith (ExitFailure 1)
36− tests -> runTests tests
37−
38− where
39− allTests = concat
40− [ makeGroup "micro" Micro.testCases
41− , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
42− , makeGroup "firing 1000" (Focused.firing 1000)
43− , makeGroup "merge 100" (Focused.merging 100)
44− , makeGroup "fan 50" (Focused.fans 50)
45− ]
46−
47− makeGroup name tests = first (\test -> intercalate "/" [name, test]) <$> tests
48−
49−
test/tests.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+module Main where
3+
4+import Test.HUnit (runTestTT, (~:), assertEqual, errors, failures, test)
5+import Data.Patch ( Patch(apply) )
6+import Data.Patch.MapWithMove ( patchThatChangesMap )
7+import Data.Map as Map ( Map, fromList, singleton )
8+import Hedgehog (checkParallel, discover, Property, property, forAll, PropertyT, (===))
9+import Hedgehog.Gen as Gen ( int )
10+import Hedgehog.Range as Range ( linear )
11+import Control.Monad (replicateM)
12+import System.Exit (exitFailure, exitSuccess)
13+import Data.Sequence as Seq ( foldMapWithIndex, replicateM )
14+
15+main :: IO ()
16+main = do
17+ counts <- runTestTT $ test [
18+ "Simple Move" ~: (do
19+ let mapBefore = Map.fromList [(0,1)]
20+ mapAfter = Map.fromList [(0,0),(1,1)]
21+ patch = patchThatChangesMap mapBefore mapAfter
22+ afterPatch = apply patch mapBefore
23+ assertEqual "Patch creates the same Map" (Just mapAfter) afterPatch),
24+ "Property Checks" ~: propertyChecks
25+ ]
26+ if errors counts + failures counts == 0 then exitSuccess else exitFailure
27+
28+propertyChecks :: IO Bool
29+propertyChecks = checkParallel $$(discover)
30+
31+prop_patchThatChangesMap :: Property
32+prop_patchThatChangesMap = property $ do
33+ mapBefore <- makeRandomIntMap
34+ mapAfter <- makeRandomIntMap
35+ let patch = patchThatChangesMap mapBefore mapAfter
36+ Just mapAfter === apply patch mapBefore
37+
38+makeRandomIntMap :: Monad m => PropertyT m (Map Int Int)
39+makeRandomIntMap = do
40+ let genNum = Gen.int (Range.linear 0 100)
41+ length <- forAll genNum
42+ listOfNumbers <- forAll $ Seq.replicateM length genNum
43+ pure $ Seq.foldMapWithIndex Map.singleton listOfNumbers