on develop help sign in
88 files changed+1636−12852
since splitsnapshots
unifiedsplit
.github/workflows/haskell.yml+47 .travis.yml−16 CONTRIBUTING.md+66 ChangeLog.md+61 Quickref.md−294 README.md+44−11 bench/Main.hs−110 bench/RunAll.hs−157 cabal.project+1 default.nix−39 dep/reflex-platform/default.nix+8 dep/reflex-platform/github.json+8 patch.cabal+100 reflex.cabal−361 release.nix+49 src/Data/AppendMap.hs−81 src/Data/FastMutableIntMap.hs−76 src/Data/FastWeakBag.hs−156 src/Data/Functor/Misc.hs+5−61 src/Data/Map/Misc.hs−55 src/Data/Monoid/DecidablyEmpty.hs+115 src/Data/Patch.hs+95 src/Reflex/Patch/Class.hs → src/Data/Patch/Class.hs+15−4 src/Reflex/Patch/DMap.hs → src/Data/Patch/DMap.hs+38−13 src/Reflex/Patch/DMapWithMove.hs → src/Data/Patch/DMapWithMove.hs+26−18 src/Data/Patch/IntMap.hs+76 src/Reflex/Patch/Map.hs → src/Data/Patch/Map.hs+38−30 src/Data/Patch/MapWithMove.hs+312 src/Data/Patch/MapWithPatchingMove.hs+432 src/Data/Semigroup/Additive.hs+54 src/Data/WeakBag.hs−121 src/Reflex.hs−34 src/Reflex/Adjustable/Class.hs−113 src/Reflex/BehaviorWriter/Base.hs−204 src/Reflex/BehaviorWriter/Class.hs−26 src/Reflex/Class.hs−1359 src/Reflex/Collection.hs−121 src/Reflex/Dynamic.hs−680 src/Reflex/Dynamic/TH.hs−130 src/Reflex/Dynamic/Uniq.hs−105 src/Reflex/DynamicWriter.hs−7 src/Reflex/DynamicWriter/Base.hs−232 src/Reflex/DynamicWriter/Class.hs−24 src/Reflex/EventWriter.hs−7 src/Reflex/EventWriter/Base.hs−306 src/Reflex/EventWriter/Class.hs−27 src/Reflex/FastWeak.hs−146 src/Reflex/FunctorMaybe.hs−43 src/Reflex/Host/Class.hs−280 src/Reflex/Network.hs−39 src/Reflex/NotReady/Class.hs−68 src/Reflex/Optimizer.hs−61 src/Reflex/Patch.hs−39 src/Reflex/Patch/IntMap.hs−49 src/Reflex/Patch/MapWithMove.hs−228 src/Reflex/PerformEvent/Base.hs−179 src/Reflex/PerformEvent/Class.hs−64 src/Reflex/PostBuild/Base.hs−197 src/Reflex/PostBuild/Class.hs−42 src/Reflex/Profiled.hs−213 src/Reflex/Pure.hs−206 src/Reflex/Query/Base.hs−339 src/Reflex/Query/Class.hs−99 src/Reflex/Requester/Base.hs−429 src/Reflex/Requester/Class.hs−73 src/Reflex/Spider.hs−17 src/Reflex/Spider/Internal.hs−2634 src/Reflex/Time.hs−284 src/Reflex/TriggerEvent/Base.hs−148 src/Reflex/TriggerEvent/Class.hs−50 src/Reflex/Widget/Basic.hs−44 src/Reflex/Workflow.hs−38 test/EventWriterT.hs−114 test/GC.hs−100 test/QueryT.hs−133 test/Reflex/Bench/Focused.hs−369 test/Reflex/Plan/Pure.hs−72 test/Reflex/Plan/Reflex.hs−135 test/Reflex/Test.hs−66 test/Reflex/Test/CrossImpl.hs−257 test/Reflex/Test/Micro.hs−346 test/Reflex/TestPlan.hs−52 test/RequesterT.hs−67 test/Test/Run.hs−74 test/hlint.hs+3−8 test/rootCleanup.hs−25 test/semantics.hs−47 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
.travis.ymldeleted−16
@@ -1,16 +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=ghc821
10− - env: COMPILER=ghc802
11− - env: COMPILER=ghcjs
12−script:
13− - set -e
14− - nix-build -E "with (import <nixpkgs> {}); pkgs.haskell.packages.$COMPILER.callPackage ./default.nix { }"
15− - nix-collect-garbage -d # This is important to do right before 'script' ends so the cache gets GCed
16−
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−294
@@ -1,294 +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−Since MonadHold depends on MonadSample, any [S] function also runs in [H] context.
17−
18−## Functions producing Event
19−
20−```haskell
21−-- Trivial Event
22−[ ] never :: Event a
23−
24−-- Extract Event from Dynamic
25−[ ] updated :: Dynamic a -> Event a
26−
27−-- Transform Event to Event using function
28−[ ] fmap :: (a -> b) -> Event a -> Event b
29−[ ] fmapMaybe :: (a -> Maybe b) -> Event a -> Event b
30−[ ] ffilter :: (a -> Bool) -> Event a -> Event a
31−[ ] ffor :: Event a -> (a -> b) -> Event b
32−[ ] fforMaybe :: Event a -> (a -> Maybe b) -> Event b
33−[ ] <$ :: a -> Event b -> Event a
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−[ ] <@> :: Behavior (a -> b) -> Event a -> Event b
50−[ ] <@ :: Behavior a -> Event b -> Event a
51−
52−-- Combine multiple Events
53−[ ] <> :: Semigroup a => Event a -> Event a -> Event a
54−[ ] difference :: Event a -> Event b -> Event a
55−[ ] align :: Event a -> Event b -> Event (These a b)
56−[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
57−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
58−[ ] leftmost :: [Event a] -> Event a
59−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
60−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
61−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
62−
63−-- Efficient one-to-many fanout
64−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
65−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
66−[ ] select :: EventSelector k -> k a -> Event a
67−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
68−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
69−
70−-- Event to Event via function that can sample current values
71−[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
72−[ ] pushAlways :: (a -> m b ) -> Event a -> Event b
73− -- Note supplied function operates in [H] context
74−
75−-- Split Event into next occurrence and all other future occurrences, as of now.
76−[H] headE :: Event a -> m (Event a)
77−[H] tailE :: Event a -> m (Event a)
78−[H] headTailE :: Event a -> m (Event a, Event a)
79−```
80−
81−## Functions producing Behavior
82−
83−```haskell
84−-- Trivial Behavior
85−[ ] constant :: a -> Behavior a
86−
87−-- Extract Behavior from Dynamic
88−[ ] current :: Dynamic a -> Behavior a
89−
90−-- Create Behavior with given initial value, updated when the Event fires.
91−[H] hold :: a -> Event a -> m (Behavior a)
92−
93−-- Transform Behavior to Behavior using function
94−[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
95−[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
96−[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
97−[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
98−[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
99−-- ... plus many more due to typeclass membership
100−
101−-- Combine multiple behaviors via applicative instance
102−[ ] ffor2 :: Behavior a -> Behavior b -> (a -> b -> c) -> Behavior c
103−[ ] ffor3 :: Behavior a -> Behavior b -> Behavior c -> (a -> b -> c -> d) -> Behavior d
104−
105−-- Behavior to Behavior by sampling current values
106−[S] sample :: Behavior a -> m a
107−[ ] pull :: m a -> Behavior a
108− -- Note supplied value is in [S] context
109−```
110−
111−## Functions producing Dynamic
112−
113−```haskell
114−-- Trivial Dynamic
115−[ ] constDyn :: a -> Dynamic a
116−
117−-- Create Dynamic with given initial value, updated (various ways) when the Event fires.
118−[H] holdDyn :: a -> Event a -> m (Dynamic a)
119−[H] foldDyn :: (a -> b -> b ) -> b -> Event a -> m (Dynamic b)
120−[H] foldDynMaybe :: (a -> b -> Maybe b ) -> b -> Event a -> m (Dynamic b)
121−[H] foldDynM :: (a -> b -> m' b ) -> b -> Event a -> m (Dynamic b)
122−[H] foldDynMaybeM :: (a -> b -> m' (Maybe b)) -> b -> Event a -> m (Dynamic b)
123− -- Note m' supplies [H] context
124−
125−-- Initial value is 0; counts Event firings from now.
126−[H] count :: Num b => Event a -> m (Dynamic b)
127−-- Initial Bool value is supplied; toggles at each Event firing.
128−[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
129−
130−-- Transform Dynamic to Dynamic using function
131−[ ] ffor :: Dynamic a -> (a -> b) -> Dynamic b
132−[ ] fmap :: (a -> b) -> Dynamic a -> Dynamic b
133−[ ] splitDynPure :: Dynamic (a, b) -> (Dynamic a, Dynamic b)
134−
135−-- Combine multiple Dynamics
136−[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
137−[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
138−[ ] <*> :: Dynamic (a -> b) -> Dynamic a -> Dynamic b
139−[ ] >>= :: Dynamic a -> (a -> Dynamic b) -> Dynamic b
140−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
141−
142−-- Combine multiple dynamics via applicative instance
143−[ ] ffor2 :: Dynamic a -> Dynamic b -> (a -> b -> c) -> Dynamic c
144−[ ] ffor3 :: Dynamic a -> Dynamic b -> Dynamic c -> (a -> b -> c -> d) -> Dynamic d
145−
146−-- Efficient one-to-many fanout
147−[ ] demux :: Ord k => Dynamic k -> Demux k
148−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
149−
150−-- Dynamic to Dynamic, removing updates w/o value change
151−[H] holdUniqDyn :: Eq a => Dynamic a -> m (Dynamic a)
152−[H] holdUniqDynBy :: (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
153−
154−-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
155−[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
156−[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
157−```
158−
159−## Flattening functions
160−
161−These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
162−
163−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.
164−
165−```haskell
166−-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
167−[ ] switch :: Behavior (Event a) -> Event a
168−
169−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
170−[ ] switchDyn :: Dynamic (Event a) -> Event a
171−
172−-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
173−[ ] coincidence :: Event (Event a) -> Event a
174−
175−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
176−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
177−-- a Behavior whose value is equal to the current value of the current value of
178−-- the input.
179−[ ] join :: Behavior (Behavior a) -> Behavior a
180−
181−
182−-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
183−-- Output updated whenever inner OR outer Dynamic updates.
184−[ ] join :: Dynamic (Dynamic a) -> Dynamic a
185−[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
186−
187−-- Analogous to 'hold': Create a Behavior that is initially identical to the
188−-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
189−[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
190−
191−-- Similar to above, for Events. Created Event initially tracks the first argument.
192−-- At switchover, the output Event immediately tracks the new Event.
193−[H] switchHold :: Event a -> Event (Event a) -> m (Event a)
194−```
195−
196−## Typeclasses to introspect and modify an FRP network.
197−
198−The functions mentioned above are used to create a static FRP network.
199−There are additional typeclasses that can be used to modify the FRP network, to have it interact with IO action, or to introspect the building of the network.
200−
201−Th typeclasses and their associated annotations include:
202−
203−- `PostBuild`
204− Fire an Event when an FRP network has been set up.
205− ```haskell
206− [B] -- Function runs in any monad supporting PostBuild
207− ```
208−
209−- `Adjustable`
210− Use Events to add or remove pieces of an FRP network.
211− ```haskell
212− [A] -- Function runs in any monad supporting Adjustable
213− ```
214−
215−- `TriggerEvent`
216− Create new externally-fired Events from within an FRP network.
217− ```haskell
218− [T] -- Function runs in any monad supporting TriggerEvent
219− ```
220−
221−- `PerformEvent`
222− Use Events to trigger IO actions and to collect their results.
223− ```haskell
224− [P] -- Function runs in any monad supporting PerformEvent
225− ```
226−
227−## Startup
228−
229−```haskell
230−-- One-shot Event that is triggered once the FRP network has been built
231−[B] getPostBuild :: m (Event ())
232−```
233−
234−## Collection management functions
235−
236−```haskell
237−-- Turn a Dynamic key/value map into a set of dynamically-changing widgets.
238−[H,A,B] listWithKey :: Ord k =>
239− Dynamic (Map k v) -> (k -> Dynamic v -> m a ) -> m (Dynamic (Map k a))
240−
241−-- Same as above where the widget constructor doesn't care about the key.
242−[H,A,B] list :: Ord k =>
243− Dynamic (Map k v) -> ( Dynamic v -> m a ) -> m (Dynamic (Map k a))
244−
245−-- Even simpler version where there are no keys and we just use a list.
246−[H,A,B] simpleList ::
247− Dynamic [v] -> ( Dynamic v -> m a ) -> m (Dynamic [a])
248−
249−-- Like listWithKey specialized for widgets returning (Event a).
250−[H,A,B] listViewWithKey :: Ord k =>
251− Dynamic (Map k v) -> (k -> Dynamic v -> m (Event a)) -> m (Event (Map k a))
252−
253−-- Create a dynamically-changing set of widgets, one of which is selected at any time.
254−[H,A,B] selectViewListWithKey_ :: Ord k => Dynamic k ->
255− Dynamic (Map k v) -> (k -> Dynamic v -> Dynamic Bool -> m (Event a)) -> m (Event k)
256−
257−-- Same as listWithKey, but takes initial values and an updates Event instead of a Dynamic.
258−[H,A] listWithKeyShallowDiff :: Ord k =>
259− Map k v -> Event (Map k (Maybe v)) -> (k -> v -> Event v -> m a) -> m (Dynamic (Map k a))
260−```
261−
262−## Creating new events from within an FRP network.
263−
264−```haskell
265−-- Create a new Event and a function that will cause the Event to fire, from within an FRP network
266−[T] newTriggerEvent :: m (Event a, a -> IO ())
267−```
268−
269−## Connecting to the real world (I/O)
270−
271−```haskell
272−-- Run side-effecting actions in Event when it occurs; returned Event contains
273−-- results. Side effects run in the (Performable m) monad which is associated with
274−-- the (PerformEvent t m) typeclass constraint.
275−-- This allows for working with IO when a ((MonadIO (Performable m)) constraint is available.
276−[P] performEvent :: Event (Performable m a ) -> m (Event a)
277−
278−-- Just run side-effects; no return Event
279−[P] performEvent_ :: Event (Performable m ()) -> m ()
280−
281−-- Actions run asynchronously; actions are given a callback to send return values
282−[P,T] performEventAsync :: Event ((a -> IO ()) -> Performable m ()) -> m (Event a)
283−```
284−
285−### Time
286−
287−```haskell
288−-- Create Event at given interval with given basis time.
289−[P,T] tickLossy :: NominalDiffTime -> UTCTime -> m (Event t TickInfo)
290−
291−-- Delay an Event's occurrences by a given amount in seconds.
292−[P,T] delay :: NominalDiffTime -> Event t a -> m (Event t a)
293−```
294−
README.md+44−11
@@ -1,19 +1,52 @@
1−## [Reflex](https://reflex-frp.org/)
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−A summary of Reflex functions is available in the [quick reference](Quickref.md).
7+A `Patch` type represents a kind of change made to a data structure.
98
10−**Visit https://reflex-frp.org/ for more information, tutorials, documentation and examples.**
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−### 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/Main.hsdeleted−110
@@ -1,110 +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 Data.Maybe (fromJust)
24−import Reflex
25−import Reflex.Host.Class
26−
27−main :: IO ()
28−main = defaultMain
29− [ bgroup "micro" micros ]
30−
31−#if !(MIN_VERSION_deepseq(1,4,2))
32−instance NFData (IORef a) where
33− rnf x = seq x ()
34−#endif
35−
36−instance NFData (TVar a) where
37− rnf x = seq x ()
38−
39−newtype WHNF a = WHNF a
40−instance NFData (WHNF a) where
41− rnf (WHNF a) = seq a ()
42−
43−withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
44−withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
45− bench name . nfIO $ runSpiderHost (action a)
46−
47−withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
48−withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
49− bench name . whnfIO $ runSpiderHost (action a)
50−
51−micros :: [Benchmark]
52−micros =
53− [ bench "newIORef" $ whnfIO $ void $ newIORef ()
54− , env (newIORef (42 :: Int)) (bench "readIORef" . whnfIO . readIORef)
55− , bench "newTVar" $ whnfIO $ void $ newTVarIO ()
56− , env (newTVarIO (42 :: Int)) (bench "readTVar" . whnfIO . readTVarIO)
57− , bench "newEventWithTrigger" $ whnfIO . void $ runSpiderHost $ newEventWithTrigger $
58− \trigger -> return () <$ evaluate trigger
59− , bench "newEventWithTriggerRef" $ whnfIO . void $ runSpiderHost newEventWithTriggerRef
60− , withSetupWHNF "subscribeEvent" newEventWithTriggerRef $ subscribeEvent . fst
61− , withSetupWHNF "subscribeSwitch"
62− (join $ hold <$> fmap fst newEventWithTriggerRef <*> fmap fst newEventWithTriggerRef)
63− (subscribeEvent . switch)
64− , withSetupWHNF "subscribeMerge(1)" (setupMerge 1) $ \(ev,_) -> subscribeEvent ev
65− , withSetupWHNF "subscribeMerge(100)" (setupMerge 100) (subscribeEvent . fst)
66− , withSetupWHNF "subscribeMerge(10000)" (setupMerge 10000) (subscribeEvent . fst)
67− , bench "runHostFrame" $ whnfIO $ runSpiderHost $ runHostFrame $ return ()
68− , withSetupWHNF "fireEventsAndRead(single/single)"
69− (newEventWithTriggerRef >>= subscribePair)
70− (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
71− , withSetupWHNF "fireEventsOnly"
72− (newEventWithTriggerRef >>= subscribePair)
73− (\(_, trigger) -> do
74− key <- fromJust <$> liftIO (readIORef trigger)
75− fireEvents [key :=> Identity (42 :: Int)])
76− , withSetupWHNF "fireEventsAndRead(head/merge1)"
77− (setupMerge 1 >>= subscribePair)
78− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
79− , withSetupWHNF "fireEventsAndRead(head/merge100)"
80− (setupMerge 100 >>= subscribePair)
81− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
82− , withSetupWHNF "fireEventsAndRead(head/merge10000)"
83− (setupMerge 10000 >>= subscribePair)
84− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
85− , withSetupWHNF "fireEventsOnly(head/merge100)"
86− (setupMerge 100 >>= subscribePair)
87− (\(_, t:_) -> do
88− key <- fromJust <$> liftIO (readIORef t)
89− fireEvents [key :=> Identity (42 :: Int)])
90− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
91− , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
92− ]
93−
94−setupMerge :: Int
95− -> SpiderHost Global ( Event (SpiderTimeline Global) (DMap (Const2 Int a) Identity)
96− , [IORef (Maybe (EventTrigger Spider a))]
97− )
98−setupMerge num = do
99− (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
100− let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
101− pure (merge m, triggers)
102−
103−subscribePair :: (Event (SpiderTimeline Global) a, b) -> SpiderHost Global (EventHandle (SpiderTimeline Global) a, b)
104−subscribePair (ev, b) = (,b) <$> subscribeEvent ev
105−
106−fireAndRead :: IORef (Maybe (EventTrigger (SpiderTimeline Global) a)) -> a -> EventHandle (SpiderTimeline Global) b
107− -> SpiderHost Global (Maybe b)
108−fireAndRead trigger val subd = do
109− key <- fromJust <$> liftIO (readIORef trigger)
110− 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 = bench name . toBenchmarkable $ \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−39
@@ -1,39 +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, random, ref-tf
5−, semigroupoids , semigroups, split, stdenv, stm, syb
6−, template-haskell , these, time, transformers
7−, transformers-compat, unbounded-delays, prim-uniq
8−, data-default, filepath, directory, filemanip, ghcjs-base
9−, monoidal-containers
10−, useTemplateHaskell ? true
11−}:
12−mkDerivation {
13− pname = "reflex";
14− version = "0.5";
15− src = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [ ".git" "dist" ])) ./.;
16− libraryHaskellDepends = [
17− base bifunctors containers dependent-map dependent-sum
18− exception-transformers lens
19− MemoTrie monad-control mtl primitive ref-tf semigroupoids
20− semigroups stm syb template-haskell these transformers
21− transformers-compat prim-uniq
22− base bifunctors containers deepseq dependent-map dependent-sum
23− mtl ref-tf split transformers data-default
24− random time unbounded-delays monoidal-containers
25− ] ++ (if ghc.isGhcjs or false then [
26− ghcjs-base
27− ] else []) ++ (if !useTemplateHaskell then [] else [
28− haskell-src-exts haskell-src-meta
29− ]);
30− testHaskellDepends = if ghc.isGhcjs or false then [] else [
31− hlint filepath directory filemanip
32− ];
33− configureFlags = if useTemplateHaskell then [] else [
34− "-f-use-template-haskell"
35− ];
36− homepage = "https://github.com/reflex-frp/reflex";
37− description = "Higher-order Functional Reactive Programming";
38− license = stdenv.lib.licenses.bsd3;
39−}
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−361
@@ -1,361 +0,0 @@
1−Name: reflex
2−Version: 0.5
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://reflex-frp.org
14−bug-reports: https://github.com/reflex-frp/reflex/issues
15−
16−flag use-reflex-optimizer
17− description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
18− default: False
19− manual: True
20−
21−flag use-template-haskell
22− description: Use template haskell to generate lenses
23− default: True
24− manual: True
25−
26−flag debug-trace-events
27− description: Add instrumentation that outputs the stack trace of the definition of an event whenever it is subscribed to. Warning: It is very slow!
28− default: False
29− manual: True
30−
31−flag fast-weak
32− description: Use the primitive implementation of FastWeak in GHCJS; note that this requires GHCJS to be built with FastWeak and FastWeakBag present in the RTS, which is not the default
33− default: False
34− manual: True
35−
36−library
37− hs-source-dirs: src
38− build-depends:
39− MemoTrie == 0.6.*,
40− base >= 4.7 && < 4.13,
41− bifunctors >= 5.2 && < 5.6,
42− comonad,
43− containers >= 0.5 && < 0.7,
44− data-default >= 0.5 && < 0.8,
45− dependent-map >= 0.2.4 && < 0.3,
46− exception-transformers == 0.4.*,
47− lens >= 4.7 && < 5,
48− monad-control >= 1.0.1 && < 1.1,
49− monoidal-containers == 0.4.*,
50− mtl >= 2.1 && < 2.3,
51− prim-uniq >= 0.1.0.1 && < 0.2,
52− primitive >= 0.5 && < 0.7,
53− random == 1.1.*,
54− ref-tf == 0.4.*,
55− reflection == 2.1.*,
56− semigroupoids >= 4.0 && < 6,
57− semigroups >= 0.16 && < 0.19,
58− stm >= 2.4 && < 2.6,
59− syb >= 0.5 && < 0.8,
60− these >= 0.4 && < 0.8,
61− time >= 1.4 && < 1.9,
62− transformers >= 0.2,
63− transformers-compat >= 0.3,
64− unbounded-delays >= 0.1.0.0 && < 0.2
65−
66− exposed-modules:
67− Data.AppendMap,
68− Data.FastMutableIntMap,
69− Data.FastWeakBag,
70− Data.Functor.Misc,
71− Data.Map.Misc,
72− Data.WeakBag,
73− Reflex,
74− Reflex.Class,
75− Reflex.Adjustable.Class,
76− Reflex.BehaviorWriter.Base,
77− Reflex.BehaviorWriter.Class,
78− Reflex.Collection,
79− Reflex.Dynamic,
80− Reflex.Dynamic.Uniq,
81− Reflex.DynamicWriter,
82− Reflex.DynamicWriter.Base,
83− Reflex.DynamicWriter.Class,
84− Reflex.EventWriter,
85− Reflex.EventWriter.Base,
86− Reflex.EventWriter.Class,
87− Reflex.FastWeak,
88− Reflex.FunctorMaybe,
89− Reflex.Host.Class,
90− Reflex.Network,
91− Reflex.NotReady.Class,
92− Reflex.Patch,
93− Reflex.Patch.Class,
94− Reflex.Patch.DMap,
95− Reflex.Patch.DMapWithMove,
96− Reflex.Patch.IntMap,
97− Reflex.Patch.Map,
98− Reflex.Patch.MapWithMove,
99− Reflex.PerformEvent.Base,
100− Reflex.PerformEvent.Class,
101− Reflex.PostBuild.Base,
102− Reflex.PostBuild.Class,
103− Reflex.Profiled,
104− Reflex.Pure,
105− Reflex.Query.Base,
106− Reflex.Query.Class,
107− Reflex.Requester.Base,
108− Reflex.Requester.Class,
109− Reflex.Spider,
110− Reflex.Spider.Internal,
111− Reflex.Time,
112− Reflex.TriggerEvent.Base,
113− Reflex.TriggerEvent.Class,
114− Reflex.Widget.Basic,
115− Reflex.Workflow
116−
117− ghc-options: -Wall -fwarn-redundant-constraints -fwarn-tabs -funbox-strict-fields -O2 -fspecialise-aggressively
118−
119− if flag(debug-trace-events)
120− cpp-options: -DDEBUG_TRACE_EVENTS
121− build-depends: bytestring
122−
123− if flag(use-reflex-optimizer)
124− cpp-options: -DUSE_REFLEX_OPTIMIZER
125− build-depends: ghc
126− exposed-modules: Reflex.Optimizer
127−
128− if flag(use-template-haskell)
129− cpp-options: -DUSE_TEMPLATE_HASKELL
130− build-depends:
131− dependent-sum >= 0.3 && < 0.5,
132− haskell-src-exts >= 1.16 && < 1.21,
133− haskell-src-meta >= 0.6 && < 0.9,
134− template-haskell >= 2.9 && < 2.15
135− exposed-modules:
136− Reflex.Dynamic.TH
137− other-extensions: TemplateHaskell
138− else
139− build-depends:
140− dependent-sum == 0.4.*
141−
142− if flag(fast-weak) && impl(ghcjs)
143− cpp-options: -DGHCJS_FAST_WEAK
144−
145− if impl(ghcjs)
146− build-depends: ghcjs-base
147−
148−test-suite semantics
149− type: exitcode-stdio-1.0
150− main-is: semantics.hs
151− hs-source-dirs: test
152− ghc-options: -O2 -Wall -rtsopts
153− build-depends:
154− base,
155− bifunctors,
156− containers,
157− deepseq >= 1.3 && < 1.5,
158− dependent-map,
159− dependent-sum,
160− mtl,
161− ref-tf,
162− reflex,
163− split,
164− transformers >= 0.3
165− other-modules:
166− Reflex.Bench.Focused
167− Reflex.Plan.Pure
168− Reflex.Plan.Reflex
169− Reflex.Test
170− Reflex.Test.Micro
171− Reflex.TestPlan
172−
173−test-suite CrossImpl
174− type: exitcode-stdio-1.0
175− main-is: Reflex/Test/CrossImpl.hs
176− hs-source-dirs: test
177− ghc-options: -O2 -Wall -rtsopts
178− build-depends:
179− base,
180− containers,
181− dependent-map,
182− dependent-sum,
183− deepseq >= 1.3 && < 1.5,
184− mtl,
185− transformers,
186− ref-tf,
187− reflex
188− other-modules:
189− Reflex.Test
190− Reflex.TestPlan
191− Reflex.Plan.Reflex
192− Reflex.Plan.Pure
193−
194−test-suite hlint
195− type: exitcode-stdio-1.0
196− main-is: hlint.hs
197− hs-source-dirs: test
198− build-depends: base
199− , directory
200− , filepath
201− , filemanip
202− , hlint
203− if impl(ghcjs)
204− buildable: False
205−
206−test-suite EventWriterT
207− type: exitcode-stdio-1.0
208− main-is: EventWriterT.hs
209− hs-source-dirs: test
210− build-depends: base
211− , containers
212− , deepseq >= 1.3 && < 1.5
213− , dependent-map
214− , dependent-sum
215− , lens
216− , mtl
217− , these
218− , transformers
219− , reflex
220− , ref-tf
221− other-modules:
222− Reflex.Test
223− Reflex.TestPlan
224− Reflex.Plan.Reflex
225− Reflex.Plan.Pure
226− Test.Run
227−
228−test-suite RequesterT
229− type: exitcode-stdio-1.0
230− main-is: RequesterT.hs
231− hs-source-dirs: test
232− build-depends: base
233− , dependent-sum
234− , dependent-map
235− , lens
236− , these
237− , transformers
238− , reflex
239− , ref-tf
240− buildable: False
241− other-modules:
242− Reflex.TestPlan
243− Reflex.Plan.Pure
244−
245−test-suite QueryT
246− type: exitcode-stdio-1.0
247− main-is: QueryT.hs
248− hs-source-dirs: test
249− build-depends: base
250− , containers
251− , dependent-map
252− , dependent-sum
253− , deepseq >= 1.3 && < 1.5
254− , lens
255− , monoidal-containers
256− , mtl
257− , ref-tf
258− , reflex
259− , semigroups
260− , these
261− , transformers
262− other-modules:
263− Test.Run
264− Reflex.TestPlan
265− Reflex.Plan.Reflex
266− Reflex.Plan.Pure
267−
268−test-suite GC-Semantics
269− type: exitcode-stdio-1.0
270− main-is: GC.hs
271− hs-source-dirs: test
272− build-depends: base
273− , containers
274− , dependent-sum
275− , dependent-map
276− , deepseq >= 1.3 && < 1.5
277− , mtl
278− , these
279− , transformers
280− , reflex
281− , ref-tf
282− if impl(ghc < 8)
283− build-depends: semigroups
284− other-modules:
285− Reflex.Plan.Pure
286− Reflex.Plan.Reflex
287− Reflex.TestPlan
288− Test.Run
289−
290−test-suite rootCleanup
291− type: exitcode-stdio-1.0
292− main-is: rootCleanup.hs
293− hs-source-dirs: test
294− build-depends: base
295− , containers
296− , deepseq >= 1.3 && < 1.5
297− , dependent-sum
298− , mtl
299− , reflex
300− , ref-tf
301− , these
302− other-modules:
303− Reflex.Plan.Pure
304− Reflex.TestPlan
305− Test.Run
306−
307−benchmark spider-bench
308− type: exitcode-stdio-1.0
309− hs-source-dirs: bench test
310− main-is: Main.hs
311− ghc-options: -Wall -O2 -rtsopts
312− build-depends:
313− base,
314− containers,
315− criterion >= 1.1 && < 1.6,
316− deepseq >= 1.3 && < 1.5,
317− dependent-map,
318− dependent-sum,
319− ref-tf,
320− mtl,
321− primitive,
322− reflex,
323− split,
324− stm,
325− transformers >= 0.3
326− other-modules:
327− Reflex.TestPlan
328− Reflex.Plan.Reflex
329− Reflex.Bench.Focused
330−
331−benchmark saulzar-bench
332− type: exitcode-stdio-1.0
333− hs-source-dirs: bench test
334− c-sources: bench-cbits/checkCapability.c
335− main-is: RunAll.hs
336− ghc-options: -Wall -O2 -rtsopts -threaded
337− build-depends:
338− base,
339− containers >= 0.5 && < 0.7,
340− criterion >= 1.1 && < 1.6,
341− deepseq >= 1.3 && < 1.5,
342− dependent-map,
343− dependent-sum,
344− loch-th,
345− mtl,
346− primitive,
347− process,
348− ref-tf,
349− reflex,
350− split,
351− stm,
352− time,
353− transformers >= 0.3
354− other-modules:
355− Reflex.TestPlan
356− Reflex.Plan.Reflex
357− Reflex.Bench.Focused
358−
359−source-repository head
360− type: git
361− 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/AppendMap.hsdeleted−81
@@ -1,81 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveGeneric #-}
4−{-# LANGUAGE DeriveTraversable #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10−{-# LANGUAGE RoleAnnotations #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE StandaloneDeriving #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# OPTIONS_GHC -fno-warn-orphans #-}
15−-- | 'Data.Map' with a better 'Monoid' instance
16−--
17−-- 'Data.Map' has @mappend = union@, which is left-biased. AppendMap has
18−-- @mappend = unionWith mappend@ instead.
19−module Data.AppendMap
20− ( module Data.AppendMap
21− , module Data.Map.Monoidal
22− ) where
23−
24−import Prelude hiding (map, null)
25−
26−import Data.Coerce
27−import Data.Default
28−import Data.Map (Map)
29−#if MIN_VERSION_containers(0,5,11)
30−import qualified Data.Map.Internal.Debug as Map (showTree, showTreeWith)
31−#else
32−import qualified Data.Map as Map (showTree, showTreeWith)
33−#endif
34−import Data.Map.Monoidal
35−import Reflex.Class (FunctorMaybe (..))
36−import Reflex.Patch (Additive, Group (..))
37−
38−{-# DEPRECATED AppendMap "Use 'MonoidalMap' instead" #-}
39−type AppendMap = MonoidalMap
40−
41−{-# DEPRECATED _unAppendMap "Use 'getMonoidalMap' instead" #-}
42−_unAppendMap :: MonoidalMap k v -> Map k v
43−_unAppendMap = getMonoidalMap
44−
45−pattern AppendMap :: Map k v -> MonoidalMap k v
46−pattern AppendMap m = MonoidalMap m
47−
48−instance FunctorMaybe (MonoidalMap k) where
49− fmapMaybe = mapMaybe
50−
51−-- | Deletes a key, returning 'Nothing' if the result is empty.
52−nonEmptyDelete :: Ord k => k -> MonoidalMap k a -> Maybe (MonoidalMap k a)
53−nonEmptyDelete k vs =
54− let deleted = delete k vs
55− in if null deleted
56− then Nothing
57− else Just deleted
58−
59−mapMaybeNoNull :: (a -> Maybe b)
60− -> MonoidalMap token a
61− -> Maybe (MonoidalMap token b)
62−mapMaybeNoNull f as =
63− let bs = fmapMaybe f as
64− in if null bs
65− then Nothing
66− else Just bs
67−
68−-- TODO: Move instances to `Reflex.Patch`
69−instance (Ord k, Group q) => Group (MonoidalMap k q) where
70− negateG = map negateG
71−
72−instance (Ord k, Additive q) => Additive (MonoidalMap k q)
73−
74−showTree :: forall k a. (Show k, Show a) => MonoidalMap k a -> String
75−showTree = coerce (Map.showTree :: Map k a -> String)
76−
77−showTreeWith :: forall k a. (k -> a -> String) -> Bool -> Bool -> MonoidalMap k a -> String
78−showTreeWith = coerce (Map.showTreeWith :: (k -> a -> String) -> Bool -> Bool -> Map k a -> String)
79−
80−instance Default (MonoidalMap k a) where
81− def = empty
src/Data/FastMutableIntMap.hsdeleted−76
@@ -1,76 +0,0 @@
1−{-# LANGUAGE TypeFamilies #-}
2−module Data.FastMutableIntMap
3− ( FastMutableIntMap
4− , new
5− , newEmpty
6− , insert
7− , isEmpty
8− , getFrozenAndClear
9− , size
10− , applyPatch
11− , PatchIntMap (..)
12− , traverseIntMapPatchWithKey
13− , lookup
14− , forIntersectionWithImmutable_
15− , for_
16− , patchIntMapNewElements
17− , patchIntMapNewElementsMap
18− , getDeletions
19− ) where
20−
21−--TODO: Pure JS version
22−--TODO: Fast copy to FastIntMap
23−--TODO: Fast patch type
24−
25−import Prelude hiding (lookup)
26−
27−import Control.Monad.IO.Class
28−import Data.Foldable (traverse_)
29−import Data.IntMap.Strict (IntMap)
30−import qualified Data.IntMap.Strict as IntMap
31−import Data.IORef
32−import Reflex.Patch.Class
33−import Reflex.Patch.IntMap
34−
35−newtype FastMutableIntMap a = FastMutableIntMap (IORef (IntMap a))
36−
37−new :: IntMap a -> IO (FastMutableIntMap a)
38−new m = FastMutableIntMap <$> newIORef m
39−
40−newEmpty :: IO (FastMutableIntMap a)
41−newEmpty = FastMutableIntMap <$> newIORef IntMap.empty
42−
43−insert :: FastMutableIntMap a -> Int -> a -> IO ()
44−insert (FastMutableIntMap r) k v = modifyIORef' r $ IntMap.insert k v
45−
46−lookup :: FastMutableIntMap a -> Int -> IO (Maybe a)
47−lookup (FastMutableIntMap r) k = IntMap.lookup k <$> readIORef r
48−
49−forIntersectionWithImmutable_ :: MonadIO m => FastMutableIntMap a -> IntMap b -> (a -> b -> m ()) -> m ()
50−forIntersectionWithImmutable_ (FastMutableIntMap r) b f = do
51− a <- liftIO $ readIORef r
52− traverse_ (uncurry f) $ IntMap.intersectionWith (,) a b
53−
54−for_ :: MonadIO m => FastMutableIntMap a -> (a -> m ()) -> m ()
55−for_ (FastMutableIntMap r) f = do
56− a <- liftIO $ readIORef r
57− traverse_ f a
58−
59−isEmpty :: FastMutableIntMap a -> IO Bool
60−isEmpty (FastMutableIntMap r) = IntMap.null <$> readIORef r
61−
62−size :: FastMutableIntMap a -> IO Int
63−size (FastMutableIntMap r) = IntMap.size <$> readIORef r
64−
65−-- | Make an immutable snapshot of the datastructure and clear it
66−getFrozenAndClear :: FastMutableIntMap a -> IO (IntMap a)
67−getFrozenAndClear (FastMutableIntMap r) = do
68− result <- readIORef r
69− writeIORef r IntMap.empty
70− return result
71−
72−applyPatch :: FastMutableIntMap a -> PatchIntMap a -> IO (IntMap a)
73−applyPatch (FastMutableIntMap r) p@(PatchIntMap m) = do
74− v <- readIORef r
75− writeIORef r $! applyAlways p v
76− return $ IntMap.intersection v m
src/Data/FastWeakBag.hsdeleted−156
@@ -1,156 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−#ifdef USE_REFLEX_OPTIMIZER
4−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
5−#endif
6−#ifdef GHCJS_FAST_WEAK
7−{-# LANGUAGE ForeignFunctionInterface #-}
8−{-# LANGUAGE JavaScriptFFI #-}
9−#endif
10−-- | This module defines the 'FastWeakBag' type, which represents a mutable
11−-- collection of items that does not cause the items to be retained in memory.
12−-- This is useful for situations where a value needs to be inspected or modified
13−-- if it is still alive, but can be ignored if it is dead.
14−module Data.FastWeakBag
15− ( FastWeakBag
16− , FastWeakBagTicket
17− , empty
18− , isEmpty
19− , insert
20− , traverse
21− , remove
22− -- * Internal functions
23− -- These will not always be available.
24−#ifndef GHCJS_FAST_WEAK
25− , _weakBag_children --TODO: Don't export this
26−#endif
27− ) where
28−
29−import Prelude hiding (traverse)
30−
31−import Control.Monad
32−import Control.Monad.IO.Class
33−
34−#ifdef GHCJS_FAST_WEAK
35−import GHCJS.Types
36−import Reflex.FastWeak (js_isNull, unsafeFromRawJSVal, unsafeToRawJSVal)
37−#else
38−import Control.Exception
39−import Data.IntMap.Strict (IntMap)
40−import qualified Data.IntMap.Strict as IntMap
41−import Data.IORef
42−import System.Mem.Weak
43−#endif
44−
45−-- | A @FastWeakBag a@ holds a set of values of type @a@, but does not retain them -
46−-- that is, they can still be garbage-collected. As long as the @a@s remain
47−-- alive, the 'FastWeakBag' will continue to refer to them.
48−#ifdef GHCJS_FAST_WEAK
49−newtype FastWeakBag a = FastWeakBag JSVal
50−#else
51−data FastWeakBag a = FastWeakBag
52− { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
53− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
54− }
55−#endif
56−
57−-- | When inserting an item into a 'FastWeakBag', a 'FastWeakBagTicket' is returned. If
58−-- the caller retains the ticket, the item is guranteed to stay in memory (and
59−-- thus in the 'FastWeakBag'). The ticket can also be used to remove the item from
60−-- the 'FastWeakBag' prematurely (i.e. while it is still alive), using 'remove'.
61−#ifdef GHCJS_FAST_WEAK
62−newtype FastWeakBagTicket a = FastWeakBagTicket JSVal
63−#else
64−data FastWeakBagTicket a = FastWeakBagTicket
65− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
66− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
67− }
68−#endif
69−
70−-- | Insert an item into a 'FastWeakBag'.
71−{-# INLINE insert #-}
72−insert :: a -- ^ The item
73− -> FastWeakBag a -- ^ The 'FastWeakBag' to insert into
74− -> IO (FastWeakBagTicket a) -- ^ Returns a 'FastWeakBagTicket' that ensures the item
75− -- is retained and allows the item to be removed.
76−#ifdef GHCJS_FAST_WEAK
77−insert a wb = js_insert (unsafeToRawJSVal a) wb
78−foreign import javascript unsafe "$r = new h$FastWeakBagTicket($2, $1);" js_insert :: JSVal -> FastWeakBag a -> IO (FastWeakBagTicket a)
79−#else
80−insert a (FastWeakBag nextId children) = {-# SCC "insert" #-} do
81− a' <- evaluate a
82− myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
83− let cleanup = atomicModifyIORef' children $ \cs -> (IntMap.delete myId cs, ())
84− wa <- mkWeakPtr a' $ Just cleanup
85− atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
86− return $ FastWeakBagTicket
87− { _weakBagTicket_weakItem = wa
88− , _weakBagTicket_item = a'
89− }
90−#endif
91−
92−-- | Create an empty 'FastWeakBag'.
93−{-# INLINE empty #-}
94−empty :: IO (FastWeakBag a)
95−#ifdef GHCJS_FAST_WEAK
96−empty = js_empty
97−foreign import javascript unsafe "$r = new h$FastWeakBag();" js_empty :: IO (FastWeakBag a)
98−#else
99−empty = {-# SCC "empty" #-} do
100− nextId <- newIORef 1
101− children <- newIORef IntMap.empty
102− let bag = FastWeakBag
103− { _weakBag_nextId = nextId
104− , _weakBag_children = children
105− }
106− return bag
107−#endif
108−
109−-- | Check whether a 'FastWeakBag' is empty.
110−{-# INLINE isEmpty #-}
111−isEmpty :: FastWeakBag a -> IO Bool
112−#ifdef GHCJS_FAST_WEAK
113−isEmpty = js_isEmpty
114−foreign import javascript unsafe "(function(){ for(var i = 0; i < $1.tickets.length; i++) { if($1.tickets[i] !== null) { return false; } }; return true; })()" js_isEmpty :: FastWeakBag a -> IO Bool --TODO: Clean up as we go along so this isn't O(n) every time
115−#else
116−isEmpty bag = {-# SCC "isEmpty" #-} IntMap.null <$> readIORef (_weakBag_children bag)
117−#endif
118−
119−{-# INLINE traverse #-}
120−-- | Visit every node in the given list. If new nodes are appended during the
121−-- traversal, they will not be visited. Every live node that was in the list
122−-- when the traversal began will be visited exactly once; however, no guarantee
123−-- is made about the order of the traversal.
124−traverse :: forall a m. MonadIO m => FastWeakBag a -> (a -> m ()) -> m ()
125−#ifdef GHCJS_FAST_WEAK
126−traverse wb f = do
127− let go cursor = when (not $ js_isNull cursor) $ do
128− val <- liftIO $ js_getTicketValue cursor
129− f $ unsafeFromRawJSVal val
130− go =<< liftIO (js_getNext (FastWeakBagTicket cursor))
131− go =<< liftIO (js_getInitial wb)
132−foreign import javascript unsafe "(function(){ for(var i = $1.tickets.length - 1; i >= 0; i--) { if($1.tickets[i] !== null) { return $1.tickets[i]; } }; return null; })()" js_getInitial :: FastWeakBag a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
133−foreign import javascript unsafe "$r = $1.val;" js_getTicketValue :: JSVal -> IO JSVal
134−--TODO: Fix the race condition where if a cursor is deleted (presumably using 'remove', below) while we're holding it, it can't find its way back to the correct bag
135−foreign import javascript unsafe "(function(){ for(var i = $1.pos - 1; i >= 0; i--) { if($1.bag.tickets[i] !== null) { return $1.bag.tickets[i]; } }; return null; })()" js_getNext :: FastWeakBagTicket a -> IO JSVal --TODO: Clean up as we go along so this isn't O(n) every time -- Result can be null or a FastWeakBagTicket a
136−#else
137−traverse (FastWeakBag _ children) f = {-# SCC "traverse" #-} do
138− cs <- liftIO $ readIORef children
139− forM_ cs $ \c -> do
140− ma <- liftIO $ deRefWeak c
141− mapM_ f ma
142−#endif
143−
144−-- | Remove an item from the 'FastWeakBag'; does nothing if invoked multiple times
145−-- on the same 'FastWeakBagTicket'.
146−{-# INLINE remove #-}
147−remove :: FastWeakBagTicket a -> IO ()
148−#ifdef GHCJS_FAST_WEAK
149−remove = js_remove
150−foreign import javascript unsafe "$1.bag.tickets[$1.pos] = null; $1.bag = new h$FastWeakBag(); $1.bag.tickets.push($1); $1.pos = 0;" js_remove :: FastWeakBagTicket a -> IO () --TODO: Don't bother with the new surrogate FastWeakBag; instead, make the GC check for bag === null, and then null it out here
151−#else
152−remove (FastWeakBagTicket w _) = {-# SCC "remove" #-} finalize w
153−#endif
154−--TODO: Should 'remove' also drop the reference to the item?
155−
156−--TODO: can/should we provide a null FastWeakBagTicket?
src/Data/Functor/Misc.hs+5−61
@@ -4,17 +4,12 @@
44{-# LANGUAGE FlexibleContexts #-}
55{-# LANGUAGE FlexibleInstances #-}
66{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE KindSignatures #-}
87{-# LANGUAGE LambdaCase #-}
98{-# LANGUAGE MultiParamTypeClasses #-}
10−{-# LANGUAGE PatternSynonyms #-}
119{-# LANGUAGE PolyKinds #-}
1210{-# LANGUAGE RankNTypes #-}
1311{-# LANGUAGE ScopedTypeVariables #-}
1412{-# LANGUAGE StandaloneDeriving #-}
15−#ifdef USE_REFLEX_OPTIMIZER
16−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
17−#endif
1813-- | This module provides types and functions with no particular theme, but
1914-- which are relevant to the use of 'Functor'-based datastructures like
2015-- 'Data.Dependent.Map.DMap'.
@@ -38,30 +33,23 @@ module Data.Functor.Misc
3833 , dmapToThese
3934 , eitherToDSum
4035 , dsumToEither
41− -- * Deprecated functions
42− , sequenceDmap
43− , wrapDMap
44− , rewrapDMap
45− , unwrapDMap
46− , unwrapDMapMaybe
47− , extractFunctorDMap
4836 , ComposeMaybe (..)
4937 ) where
5038
51−import Control.Applicative (Applicative, (<$>))
52−import Control.Monad.Identity
39+import Control.Applicative ((<$>))
5340import Data.Dependent.Map (DMap)
5441import qualified Data.Dependent.Map as DMap
5542import Data.Dependent.Sum
43+import Data.Functor.Identity
5644import Data.GADT.Compare
5745import Data.GADT.Show
5846import Data.IntMap (IntMap)
5947import qualified Data.IntMap as IntMap
6048import Data.Map (Map)
6149import qualified Data.Map as Map
62−import Data.Some (Some)
63−import qualified Data.Some as Some
50+import Data.Some (Some, mkSome)
6451import Data.These
52+import Data.Type.Equality ((:~:)(Refl))
6553import Data.Typeable hiding (Refl)
6654
6755--------------------------------------------------------------------------------
@@ -86,9 +74,6 @@ deriving instance Read k => Read (Const2 k v v)
8674instance Show k => GShow (Const2 k v) where
8775 gshowsPrec n x@(Const2 _) = showsPrec n x
8876
89−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
90− showTaggedPrec (Const2 _) = showsPrec
91−
9277instance Eq k => GEq (Const2 k v) where
9378 geq (Const2 a) (Const2 b) =
9479 if a == b
@@ -131,7 +116,7 @@ intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=
131116-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
132117-- the keys, using a function to remove the wrapping 'Functor'
133118weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
134−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
135120
136121--------------------------------------------------------------------------------
137122-- WrapArg
@@ -221,11 +206,6 @@ instance GShow (EitherTag l r) where
221206 LeftTag -> showString "LeftTag"
222207 RightTag -> showString "RightTag"
223208
224−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
225− showTaggedPrec t n (Identity a) = case t of
226− LeftTag -> showsPrec n a
227− RightTag -> showsPrec n a
228−
229209-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
230210eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
231211eitherToDSum = \case
@@ -249,39 +229,3 @@ newtype ComposeMaybe f a =
249229 ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
250230
251231deriving instance Functor f => Functor (ComposeMaybe f)
252−
253−--------------------------------------------------------------------------------
254−-- Deprecated functions
255−--------------------------------------------------------------------------------
256−
257−{-# INLINE sequenceDmap #-}
258−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ -> fmap Identity)' instead" #-}
259−-- | Run the actions contained in the 'DMap'
260−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
261−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
262−
263−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
264−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
265−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
266−wrapDMap f = DMap.map $ f . runIdentity
267−
268−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
269−-- | Replace one functor for a 'DMap''s values with a different functor
270−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
271−rewrapDMap = DMap.map
272−
273−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
274−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
275−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
276−unwrapDMap f = DMap.map $ Identity . f
277−
278−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
279−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
280−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
281−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
282−
283−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
284−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
285−-- underlying functor
286−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
287−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
src/Data/Map/Misc.hsdeleted−55
@@ -1,55 +0,0 @@
1−{-# LANGUAGE LambdaCase #-}
2−module Data.Map.Misc
3− (
4− -- * Working with Maps
5− diffMapNoEq
6− , diffMap
7− , applyMap
8− , mapPartitionEithers
9− , applyMapKeysSet
10− ) where
11−
12−import Data.Align
13−import Data.Either (isLeft)
14−import Data.Map (Map)
15−import qualified Data.Map as Map
16−import Data.Maybe
17−import Data.Set (Set)
18−import qualified Data.Set as Set
19−import Data.These
20−
21−diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
22−diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
23− This _ -> Just Nothing
24− These _ new -> Just $ Just new
25− That new -> Just $ Just new
26−
27−diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
28−diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
29− This _ -> Just Nothing
30− These old new
31− | old == new -> Nothing
32− | otherwise -> Just $ Just new
33− That new -> Just $ Just new
34−
35−applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
36−applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
37− where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
38− maybeToEither = \case
39− Nothing -> Left ()
40− Just r -> Right r
41−
42−mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
43−mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
44− where (ls, rs) = Map.partition isLeft m
45− fromLeft (Left l) = l
46− fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
47− fromRight (Right r) = r
48− fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
49−
50−-- | Apply a map patch to a set
51−-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
52−applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
53−applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
54− where (insertions, deletions) = Map.partition isJust patch
55−
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/Reflex/Patch/Class.hs → src/Data/Patch/Class.hsrenamed+15−4
@@ -1,17 +1,23 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
13{-# LANGUAGE TypeFamilies #-}
24-- | The interface for types which represent changes made to other types
3−module Reflex.Patch.Class where
5+module Data.Patch.Class where
46
5−import Control.Monad.Identity
7+import Data.Functor.Identity
8+import Data.Kind (Type)
69import Data.Maybe
7−import Data.Semigroup
10+#if !MIN_VERSION_base(4,11,0)
11+import Data.Semigroup (Semigroup(..))
12+#endif
13+import Data.Proxy
814
915-- | A 'Patch' type represents a kind of change made to a datastructure.
1016--
1117-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
1218-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
1319class Patch p where
14− type PatchTarget p :: *
20+ type PatchTarget p :: Type
1521 -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
1622 -- 'Nothing'.
1723 apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
@@ -25,6 +31,11 @@ instance Patch (Identity a) where
2531 type PatchTarget (Identity a) = a
2632 apply (Identity a) _ = Just a
2733
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+
2839-- | Like '(.)', but composes functions that return patches rather than
2940-- functions that return new values. The Semigroup instance for patches must
3041-- apply patches right-to-left, like '(.)'.
src/Reflex/Patch/DMap.hs → src/Data/Patch/DMap.hsrenamed+38−13
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE GeneralizedNewtypeDeriving #-}
23{-# LANGUAGE LambdaCase #-}
34{-# LANGUAGE PolyKinds #-}
@@ -5,29 +6,42 @@
56{-# LANGUAGE ScopedTypeVariables #-}
67{-# LANGUAGE StandaloneDeriving #-}
78{-# LANGUAGE TypeFamilies #-}
8−module Reflex.Patch.DMap where
99
10−import Reflex.Patch.Class
11−import Reflex.Patch.IntMap
12−import Reflex.Patch.Map
10+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
11+module Data.Patch.DMap where
1312
14−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
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 (..))
1520import qualified Data.Dependent.Map as DMap
1621import Data.Functor.Constant
1722import Data.Functor.Misc
1823import qualified Data.IntMap as IntMap
1924import qualified Data.Map as Map
25+import Data.Monoid.DecidablyEmpty
26+#if !MIN_VERSION_base(4,11,0)
2027import Data.Semigroup (Semigroup (..))
28+#endif
2129import Data.Some (Some)
2230
23−-- | A set of changes to a 'DMap'. Any element may be inserted/updated or
24−-- deleted.
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@.
2534newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
2635
2736deriving instance GCompare k => Semigroup (PatchDMap k v)
2837
2938deriving instance GCompare k => Monoid (PatchDMap k v)
3039
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'.
3145instance GCompare k => Patch (PatchDMap k v) where
3246 type PatchTarget (PatchDMap k v) = DMap k v
3347 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?
@@ -37,32 +51,43 @@ instance GCompare k => Patch (PatchDMap k v) where
3751 Nothing -> Just $ Constant ()
3852 Just _ -> Nothing
3953
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'@.
4056mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
4157mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
4258
43−traversePatchDMap :: Applicative m => (forall a. v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
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')
4462traversePatchDMap f = traversePatchDMapWithKey $ const f
4563
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'@.
4666traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
4767traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
4868
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.
4971weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
5072weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
5173
52−patchDMapToPatchMapWith :: (f v -> v') -> PatchDMap (Const2 k v) f -> PatchMap k v'
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'
5377patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
5478
55−const2PatchDMapWith :: forall k v f a. (v -> f a) -> PatchMap k v -> PatchDMap (Const2 k a) f
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'
5681const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
57− where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe f)
82+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
5883 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
5984
85+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
6086const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
6187const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
6288 where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
6389 g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
6490
65−-- | Get the values that will be deleted if the given patch is applied to the
66−-- given 'DMap'. Includes values that will be replaced.
91+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
6792getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
6893getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Reflex/Patch/DMapWithMove.hs → src/Data/Patch/DMapWithMove.hsrenamed+26−18
@@ -13,25 +13,28 @@
1313
1414-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
1515-- move values between keys.
16−module Reflex.Patch.DMapWithMove where
16+module Data.Patch.DMapWithMove where
1717
18−import Reflex.Patch.Class
19−import Reflex.Patch.MapWithMove (PatchMapWithMove (..))
20−import qualified Reflex.Patch.MapWithMove as MapWithMove
18+import Data.Patch.Class
19+import Data.Patch.MapWithMove (PatchMapWithMove (..))
20+import qualified Data.Patch.MapWithMove as MapWithMove
2121
22−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
22+import Data.Constraint.Extras
23+import Data.Dependent.Map (DMap)
24+import Data.Dependent.Sum (DSum (..))
2325import qualified Data.Dependent.Map as DMap
24−import Data.Dependent.Sum (EqTag (..))
2526import Data.Functor.Constant
2627import Data.Functor.Misc
2728import Data.Functor.Product
28−import Data.GADT.Compare (GEq (..))
29+import Data.GADT.Compare (GEq (..), GCompare (..))
2930import Data.GADT.Show (GShow, gshow)
3031import qualified Data.Map as Map
3132import Data.Maybe
32−import Data.Semigroup (Semigroup (..), (<>))
33−import Data.Some (Some)
34−import qualified Data.Some as Some
33+import Data.Monoid.DecidablyEmpty
34+#if !MIN_VERSION_base(4,11,0)
35+import Data.Semigroup (Semigroup (..))
36+#endif
37+import Data.Some (Some, mkSome)
3538import Data.These
3639
3740-- | 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
@@ -43,6 +46,10 @@ import Data.These
4346-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
4447newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
4548
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+
4653-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
4754-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
4855data NodeInfo k v a = NodeInfo
@@ -104,8 +111,9 @@ validationErrorsForPatchDMapWithMove m =
104111 Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
105112 unbalancedMove _ = Nothing
106113
107−instance EqTag k (NodeInfo k v) => Eq (PatchDMapWithMove k v) where
108− PatchDMapWithMove a == PatchDMapWithMove b = a == b
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
109117
110118-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
111119data Pair1 f g a = Pair1 (f a) (g a)
@@ -212,8 +220,8 @@ moveDMapKey src dst = case src `geq` dst of
212220-- @
213221-- let aMay = DMap.lookup a dmap
214222-- bMay = DMap.lookup b dmap
215−-- in maybe id (DMap.insert a) (bMay `mplus` aMay)
216−-- . maybe id (DMap.insert b) (aMay `mplus` bMay)
223+-- in maybe id (DMap.insert a) (bMay <> aMay)
224+-- . maybe id (DMap.insert b) (aMay <> bMay)
217225-- . DMap.delete a . DMap.delete b $ dmap
218226-- @
219227swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
@@ -310,8 +318,8 @@ weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenD
310318 { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
311319 From_Insert v -> MapWithMove.From_Insert $ f v
312320 From_Delete -> MapWithMove.From_Delete
313− From_Move k -> MapWithMove.From_Move $ Some.This k
314− , 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)
315323 }
316324
317325-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
@@ -341,6 +349,7 @@ const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fr
341349 , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
342350 }
343351
352+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
344353instance GCompare k => Patch (PatchDMapWithMove k v) where
345354 type PatchTarget (PatchDMapWithMove k v) = DMap k v
346355 apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
@@ -356,8 +365,7 @@ instance GCompare k => Patch (PatchDMapWithMove k v) where
356365 From_Delete -> Just $ Constant ()
357366 _ -> Nothing
358367
359−-- | Get the values that will be deleted or moved if the given patch is applied
360−-- 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'.
361369getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
362370getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
363371 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/Reflex/Patch/Map.hs → src/Data/Patch/Map.hsrenamed+38−30
@@ -1,34 +1,40 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
25{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TemplateHaskell #-}
39{-# LANGUAGE TypeFamilies #-}
10+
411-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
512-- and deletions
6−module Reflex.Patch.Map where
13+module Data.Patch.Map where
714
8−import Reflex.Patch.Class
15+import Data.Patch.Class
916
17+import Control.Lens
1018import Data.Map (Map)
1119import qualified Data.Map as Map
1220import Data.Maybe
13−import Data.Semigroup
21+import Data.Monoid.DecidablyEmpty
22+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
1423
1524-- | A set of changes to a 'Map'. Any element may be inserted/updated or
1625-- deleted. Insertions are represented as values wrapped in 'Just', while
1726-- deletions are represented as 'Nothing's
1827newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
19− deriving (Show, Read, Eq, Ord)
28+ deriving ( Show, Read, Eq, Ord
29+ , Foldable, Traversable
30+ , DecidablyEmpty
31+ )
2032
21−-- | Applying a 'PatchMap' will update the 'Map' by performing the insertions
22−-- and deletions specified
23−instance Ord k => Patch (PatchMap k v) where
24− type PatchTarget (PatchMap k v) = Map k v
25− {-# INLINABLE apply #-}
26− 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?
27− where insertions = Map.mapMaybeWithKey (const id) p
28− deletions = Map.mapMaybeWithKey (const nothingToJust) p
29− nothingToJust = \case
30− Nothing -> Just ()
31− Just _ -> Nothing
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)
3238
3339-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
3440-- If the same key is modified by both patches, the one on the left will take
@@ -36,24 +42,24 @@ instance Ord k => Patch (PatchMap k v) where
3642instance Ord k => Semigroup (PatchMap k v) where
3743 PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
3844 -- PatchMap is idempotent, so stimes n is id for every n
39−#if MIN_VERSION_semigroups(0,17,0)
4045 stimes = stimesIdempotentMonoid
41−#else
42− times1p n x = case compare n 0 of
43− LT -> error "stimesIdempotentMonoid: negative multiplier"
44− EQ -> mempty
45− GT -> x
46−#endif
4746
48−-- | The empty 'PatchMap' contains no insertions or deletions
49−instance Ord k => Monoid (PatchMap k v) where
50− mempty = PatchMap mempty
51− mappend = (<>)
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
5257
53−-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
54−-- Deletions are unaffected.
55−instance Functor (PatchMap k) where
56− fmap f = PatchMap . fmap (fmap f) . unPatchMap
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
5763
5864-- | Returns all the new elements that will be added to the 'Map'
5965patchMapNewElements :: PatchMap k v -> [v]
@@ -62,3 +68,5 @@ patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
6268-- | Returns all the new elements that will be added to the 'Map'
6369patchMapNewElementsMap :: PatchMap k v -> Map k v
6470patchMapNewElementsMap (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−121
@@ -1,121 +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 Prelude hiding (traverse)
25−
26−import Control.Exception
27−import Control.Monad
28−import Control.Monad.IO.Class
29−import Data.IntMap.Strict (IntMap)
30−import qualified Data.IntMap.Strict as IntMap
31−import Data.IORef
32−import System.Mem.Weak
33−
34−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
35−-- that is, they can still be garbage-collected. As long as the @a@s remain
36−-- alive, the 'WeakBag' will continue to refer to them.
37−data WeakBag a = WeakBag
38− { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
39− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a))) -- ^ The items referenced by the WeakBag
40− }
41−
42−-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
43−-- the caller retains the ticket, the item is guranteed to stay in memory (and
44−-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
45−-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
46−data WeakBagTicket = forall a. WeakBagTicket
47− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
48− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
49− }
50−
51−-- | Insert an item into a 'WeakBag'.
52−{-# INLINE insert #-}
53−insert :: a -- ^ The item
54− -> WeakBag a -- ^ The 'WeakBag' to insert into
55− -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
56− -- callback
57− -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
58− -- (whether automatically by the item being garbage
59− -- collected or manually via 'remove')
60− -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
61− -- is retained and allows the item to be removed.
62−insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
63− a' <- evaluate a
64− wbRef' <- evaluate wbRef
65− myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
66− let cleanup = do
67− wb <- readIORef wbRef'
68− mb <- deRefWeak wb
69− forM_ mb $ \b -> do
70− csWithoutMe <- atomicModifyIORef children $ \cs ->
71− let !csWithoutMe = IntMap.delete myId cs
72− in (csWithoutMe, csWithoutMe)
73− when (IntMap.null csWithoutMe) $ finalizer b
74− wa <- mkWeakPtr a' $ Just cleanup
75− atomicModifyIORef' children $ \cs -> (IntMap.insert myId wa cs, ())
76− return $ WeakBagTicket
77− { _weakBagTicket_weakItem = wa
78− , _weakBagTicket_item = a'
79− }
80−
81−-- | Create an empty 'WeakBag'.
82−{-# INLINE empty #-}
83−empty :: IO (WeakBag a)
84−empty = {-# SCC "empty" #-} do
85− nextId <- newIORef 1
86− children <- newIORef IntMap.empty
87− let bag = WeakBag
88− { _weakBag_nextId = nextId
89− , _weakBag_children = children
90− }
91− return bag
92−
93−-- | Create a 'WeakBag' with one item; equivalent to creating the 'WeakBag' with
94−-- 'empty', then using 'insert'.
95−{-# INLINE singleton #-}
96−singleton :: a -> IORef (Weak b) -> (b -> IO ()) -> IO (WeakBag a, WeakBagTicket)
97−singleton a wbRef finalizer = {-# SCC "singleton" #-} do
98− bag <- empty
99− ticket <- insert a bag wbRef finalizer
100− return (bag, ticket)
101−
102−{-# INLINE traverse #-}
103−-- | Visit every node in the given list. If new nodes are appended during the
104−-- traversal, they will not be visited. Every live node that was in the list
105−-- when the traversal began will be visited exactly once; however, no guarantee
106−-- is made about the order of the traversal.
107−traverse :: MonadIO m => WeakBag a -> (a -> m ()) -> m ()
108−traverse (WeakBag _ children) f = {-# SCC "traverse" #-} do
109− cs <- liftIO $ readIORef children
110− forM_ cs $ \c -> do
111− ma <- liftIO $ deRefWeak c
112− mapM_ f ma
113−
114−-- | Remove an item from the 'WeakBag'; does nothing if invoked multiple times
115−-- on the same 'WeakBagTicket'.
116−{-# INLINE remove #-}
117−remove :: WeakBagTicket -> IO ()
118−remove (WeakBagTicket w _) = {-# SCC "remove" #-} finalize w
119−--TODO: Should 'remove' also drop the reference to the item?
120−
121−--TODO: can/should we provide a null WeakBagTicket?
src/Reflex.hsdeleted−34
@@ -1,34 +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.Adjustable.Class as X
10−import Reflex.BehaviorWriter.Base as X
11−import Reflex.BehaviorWriter.Class as X
12−import Reflex.Collection as X
13−import Reflex.Dynamic as X
14−import Reflex.EventWriter.Base as X
15−import Reflex.EventWriter.Class as X
16−#ifdef USE_TEMPLATE_HASKELL
17−import Reflex.Dynamic.TH as X
18−#endif
19−import Reflex.Dynamic.Uniq as X
20−import Reflex.DynamicWriter.Base as X
21−import Reflex.DynamicWriter.Class as X
22−import Reflex.PerformEvent.Base as X
23−import Reflex.PerformEvent.Class as X
24−import Reflex.PostBuild.Base as X
25−import Reflex.PostBuild.Class as X
26−import Reflex.Profiled as X
27−import Reflex.Query.Base as X
28−import Reflex.Query.Class as X
29−import Reflex.Requester.Base as X
30−import Reflex.Requester.Class as X
31−import Reflex.Spider as X
32−import Reflex.Time as X
33−import Reflex.TriggerEvent.Base as X
34−import Reflex.TriggerEvent.Class as X
src/Reflex/Adjustable/Class.hsdeleted−113
@@ -1,113 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−#ifdef USE_REFLEX_OPTIMIZER
12−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
13−#endif
14−module Reflex.Adjustable.Class
15− (
16− -- * The Adjustable typeclass
17− Adjustable(..)
18− , sequenceDMapWithAdjust
19− , sequenceDMapWithAdjustWithMove
20− , mapMapWithAdjustWithMove
21− -- * Deprecated aliases
22− , MonadAdjust
23− ) where
24−
25−import Control.Monad.Identity
26−import Control.Monad.Reader
27−import Data.Dependent.Map (DMap, GCompare (..))
28−import qualified Data.Dependent.Map as DMap
29−import Data.Functor.Constant
30−import Data.Functor.Misc
31−import Data.IntMap.Strict (IntMap)
32−import qualified Data.IntMap.Strict as IntMap
33−import Data.Map (Map)
34−
35−import Reflex.Class
36−import Reflex.Patch.DMapWithMove
37−
38−-- | A 'Monad' that supports adjustment over time. After an action has been
39−-- run, if the given events fire, it will adjust itself so that its net effect
40−-- is as though it had originally been run with the new value. Note that there
41−-- is some issue here with persistent side-effects: obviously, IO (and some
42−-- other side-effects) cannot be undone, so it is up to the instance implementer
43−-- to determine what the best meaning for this class is in such cases.
44−class (Reflex t, Monad m) => Adjustable t m | m -> t where
45− runWithReplace
46− :: m a
47− -> Event t (m b)
48− -> m (a, Event t b)
49−
50− traverseIntMapWithKeyWithAdjust
51− :: (IntMap.Key -> v -> m v')
52− -> IntMap v
53− -> Event t (PatchIntMap v)
54− -> m (IntMap v', Event t (PatchIntMap v'))
55−
56− traverseDMapWithKeyWithAdjust
57− :: GCompare k
58− => (forall a. k a -> v a -> m (v' a))
59− -> DMap k v
60− -> Event t (PatchDMap k v)
61− -> m (DMap k v', Event t (PatchDMap k v'))
62− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
63− traverseDMapWithKeyWithAdjust f dm0 dm' = fmap (fmap (fmap fromPatchWithMove)) $
64− traverseDMapWithKeyWithAdjustWithMove f dm0 $ fmap toPatchWithMove dm'
65− where
66− toPatchWithMove (PatchDMap m) = PatchDMapWithMove $ DMap.map toNodeInfoWithMove m
67− toNodeInfoWithMove = \case
68− ComposeMaybe (Just v) -> NodeInfo (From_Insert v) $ ComposeMaybe Nothing
69− ComposeMaybe Nothing -> NodeInfo From_Delete $ ComposeMaybe Nothing
70− fromPatchWithMove (PatchDMapWithMove m) = PatchDMap $ DMap.map fromNodeInfoWithMove m
71− fromNodeInfoWithMove (NodeInfo from _) = ComposeMaybe $ case from of
72− From_Insert v -> Just v
73− From_Delete -> Nothing
74− From_Move _ -> error "traverseDMapWithKeyWithAdjust: implementation of traverseDMapWithKeyWithAdjustWithMove inserted spurious move"
75−
76− traverseDMapWithKeyWithAdjustWithMove
77− :: GCompare k
78− => (forall a. k a -> v a -> m (v' a))
79− -> DMap k v
80− -> Event t (PatchDMapWithMove k v)
81− -> m (DMap k v', Event t (PatchDMapWithMove k v'))
82−
83−instance Adjustable t m => Adjustable t (ReaderT r m) where
84− runWithReplace a0 a' = do
85− r <- ask
86− lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
87− traverseIntMapWithKeyWithAdjust f dm0 dm' = do
88− r <- ask
89− lift $ traverseIntMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
90− traverseDMapWithKeyWithAdjust f dm0 dm' = do
91− r <- ask
92− lift $ traverseDMapWithKeyWithAdjust (\k v -> runReaderT (f k v) r) dm0 dm'
93− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
94− r <- ask
95− lift $ traverseDMapWithKeyWithAdjustWithMove (\k v -> runReaderT (f k v) r) dm0 dm'
96−
97−sequenceDMapWithAdjust :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
98−sequenceDMapWithAdjust = traverseDMapWithKeyWithAdjust $ \_ -> fmap Identity
99−
100−sequenceDMapWithAdjustWithMove :: (GCompare k, Adjustable t m) => DMap k m -> Event t (PatchDMapWithMove k m) -> m (DMap k Identity, Event t (PatchDMapWithMove k Identity))
101−sequenceDMapWithAdjustWithMove = traverseDMapWithKeyWithAdjustWithMove $ \_ -> fmap Identity
102−
103−mapMapWithAdjustWithMove :: forall t m k v v'. (Adjustable 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'))
104−mapMapWithAdjustWithMove f m0 m' = do
105− (out0 :: DMap (Const2 k v) (Constant v'), out') <- traverseDMapWithKeyWithAdjustWithMove (\(Const2 k) (Identity v) -> Constant <$> f k v) (mapToDMap m0) (const2PatchDMapWithMoveWith Identity <$> m')
106− return (dmapToMapWith (\(Constant v') -> v') out0, patchDMapWithMoveToPatchMapWithMoveWith (\(Constant v') -> v') <$> out')
107−
108−--------------------------------------------------------------------------------
109−-- Deprecated functions
110−--------------------------------------------------------------------------------
111−
112−{-# DEPRECATED MonadAdjust "Use Adjustable instead" #-}
113−type MonadAdjust = Adjustable
src/Reflex/BehaviorWriter/Base.hsdeleted−204
@@ -1,204 +0,0 @@
1−{-|
2−Module: Reflex.BehaviorWriter.Base
3−Description: Implementation of MonadBehaviorWriter
4−-}
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE GADTs #-}
8−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9−{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11−{-# LANGUAGE RankNTypes #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE TupleSections #-}
14−{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−{-# LANGUAGE StandaloneDeriving #-}
17−#ifdef USE_REFLEX_OPTIMIZER
18−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19−#endif
20−module Reflex.BehaviorWriter.Base
21− ( BehaviorWriterT (..)
22− , runBehaviorWriterT
23− , withBehaviorWriterT
24− ) where
25−
26−import Control.Monad.Exception
27−import Control.Monad.Identity
28−import Control.Monad.IO.Class
29−import Control.Monad.Reader
30−import Control.Monad.Ref
31−import Control.Monad.State.Strict
32−import Data.Dependent.Map (DMap)
33−import qualified Data.Dependent.Map as DMap
34−import Data.Functor.Misc
35−import Data.IntMap (IntMap)
36−import qualified Data.IntMap as IntMap
37−import Data.Map (Map)
38−import qualified Data.Map as Map
39−import Data.Some (Some)
40−
41−import Reflex.Class
42−import Reflex.Adjustable.Class
43−import Reflex.BehaviorWriter.Class
44−import Reflex.Host.Class
45−import Reflex.PerformEvent.Class
46−import Reflex.PostBuild.Class
47−import Reflex.Query.Class
48−import Reflex.Requester.Class
49−import Reflex.TriggerEvent.Class
50−
51−-- | A basic implementation of 'MonadBehaviorWriter'.
52−newtype BehaviorWriterT t w m a = BehaviorWriterT { unBehaviorWriterT :: StateT [Behavior t w] m a }
53− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
54−
55−-- | Run a 'BehaviorWriterT' action. The behavior writer output will be provided
56−-- along with the result of the action.
57−runBehaviorWriterT :: (Monad m, Reflex t, Monoid w) => BehaviorWriterT t w m a -> m (a, Behavior t w)
58−runBehaviorWriterT (BehaviorWriterT a) = do
59− (result, ws) <- runStateT a []
60− return (result, mconcat $ reverse ws)
61−
62−-- | Map a function over the output of a 'BehaviorWriterT'.
63−withBehaviorWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m)
64− => (w -> w')
65− -> BehaviorWriterT t w m a
66− -> BehaviorWriterT t w' m a
67−withBehaviorWriterT f dw = do
68− (r, d) <- lift $ do
69− (r, d) <- runBehaviorWriterT dw
70− let d' = fmap f d
71− return (r, d')
72− tellBehavior d
73− return r
74−
75−deriving instance MonadHold t m => MonadHold t (BehaviorWriterT t w m)
76−deriving instance MonadSample t m => MonadSample t (BehaviorWriterT t w m)
77−
78−instance MonadTrans (BehaviorWriterT t w) where
79− lift = BehaviorWriterT . lift
80−
81−instance MonadRef m => MonadRef (BehaviorWriterT t w m) where
82− type Ref (BehaviorWriterT t w m) = Ref m
83− newRef = lift . newRef
84− readRef = lift . readRef
85− writeRef r = lift . writeRef r
86−
87−instance MonadAtomicRef m => MonadAtomicRef (BehaviorWriterT t w m) where
88− atomicModifyRef r = lift . atomicModifyRef r
89−
90−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (BehaviorWriterT t w m) where
91− newEventWithTrigger = lift . newEventWithTrigger
92− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
93−
94−instance (Monad m, Monoid w, Reflex t) => MonadBehaviorWriter t w (BehaviorWriterT t w m) where
95− tellBehavior w = BehaviorWriterT $ modify (w :)
96−
97−instance MonadReader r m => MonadReader r (BehaviorWriterT t w m) where
98− ask = lift ask
99− local f (BehaviorWriterT a) = BehaviorWriterT $ mapStateT (local f) a
100− reader = lift . reader
101−
102−instance PerformEvent t m => PerformEvent t (BehaviorWriterT t w m) where
103− type Performable (BehaviorWriterT t w m) = Performable m
104− performEvent_ = lift . performEvent_
105− performEvent = lift . performEvent
106−
107−instance TriggerEvent t m => TriggerEvent t (BehaviorWriterT t w m) where
108− newTriggerEvent = lift newTriggerEvent
109− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
110− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
111−
112−instance PostBuild t m => PostBuild t (BehaviorWriterT t w m) where
113− getPostBuild = lift getPostBuild
114−
115−instance MonadState s m => MonadState s (BehaviorWriterT t w m) where
116− get = lift get
117− put = lift . put
118−
119−instance Requester t m => Requester t (BehaviorWriterT t w m) where
120− type Request (BehaviorWriterT t w m) = Request m
121− type Response (BehaviorWriterT t w m) = Response m
122− requesting = lift . requesting
123− requesting_ = lift . requesting_
124−
125−instance (MonadQuery t q m, Monad m) => MonadQuery t q (BehaviorWriterT t w m) where
126− tellQueryIncremental = lift . tellQueryIncremental
127− askQueryResult = lift askQueryResult
128− queryIncremental = lift . queryIncremental
129−
130−instance (Adjustable t m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (BehaviorWriterT t w m) where
131− runWithReplace a0 a' = do
132− (result0, result') <- lift $ runWithReplace (runBehaviorWriterT a0) $ runBehaviorWriterT <$> a'
133− tellBehavior . join =<< hold (snd result0) (snd <$> result')
134− return (fst result0, fst <$> result')
135− traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust
136− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith
137−
138−traverseIntMapWithKeyWithAdjustImpl
139− :: forall t w v' p p' v m.
140− ( PatchTarget (p' (Behavior t w)) ~ IntMap (Behavior t w)
141− , Patch (p' (Behavior t w))
142− , Monoid w
143− , Reflex t
144− , MonadHold t m
145− , Functor p
146− , p ~ p'
147− )
148− => ( (IntMap.Key -> v -> m (v', Behavior t w))
149− -> IntMap v
150− -> Event t (p v)
151− -> m (IntMap (v', Behavior t w), Event t (p (v', Behavior t w)))
152− )
153− -> (IntMap.Key -> v -> BehaviorWriterT t w m v')
154− -> IntMap v
155− -> Event t (p v)
156− -> BehaviorWriterT t w m (IntMap v', Event t (p v'))
157−traverseIntMapWithKeyWithAdjustImpl base f (dm0 :: IntMap v) dm' = do
158− (result0, result') <- lift $ base (\k v -> runBehaviorWriterT $ f k v) dm0 dm'
159− let liftedResult0 = fmap fst result0
160− liftedResult' = fmap (fmap fst) result'
161− liftedWritten0 :: IntMap (Behavior t w)
162− liftedWritten0 = fmap snd result0
163− liftedWritten' = fmap (fmap snd) result'
164− i <- holdIncremental liftedWritten0 liftedWritten'
165− tellBehavior $ pull $ do
166− m <- sample $ currentIncremental i
167− mconcat . IntMap.elems <$> traverse sample m
168− return (liftedResult0, liftedResult')
169−
170−newtype BehaviorWriterTLoweredResult t w v a = BehaviorWriterTLoweredResult (v a, Behavior t w)
171−
172−traverseDMapWithKeyWithAdjustImpl
173− :: forall t w k v' p p' v m.
174− ( PatchTarget (p' (Some k) (Behavior t w)) ~ Map (Some k) (Behavior t w)
175− , Patch (p' (Some k) (Behavior t w))
176− , Monoid w
177− , Reflex t
178− , MonadHold t m
179− )
180− => ( (forall a. k a -> v a -> m (BehaviorWriterTLoweredResult t w v' a))
181− -> DMap k v
182− -> Event t (p k v)
183− -> m (DMap k (BehaviorWriterTLoweredResult t w v'), Event t (p k (BehaviorWriterTLoweredResult t w v')))
184− )
185− -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> v' a) -> p k (BehaviorWriterTLoweredResult t w v') -> p k v')
186− -> ((forall a. BehaviorWriterTLoweredResult t w v' a -> Behavior t w) -> p k (BehaviorWriterTLoweredResult t w v') -> p' (Some k) (Behavior t w))
187− -> (forall a. k a -> v a -> BehaviorWriterT t w m (v' a))
188− -> DMap k v
189− -> Event t (p k v)
190− -> BehaviorWriterT t w m (DMap k v', Event t (p k v'))
191−traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith f (dm0 :: DMap k v) dm' = do
192− (result0, result') <- lift $ base (\k v -> fmap BehaviorWriterTLoweredResult $ runBehaviorWriterT $ f k v) dm0 dm'
193− let getValue (BehaviorWriterTLoweredResult (v, _)) = v
194− getWritten (BehaviorWriterTLoweredResult (_, w)) = w
195− liftedResult0 = DMap.map getValue result0
196− liftedResult' = ffor result' $ mapPatch getValue
197− liftedWritten0 :: Map (Some k) (Behavior t w)
198− liftedWritten0 = weakenDMapWith getWritten result0
199− liftedWritten' = ffor result' $ weakenPatchWith getWritten
200− i <- holdIncremental liftedWritten0 liftedWritten'
201− tellBehavior $ pull $ do
202− m <- sample $ currentIncremental i
203− mconcat . Map.elems <$> traverse sample m
204− return (liftedResult0, liftedResult')
src/Reflex/BehaviorWriter/Class.hsdeleted−26
@@ -1,26 +0,0 @@
1−{-|
2−Module: Reflex.BehaviorWriter.Class
3−Description: This module defines the 'MonadBehaviorWriter' class
4−-}
5−{-# LANGUAGE CPP #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−#ifdef USE_REFLEX_OPTIMIZER
11−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12−#endif
13−module Reflex.BehaviorWriter.Class
14− ( MonadBehaviorWriter (..)
15− ) where
16−
17−import Control.Monad.Reader (ReaderT, lift)
18−import Reflex.Class (Behavior)
19−
20−-- | 'MonadBehaviorWriter' efficiently collects 'Behavior' values using 'tellBehavior'
21−-- and combines them monoidally to provide a 'Behavior' result.
22−class (Monad m, Monoid w) => MonadBehaviorWriter t w m | m -> t w where
23− tellBehavior :: Behavior t w -> m ()
24−
25−instance MonadBehaviorWriter t w m => MonadBehaviorWriter t w (ReaderT r m) where
26− tellBehavior = lift . tellBehavior
src/Reflex/Class.hsdeleted−1359
@@ -1,1359 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ConstraintKinds #-}
4−{-# LANGUAGE DefaultSignatures #-}
5−{-# LANGUAGE FlexibleContexts #-}
6−{-# LANGUAGE FlexibleInstances #-}
7−{-# LANGUAGE FunctionalDependencies #-}
8−{-# LANGUAGE GADTs #-}
9−{-# LANGUAGE LambdaCase #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11−{-# LANGUAGE NoMonomorphismRestriction #-}
12−{-# LANGUAGE PolyKinds #-}
13−{-# LANGUAGE RankNTypes #-}
14−{-# LANGUAGE RecursiveDo #-}
15−{-# LANGUAGE RoleAnnotations #-}
16−{-# LANGUAGE ScopedTypeVariables #-}
17−{-# LANGUAGE TypeFamilies #-}
18−{-# LANGUAGE TypeOperators #-}
19−{-# LANGUAGE UndecidableInstances #-}
20−#ifdef USE_REFLEX_OPTIMIZER
21−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
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− , mergeInt
32− , coerceBehavior
33− , coerceEvent
34− , coerceDynamic
35− , MonadSample (..)
36− , MonadHold (..)
37− -- ** 'fan'-related types
38− , EventSelector (..)
39− , EventSelectorInt (..)
40− -- ** 'Incremental'-related types
41− -- * Convenience functions
42− , constDyn
43− , pushAlways
44− -- ** Combining 'Event's
45− , leftmost
46− , mergeMap
47− , mergeIntMap
48− , mergeMapIncremental
49− , mergeMapIncrementalWithMove
50− , mergeIntMapIncremental
51− , coincidencePatchMap
52− , coincidencePatchMapWithMove
53− , coincidencePatchIntMap
54− , mergeList
55− , mergeWith
56− , difference
57− , alignEventWithMaybe
58− -- ** Breaking up 'Event's
59− , splitE
60− , fanEither
61− , fanThese
62− , fanMap
63− , dmapToThese
64− , EitherTag (..)
65− , eitherToDSum
66− , dsumToEither
67− , factorEvent
68− , filterEventKey
69− -- ** Collapsing 'Event . Event'
70− , switchHold
71− , switchHoldPromptly
72− , switchHoldPromptOnly
73− , switchHoldPromptOnlyIncremental
74− -- ** Using 'Event's to sample 'Behavior's
75− , tag
76− , tagMaybe
77− , attach
78− , attachWith
79− , attachWithMaybe
80− -- ** Blocking an 'Event' based on a 'Behavior'
81− , gate
82− -- ** Combining 'Dynamic's
83− , distributeDMapOverDynPure
84− , distributeListOverDyn
85− , distributeListOverDynWith
86− , zipDyn
87− , zipDynWith
88− -- ** Accumulating state
89− , Accumulator (..)
90− , accumDyn
91− , accumMDyn
92− , accumMaybeDyn
93− , accumMaybeMDyn
94− , mapAccumDyn
95− , mapAccumMDyn
96− , mapAccumMaybeDyn
97− , mapAccumMaybeMDyn
98− , accumB
99− , accumMB
100− , accumMaybeB
101− , accumMaybeMB
102− , mapAccumB
103− , mapAccumMB
104− , mapAccumMaybeB
105− , mapAccumMaybeMB
106− , mapAccum_
107− , mapAccumM_
108− , mapAccumMaybe_
109− , mapAccumMaybeM_
110− , accumIncremental
111− , accumMIncremental
112− , accumMaybeIncremental
113− , accumMaybeMIncremental
114− , mapAccumIncremental
115− , mapAccumMIncremental
116− , mapAccumMaybeIncremental
117− , mapAccumMaybeMIncremental
118− , zipListWithEvent
119− , numberOccurrences
120− , numberOccurrencesFrom
121− , numberOccurrencesFrom_
122− , (<@>)
123− , (<@)
124− , tailE
125− , headTailE
126− , takeWhileE
127− , takeWhileJustE
128− , dropWhileE
129− , takeDropWhileJustE
130− , switcher
131− -- * Debugging functions
132− , traceEvent
133− , traceEventWith
134− -- * Unsafe functions
135− , unsafeDynamic
136− , unsafeMapIncremental
137− -- * 'FunctorMaybe'
138− , FunctorMaybe (..)
139− , fforMaybe
140− , ffilter
141− , filterLeft
142− , filterRight
143− -- * Miscellaneous convenience functions
144− , ffor
145− , ffor2
146− , ffor3
147− -- * Deprecated functions
148− , appendEvents
149− , onceE
150− , sequenceThese
151− , fmapMaybeCheap
152− , fmapCheap
153− , fforCheap
154− , fforMaybeCheap
155− , pushAlwaysCheap
156− , tagCheap
157− , mergeWithCheap
158− , mergeWithCheap'
159− , switchPromptly
160− , switchPromptOnly
161− -- * Slow, but general, implementations
162− , slowHeadE
163− ) where
164−
165−import Control.Applicative
166−import Control.Monad.Identity
167−import Control.Monad.Reader
168−import Control.Monad.State.Strict
169−import Control.Monad.Trans.Cont (ContT)
170−import Control.Monad.Trans.Except (ExceptT)
171−import Control.Monad.Trans.RWS (RWST)
172−import Control.Monad.Trans.Writer (WriterT)
173−import Data.Align
174−import Data.Bifunctor
175−import Data.Coerce
176−import Data.Default
177−import Data.Dependent.Map (DMap, DSum (..))
178−import qualified Data.Dependent.Map as DMap
179−import Data.Functor.Compose
180−import Data.Functor.Product
181−import Data.GADT.Compare (GEq (..), GCompare (..), (:~:) (..))
182−import Data.FastMutableIntMap (PatchIntMap)
183−import Data.Foldable
184−import Data.Functor.Bind
185−import Data.Functor.Misc
186−import Data.Functor.Plus
187−import Data.IntMap.Strict (IntMap)
188−import qualified Data.IntMap.Strict as IntMap
189−import Data.List.NonEmpty (NonEmpty (..))
190−import Data.Map (Map)
191−import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
192−import Data.Some (Some)
193−import qualified Data.Some as Some
194−import Data.String
195−import Data.These
196−import Data.Type.Coercion
197−import Reflex.FunctorMaybe
198−import Reflex.Patch
199−import qualified Reflex.Patch.MapWithMove as PatchMapWithMove
200−
201−import Debug.Trace (trace)
202−
203−-- | The 'Reflex' class contains all the primitive functionality needed for
204−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
205−-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
206−-- and the type of the timeline determines the FRP engine to be used. For most
207−-- purposes, the 'Reflex.Spider' implementation is recommended.
208−class ( MonadHold t (PushM t)
209− , MonadSample t (PullM t)
210− , MonadFix (PushM t)
211− , Functor (Dynamic t)
212− , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
213− , Monad (Dynamic t)
214− ) => Reflex t where
215− -- | A container for a value that can change over time. 'Behavior's can be
216− -- sampled at will, but it is not possible to be notified when they change
217− data Behavior t :: * -> *
218− -- | A stream of occurrences. During any given frame, an 'Event' is either
219− -- occurring or not occurring; if it is occurring, it will contain a value of
220− -- the given type (its "occurrence type")
221− data Event t :: * -> *
222− -- | A container for a value that can change over time and allows
223− -- notifications on changes. Basically a combination of a 'Behavior' and an
224− -- 'Event', with a rule that the 'Behavior' will change if and only if the
225− -- 'Event' fires.
226− data Dynamic t :: * -> *
227− -- | An 'Incremental' is a more general form of a 'Dynamic'.
228− -- Instead of always fully replacing the value, only parts of it can be patched.
229− -- This is only needed for performance critical code via `mergeIncremental` to make small
230− -- changes to large values.
231− data Incremental t :: * -> *
232− -- | A monad for doing complex push-based calculations efficiently
233− type PushM t :: * -> *
234− -- | A monad for doing complex pull-based calculations efficiently
235− type PullM t :: * -> *
236− -- | An 'Event' with no occurrences
237− never :: Event t a
238− -- | Create a 'Behavior' that always has the given value
239− 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
240− -- | Create an 'Event' from another 'Event'; the provided function can sample
241− -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
242− -- (Just) or non-occurring (Nothing) result
243− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
244− -- | 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.
245− pushCheap :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
246− -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
247− -- recomputed whenever any of the read 'Behavior's changes
248− pull :: PullM t a -> Behavior t a
249− -- | Merge a collection of events; the resulting 'Event' will only occur if at
250− -- least one input event is occurring, and will contain all of the input keys
251− -- that are occurring simultaneously
252− 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
253− -- | Efficiently fan-out an event to many destinations. This function should
254− -- be partially applied, and then the result applied repeatedly to create
255− -- child events
256− 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
257− -- | Create an 'Event' that will occur whenever the currently-selected input
258− -- 'Event' occurs
259− switch :: Behavior t (Event t a) -> Event t a
260− -- | Create an 'Event' that will occur whenever the input event is occurring
261− -- and its occurrence value, another 'Event', is also occurring
262− coincidence :: Event t (Event t a) -> Event t a
263− -- | Extract the 'Behavior' of a 'Dynamic'.
264− current :: Dynamic t a -> Behavior t a
265− -- | Extract the 'Event' of the 'Dynamic'.
266− updated :: Dynamic t a -> Event t a
267− -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
268− -- recent firing of the given 'Event', if any.
269− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
270− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
271− -- in the same way that the accumulated application of patches would change
272− -- that value.
273− unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
274− -- | Create a merge whose parents can change over time
275− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
276− -- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
277− mergeIncrementalWithMove :: GCompare k => Incremental t (PatchDMapWithMove k (Event t)) -> Event t (DMap k Identity)
278− -- | Extract the 'Behavior' component of an 'Incremental'
279− currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
280− -- | Extract the 'Event' component of an 'Incremental'
281− updatedIncremental :: Patch p => Incremental t p -> Event t p
282− -- | Convert an 'Incremental' to a 'Dynamic'
283− incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
284− -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
285− -- occurrence type
286− behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
287− -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
288− -- occurrence type
289− eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
290− -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
291− -- occurrence type
292− dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
293− mergeIntIncremental :: Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
294− fanInt :: Event t (IntMap a) -> EventSelectorInt t a
295−
296−--TODO: Specialize this so that we can take advantage of knowing that there's no changing going on
297−mergeInt :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
298−mergeInt m = mergeIntIncremental $ unsafeBuildIncremental (return m) never
299−
300−-- | Coerce a 'Behavior' between representationally-equivalent value types
301−coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
302−coerceBehavior = coerceWith $ behaviorCoercion Coercion
303−
304−-- | Coerce an 'Event' between representationally-equivalent occurrence types
305−coerceEvent :: (Reflex t, Coercible a b) => Event t a -> Event t b
306−coerceEvent = coerceWith $ eventCoercion Coercion
307−
308−-- | Coerce a 'Dynamic' between representationally-equivalent value types
309−coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
310−coerceDynamic = coerceWith $ dynamicCoercion Coercion
311−
312−-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
313−-- _must_ change when and only when the 'Event' fires, such that the
314−-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
315−-- if this is not the case, the resulting 'Dynamic' will behave
316−-- nondeterministically.
317−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
318−unsafeDynamic = unsafeBuildDynamic . sample
319−
320−-- | Construct a 'Dynamic' value that never changes
321−constDyn :: Reflex t => a -> Dynamic t a
322−constDyn = pure
323−
324−instance (Reflex t, Default a) => Default (Dynamic t a) where
325− def = pure def
326−
327−-- | 'MonadSample' designates monads that can read the current value of a
328−-- 'Behavior'. This includes both 'PullM' and 'PushM'.
329−class (Applicative m, Monad m) => MonadSample t m | m -> t where
330− -- | Get the current value in the 'Behavior'
331− sample :: Behavior t a -> m a
332−
333−-- | 'MonadHold' designates monads that can create new 'Behavior's based on
334−-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
335−-- is required to create any stateful computations with Reflex.
336−class MonadSample t m => MonadHold t m where
337− -- | Create a new 'Behavior' whose value will initially be equal to the given
338− -- value and will be updated whenever the given 'Event' occurs. The update
339− -- takes effect immediately after the 'Event' occurs; if the occurrence that
340− -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
341− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
342− -- one.
343− hold :: a -> Event t a -> m (Behavior t a)
344− default hold :: (m ~ f m', MonadTrans f, MonadHold t m') => a -> Event t a -> m (Behavior t a)
345− hold v0 = lift . hold v0
346− -- | Create a 'Dynamic' value using the given initial value that changes every
347− -- time the 'Event' occurs.
348− holdDyn :: a -> Event t a -> m (Dynamic t a)
349− default holdDyn :: (m ~ f m', MonadTrans f, MonadHold t m') => a -> Event t a -> m (Dynamic t a)
350− holdDyn v0 = lift . holdDyn v0
351− -- | Create an 'Incremental' value using the given initial value that changes
352− -- every time the 'Event' occurs.
353− holdIncremental :: Patch p => PatchTarget p -> Event t p -> m (Incremental t p)
354− default holdIncremental :: (Patch p, m ~ f m', MonadTrans f, MonadHold t m') => PatchTarget p -> Event t p -> m (Incremental t p)
355− holdIncremental v0 = lift . holdIncremental v0
356− buildDynamic :: PushM t a -> Event t a -> m (Dynamic t a)
357− {-
358− default buildDynamic :: (m ~ f m', MonadTrans f, MonadHold t m') => PullM t a -> Event t a -> m (Dynamic t a)
359− buildDynamic getV0 = lift . buildDynamic getV0
360− -}
361− -- | Create a new 'Event' that only occurs only once, on the first occurrence of
362− -- the supplied 'Event'.
363− headE :: Event t a -> m (Event t a)
364−
365−accumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> p) -> PatchTarget p -> Event t b -> m (Incremental t p)
366−accumIncremental f = accumMaybeIncremental $ \v o -> Just $ f v o
367−accumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t p) -> PatchTarget p -> Event t b -> m (Incremental t p)
368−accumMIncremental f = accumMaybeMIncremental $ \v o -> Just <$> f v o
369−accumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> Maybe p) -> PatchTarget p -> Event t b -> m (Incremental t p)
370−accumMaybeIncremental f = accumMaybeMIncremental $ \v o -> return $ f v o
371−accumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p)) -> PatchTarget p -> Event t b -> m (Incremental t p)
372−accumMaybeMIncremental f z e = do
373− rec let e' = flip push e $ \o -> do
374− v <- sample $ currentIncremental d'
375− f v o
376− d' <- holdIncremental z e'
377− return d'
378−mapAccumIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
379−mapAccumIncremental f = mapAccumMaybeIncremental $ \v o -> bimap Just Just $ f v o
380−mapAccumMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (p, c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
381−mapAccumMIncremental f = mapAccumMaybeMIncremental $ \v o -> bimap Just Just <$> f v o
382−mapAccumMaybeIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
383−mapAccumMaybeIncremental f = mapAccumMaybeMIncremental $ \v o -> return $ f v o
384−mapAccumMaybeMIncremental :: (Reflex t, Patch p, MonadHold t m, MonadFix m) => (PatchTarget p -> b -> PushM t (Maybe p, Maybe c)) -> PatchTarget p -> Event t b -> m (Incremental t p, Event t c)
385−mapAccumMaybeMIncremental f z e = do
386− rec let e' = flip push e $ \o -> do
387− v <- sample $ currentIncremental d'
388− result <- f v o
389− return $ case result of
390− (Nothing, Nothing) -> Nothing
391− _ -> Just result
392− d' <- holdIncremental z $ fmapMaybe fst e'
393− return (d', fmapMaybe snd e')
394−
395−slowHeadE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
396−slowHeadE e = do
397− rec be <- hold e $ fmapCheap (const never) e'
398− let e' = switch be
399− return e'
400−
401−-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
402−-- key. This is much more efficient than filtering for each key with
403−-- 'fmapMaybe'.
404−newtype EventSelector t k = EventSelector
405− { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
406− -- determined by the type of the key, so this can be used to fan-out
407− -- 'Event's whose sub-'Event's have different types.
408− --
409− -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
410− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
411− -- occurrences of an 'Event'.
412− select :: forall a. k a -> Event t a
413− }
414−
415−newtype EventSelectorInt t a = EventSelectorInt { selectInt :: Int -> Event t a }
416−
417−--------------------------------------------------------------------------------
418−-- Instances
419−--------------------------------------------------------------------------------
420−
421−instance MonadSample t m => MonadSample t (ReaderT r m) where
422− sample = lift . sample
423−
424−instance MonadHold t m => MonadHold t (ReaderT r m) where
425− hold a0 = lift . hold a0
426− holdDyn a0 = lift . holdDyn a0
427− holdIncremental a0 = lift . holdIncremental a0
428− buildDynamic a0 = lift . buildDynamic a0
429− headE = lift . headE
430−
431−instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
432− sample = lift . sample
433−
434−instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
435− hold a0 = lift . hold a0
436− holdDyn a0 = lift . holdDyn a0
437− holdIncremental a0 = lift . holdIncremental a0
438− buildDynamic a0 = lift . buildDynamic a0
439− headE = lift . headE
440−
441−instance MonadSample t m => MonadSample t (StateT s m) where
442− sample = lift . sample
443−
444−instance MonadHold t m => MonadHold t (StateT s m) where
445− hold a0 = lift . hold a0
446− holdDyn a0 = lift . holdDyn a0
447− holdIncremental a0 = lift . holdIncremental a0
448− buildDynamic a0 = lift . buildDynamic a0
449− headE = lift . headE
450−
451−instance MonadSample t m => MonadSample t (ExceptT e m) where
452− sample = lift . sample
453−
454−instance MonadHold t m => MonadHold t (ExceptT e m) where
455− hold a0 = lift . hold a0
456− holdDyn a0 = lift . holdDyn a0
457− holdIncremental a0 = lift . holdIncremental a0
458− buildDynamic a0 = lift . buildDynamic a0
459− headE = lift . headE
460−
461−instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
462− sample = lift . sample
463−
464−instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
465− hold a0 = lift . hold a0
466− holdDyn a0 = lift . holdDyn a0
467− holdIncremental a0 = lift . holdIncremental a0
468− buildDynamic a0 = lift . buildDynamic a0
469− headE = lift . headE
470−
471−instance MonadSample t m => MonadSample t (ContT r m) where
472− sample = lift . sample
473−
474−instance MonadHold t m => MonadHold t (ContT r m) where
475− hold a0 = lift . hold a0
476− holdDyn a0 = lift . holdDyn a0
477− holdIncremental a0 = lift . holdIncremental a0
478− buildDynamic a0 = lift . buildDynamic a0
479− headE = lift . headE
480−
481−--------------------------------------------------------------------------------
482−-- Convenience functions
483−--------------------------------------------------------------------------------
484−
485−-- | Create an 'Event' from another 'Event'. The provided function can sample
486−-- 'Behavior's and hold 'Event's.
487−pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
488−pushAlways f = push (fmap Just . f)
489−
490−-- | Flipped version of 'fmap'.
491−ffor :: Functor f => f a -> (a -> b) -> f b
492−ffor = flip fmap
493−
494−-- | Rotated version of 'liftA2'.
495−ffor2 :: Applicative f => f a -> f b -> (a -> b -> c) -> f c
496−ffor2 a b f = liftA2 f a b
497−
498−-- | Rotated version of 'liftA3'.
499−ffor3 :: Applicative f => f a -> f b -> f c -> (a -> b -> c -> d) -> f d
500−ffor3 a b c f = liftA3 f a b c
501−
502−instance Reflex t => Applicative (Behavior t) where
503− pure = constant
504− f <*> x = pull $ sample f `ap` sample x
505− _ *> b = b
506− a <* _ = a
507−
508−instance Reflex t => Apply (Behavior t) where
509− (<.>) = (<*>)
510−
511−instance Reflex t => Bind (Behavior t) where
512− (>>-) = (>>=)
513−
514−instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
515− (/) = liftA2 (/)
516− fromRational = pure . fromRational
517− recip = fmap recip
518−
519−instance Reflex t => Functor (Behavior t) where
520− fmap f = pull . fmap f . sample
521−
522−instance (Reflex t, IsString a) => IsString (Behavior t a) where
523− fromString = pure . fromString
524−
525−instance Reflex t => Monad (Behavior t) where
526− a >>= f = pull $ sample a >>= sample . f
527− -- 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.
528− return = constant
529− fail = error "Monad (Behavior t) does not support fail"
530−
531−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
532− mempty = constant mempty
533− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
534− mconcat = pull . fmap mconcat . mapM sample
535−
536−instance (Reflex t, Num a) => Num (Behavior t a) where
537− (+) = liftA2 (+)
538− (-) = liftA2 (-)
539− (*) = liftA2 (*)
540− abs = fmap abs
541− fromInteger = pure . fromInteger
542− negate = fmap negate
543− signum = fmap signum
544−
545−instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
546− a <> b = pull $ liftM2 (<>) (sample a) (sample b)
547− sconcat = pull . fmap sconcat . mapM sample
548−#if MIN_VERSION_semigroups(0,17,0)
549− stimes n = fmap $ stimes n
550−#else
551− times1p n = fmap $ times1p n
552−#endif
553−
554−-- | Flipped version of 'fmapMaybe'.
555−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
556−fforMaybe = flip fmapMaybe
557−
558−-- | Filter 'f a' using the provided predicate.
559−-- Relies on 'fforMaybe'.
560−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
561−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
562−
563−-- | Filter 'Left's from 'f (Either a b)' into 'a'.
564−filterLeft :: FunctorMaybe f => f (Either a b) -> f a
565−filterLeft = fmapMaybe (either Just (const Nothing))
566−
567−-- | Filter 'Right's from 'f (Either a b)' into 'b'.
568−filterRight :: FunctorMaybe f => f (Either a b) -> f b
569−filterRight = fmapMaybe (either (const Nothing) Just)
570−
571−-- | Left-biased event union (prefers left event on simultaneous
572−-- occurrence).
573−instance Reflex t => Alt (Event t) where
574− ev1 <!> ev2 = leftmost [ev1, ev2]
575−
576−-- | 'Event' intersection (convenient interface to 'coincidence').
577−instance Reflex t => Apply (Event t) where
578− evf <.> evx = coincidence (fmap (<$> evx) evf)
579−
580−-- | 'Event' intersection (convenient interface to 'coincidence').
581−instance Reflex t => Bind (Event t) where
582− evx >>- f = coincidence (f <$> evx)
583− join = coincidence
584−
585−instance Reflex t => Functor (Event t) where
586− {-# INLINE fmap #-}
587− fmap f = fmapMaybe $ Just . f
588− {-# INLINE (<$) #-}
589− x <$ e = fmapCheap (const x) e
590−
591−instance Reflex t => FunctorMaybe (Event t) where
592− {-# INLINE fmapMaybe #-}
593− fmapMaybe f = push $ return . f
594−
595−-- | Never: @'zero' = 'never'@.
596−instance Reflex t => Plus (Event t) where
597− zero = never
598−
599−-- | Replace each occurrence value of the 'Event' with the value of the
600−-- 'Behavior' at the time of that occurrence.
601−tag :: Reflex t => Behavior t b -> Event t a -> Event t b
602−tag b = pushAlways $ \_ -> sample b
603−
604−-- | Replace each occurrence value of the 'Event' with the value of the
605−-- 'Behavior' at that time; if it is 'Just', fire with the contained value; if
606−-- it is 'Nothing', drop the occurrence.
607−tagMaybe :: Reflex t => Behavior t (Maybe b) -> Event t a -> Event t b
608−tagMaybe b = push $ \_ -> sample b
609−
610−-- | Create a new 'Event' that combines occurrences of supplied 'Event' with the
611−-- current value of the 'Behavior'.
612−attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
613−attach = attachWith (,)
614−
615−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
616−-- combining it with the current value of the 'Behavior'.
617−attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
618−attachWith f = attachWithMaybe $ \a b -> Just $ f a b
619−
620−-- | Create a new 'Event' by combining each occurrence with the current value of
621−-- the 'Behavior'. The occurrence is discarded if the combining function returns
622−-- Nothing
623−attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
624−attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
625−
626−-- | Create a new 'Event' that occurs on all but the first occurrence of the
627−-- supplied 'Event'.
628−tailE :: (Reflex t, MonadHold t m) => Event t a -> m (Event t a)
629−tailE e = snd <$> headTailE e
630−
631−-- | Create a tuple of two 'Event's with the first one occurring only the first
632−-- time the supplied 'Event' occurs and the second occurring on all but the first
633−-- occurrence.
634−headTailE :: (Reflex t, MonadHold t m) => Event t a -> m (Event t a, Event t a)
635−headTailE e = do
636− eHead <- headE e
637− be <- hold never $ fmap (const e) eHead
638− return (eHead, switch be)
639−
640−-- | Take the streak of occurrences starting at the current time for which the
641−-- event returns 'True'.
642−--
643−-- Starting at the current time, fire all the occurrences of the 'Event' for
644−-- which the given predicate returns 'True'. When 'False' first is returned,
645−-- do not fire, and permanently stop firing, even if 'True' values would have
646−-- been encountered later.
647−takeWhileE
648− :: forall t m a
649− . (Reflex t, MonadFix m, MonadHold t m)
650− => (a -> Bool)
651− -> Event t a
652− -> m (Event t a)
653−takeWhileE f = takeWhileJustE $ \v -> guard (f v) $> v
654−
655−-- | Take the streak of occurrences starting at the current time for which the
656−-- event returns 'Just b'.
657−--
658−-- Starting at the current time, fire all the occurrences of the 'Event' for
659−-- which the given predicate returns 'Just b'. When 'Nothing' first is returned,
660−-- do not fire, and permanently stop firing, even if 'Just b' values would have
661−-- been encountered later.
662−takeWhileJustE
663− :: forall t m a b
664− . (Reflex t, MonadFix m, MonadHold t m)
665− => (a -> Maybe b)
666− -> Event t a
667− -> m (Event t b)
668−takeWhileJustE f e = do
669− rec let (eBad, eTrue) = fanEither $ ffor e' $ \a -> case f a of
670− Nothing -> Left never
671− Just b -> Right b
672− eFirstBad <- headE eBad
673− e' <- switchHold e eFirstBad
674− return eTrue
675−
676−-- | Drop the streak of occurrences starting at the current time for which the
677−-- event returns 'True'.
678−--
679−-- Starting at the current time, do not fire all the occurrences of the 'Event'
680−-- for which the given predicate returns 'True'. When 'False' is first
681−-- returned, do fire, and permanently continue firing, even if 'True' values
682−-- would have been encountered later.
683−dropWhileE
684− :: forall t m a
685− . (Reflex t, MonadFix m, MonadHold t m)
686− => (a -> Bool)
687− -> Event t a
688− -> m (Event t a)
689−dropWhileE f e = snd <$> takeDropWhileJustE (\v -> guard (f v) $> v) e
690−
691−-- | Both take and drop the streak of occurrences starting at the current time
692−-- for which the event returns 'Just b'.
693−--
694−-- For the left event, starting at the current time, fire all the occurrences
695−-- of the 'Event' for which the given function returns 'Just b'. When
696−-- 'Nothing' is returned, do not fire, and permanently stop firing, even if
697−-- 'Just b' values would have been encountered later.
698−--
699−-- For the right event, do not fire until the first occurrence where the given
700−-- function returns 'Nothing', and fire that one and all subsequent
701−-- occurrences. Even if the function would have again returned 'Just b', keep
702−-- on firing.
703−takeDropWhileJustE
704− :: forall t m a b
705− . (Reflex t, MonadFix m, MonadHold t m)
706− => (a -> Maybe b)
707− -> Event t a
708− -> m (Event t b, Event t a)
709−takeDropWhileJustE f e = do
710− rec let (eBad, eGood) = fanEither $ ffor e' $ \a -> case f a of
711− Nothing -> Left ()
712− Just b -> Right b
713− eFirstBad <- headE eBad
714− e' <- switchHold e (never <$ eFirstBad)
715− eRest <- switchHoldPromptOnly never (e <$ eFirstBad)
716− return (eGood, eRest)
717−
718−-- | Split the supplied 'Event' into two individual 'Event's occurring at the
719−-- same time with the respective values from the tuple.
720−splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
721−splitE e = (fmap fst e, fmap snd e)
722−
723−-- | Print the supplied 'String' and the value of the 'Event' on each
724−-- occurrence. This should /only/ be used for debugging.
725−--
726−-- Note: As with Debug.Trace.trace, the message will only be printed if the
727−-- 'Event' is actually used.
728−traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
729−traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
730−
731−-- | Print the output of the supplied function on each occurrence of the
732−-- 'Event'. This should /only/ be used for debugging.
733−--
734−-- Note: As with Debug.Trace.trace, the message will only be printed if the
735−-- 'Event' is actually used.
736−traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
737−traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
738−
739−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
740− (<>) = alignWith (mergeThese (<>))
741− sconcat = fmap sconcat . mergeList . toList
742−#if MIN_VERSION_semigroups(0,17,0)
743− stimes n = fmap $ stimes n
744−#else
745− times1p n = fmap $ times1p n
746−#endif
747−
748−instance (Semigroup a, Reflex t) => Monoid (Event t a) where
749− mempty = never
750− mappend = (<>)
751− mconcat = fmap sconcat . mergeList
752−
753−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
754−-- list occurs. If multiple occur at the same time they are folded from the left
755−-- with the given function.
756−{-# INLINE mergeWith #-}
757−mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
758−mergeWith = mergeWith' id
759−
760−{-# INLINE mergeWith' #-}
761−mergeWith' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
762−mergeWith' f g es = fmap (Prelude.foldl1 g . fmap f)
763− . mergeInt
764− . IntMap.fromDistinctAscList
765− $ zip [0 :: Int ..] es
766−
767−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
768−-- list occurs. If multiple occur at the same time the value is the value of the
769−-- leftmost event.
770−{-# INLINE leftmost #-}
771−leftmost :: Reflex t => [Event t a] -> Event t a
772−leftmost = mergeWith const
773−
774−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
775−-- list occurs and has a list of the values of all 'Event's occurring at that
776−-- time.
777−mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
778−mergeList [] = never
779−mergeList es = mergeWithFoldCheap' id es
780−
781−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
782−unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
783−
784−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
785−-- occurs if at least one of them occurs and has a map of values of all 'Event's
786−-- occurring at that time.
787−mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
788−mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
789−
790−-- | Like 'mergeMap' but for 'IntMap'.
791−mergeIntMap :: Reflex t => IntMap (Event t a) -> Event t (IntMap a)
792−mergeIntMap = fmap dmapToIntMap . merge . intMapWithFunctorToDMap
793−
794−-- | Create a merge whose parents can change over time
795−mergeMapIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t a)) -> Event t (Map k a)
796−mergeMapIncremental = fmap dmapToMap . mergeIncremental . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWith id)
797−
798−-- | Create a merge whose parents can change over time
799−mergeIntMapIncremental :: Reflex t => Incremental t (PatchIntMap (Event t a)) -> Event t (IntMap a)
800−mergeIntMapIncremental = fmap dmapToIntMap . mergeIncremental . unsafeMapIncremental intMapWithFunctorToDMap (const2IntPatchDMapWith id)
801−
802−-- | Experimental: Create a merge whose parents can change over time; changing the key of an Event is more efficient than with mergeIncremental
803−mergeMapIncrementalWithMove :: (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Event t a)) -> Event t (Map k a)
804−mergeMapIncrementalWithMove = fmap dmapToMap . mergeIncrementalWithMove . unsafeMapIncremental mapWithFunctorToDMap (const2PatchDMapWithMoveWith id)
805−
806−-- | Split the event into separate events for 'Left' and 'Right' values.
807−fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
808−fanEither e =
809− let justLeft = either Just (const Nothing)
810− justRight = either (const Nothing) Just
811− in (fmapMaybe justLeft e, fmapMaybe justRight e)
812−
813−-- | Split the event into separate events for 'This' and 'That' values,
814−-- allowing them to fire simultaneously when the input value is 'These'.
815−fanThese :: Reflex t => Event t (These a b) -> (Event t a, Event t b)
816−fanThese e =
817− let this (This x) = Just x
818− this (These x _) = Just x
819− this _ = Nothing
820− that (That y) = Just y
821− that (These _ y) = Just y
822− that _ = Nothing
823− in (fmapMaybe this e, fmapMaybe that e)
824−
825−-- | Split the event into an 'EventSelector' that allows efficient selection of
826−-- the individual 'Event's.
827−fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
828−fanMap = fan . fmap mapToDMap
829−
830−-- | Switches to the new event whenever it receives one. Only the old event is
831−-- considered the moment a new one is switched in; the output event will fire at
832−-- that moment if only if the old event does.
833−--
834−-- Because the simultaneous firing case is irrelevant, this function imposes
835−-- laxer "timing requirements" on the overall circuit, avoiding many potential
836−-- cyclic dependency / metastability failures. It's also more performant. Use
837−-- this rather than 'switchHoldPromptly' and 'switchHoldPromptOnly' unless you
838−-- are absolutely sure you need to act on the new event in the coincidental
839−-- case.
840−switchHold :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
841−switchHold ea0 eea = switch <$> hold ea0 eea
842−
843−-- | Switches to the new event whenever it receives one. Whenever a new event is
844−-- provided, if it is firing, its value will be the resulting event's value; if
845−-- it is not firing, but the old one is, the old one's value will be used.
846−--
847−-- 'switchHold', by always forwarding the old event the moment it is switched
848−-- out, avoids many potential cyclic dependency problems / metastability
849−-- problems. It's also more performant. Use it instead unless you are sure you
850−-- cannot.
851−switchHoldPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
852−switchHoldPromptly ea0 eea = do
853− bea <- hold ea0 eea
854− let eLag = switch bea
855− eCoincidences = coincidence eea
856− return $ leftmost [eCoincidences, eLag]
857−
858−-- | switches to a new event whenever it receives one. At the moment of
859−-- switching, the old event will be ignored if it fires, and the new one will be
860−-- used if it fires; this is the opposite of 'switch', which will use only the
861−-- old value.
862−--
863−-- 'switchHold', by always forwarding the old event the moment it is switched
864−-- out, avoids many potential cyclic dependency problems / metastability
865−-- problems. It's also more performant. Use it instead unless you are sure you
866−-- cannot.
867−switchHoldPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
868−switchHoldPromptOnly e0 e' = do
869− eLag <- switch <$> hold e0 e'
870− return $ coincidence $ leftmost [e', eLag <$ eLag]
871−
872−-- | When the given outer event fires, condense the inner events into the contained patch. Non-firing inner events will be replaced with deletions.
873−coincidencePatchMap :: (Reflex t, Ord k) => Event t (PatchMap k (Event t v)) -> Event t (PatchMap k v)
874−coincidencePatchMap e = fmapCheap PatchMap $ coincidence $ ffor e $ \(PatchMap m) -> mergeMap $ ffor m $ \case
875− Nothing -> fmapCheap (const Nothing) e
876− Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
877−
878−-- | See 'coincicencePatchMap'
879−coincidencePatchIntMap :: Reflex t => Event t (PatchIntMap (Event t v)) -> Event t (PatchIntMap v)
880−coincidencePatchIntMap e = fmapCheap PatchIntMap $ coincidence $ ffor e $ \(PatchIntMap m) -> mergeIntMap $ ffor m $ \case
881− Nothing -> fmapCheap (const Nothing) e
882− Just ev -> leftmost [fmapCheap Just ev, fmapCheap (const Nothing) e]
883−
884−-- | See 'coincicencePatchMap'
885−coincidencePatchMapWithMove :: (Reflex t, Ord k) => Event t (PatchMapWithMove k (Event t v)) -> Event t (PatchMapWithMove k v)
886−coincidencePatchMapWithMove e = fmapCheap unsafePatchMapWithMove $ coincidence $ ffor e $ \p -> mergeMap $ ffor (unPatchMapWithMove p) $ \ni -> case PatchMapWithMove._nodeInfo_from ni of
887− PatchMapWithMove.From_Delete -> fforCheap e $ \_ ->
888− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
889− PatchMapWithMove.From_Move k -> fforCheap e $ \_ ->
890− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Move k }
891− PatchMapWithMove.From_Insert ev -> leftmost
892− [ fforCheap ev $ \v ->
893− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Insert v }
894− , fforCheap e $ \_ ->
895− ni { PatchMapWithMove._nodeInfo_from = PatchMapWithMove.From_Delete }
896− ]
897−
898−switchHoldPromptOnlyIncremental
899− :: forall t m p pt w
900− . ( Reflex t
901− , MonadHold t m
902− , Patch (p (Event t w))
903− , PatchTarget (p (Event t w)) ~ pt (Event t w)
904− , Patch (p w)
905− , PatchTarget (p w) ~ pt w
906− , Monoid (pt w)
907− )
908− => (Incremental t (p (Event t w)) -> Event t (pt w))
909− -> (Event t (p (Event t w)) -> Event t (p w))
910− -> pt (Event t w)
911− -> Event t (p (Event t w))
912− -> m (Event t (pt w))
913−switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch e0 e' = do
914− lag <- mergePatchIncremental <$> holdIncremental e0 e'
915− pure $ ffor (align lag (coincidencePatch e')) $ \case
916− This old -> old
917− That new -> new `applyAlways` mempty
918− These old new -> new `applyAlways` old
919−
920−instance Reflex t => Align (Event t) where
921− nil = never
922− align = alignEventWithMaybe Just
923−
924−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
925−-- the 'Behavior' is true at the time of occurrence.
926−gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
927−gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
928−
929−-- | Create a new behavior given a starting behavior and switch to a the behvior
930−-- carried by the event when it fires.
931−switcher :: (Reflex t, MonadHold t m)
932− => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
933−switcher b eb = pull . (sample <=< sample) <$> hold b eb
934−
935−instance (Reflex t, IsString a) => IsString (Dynamic t a) where
936− fromString = pure . fromString
937−
938−-- | Combine two 'Dynamic's. The result will change whenever either (or both)
939−-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
940−zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
941−zipDyn = zipDynWith (,)
942−
943−-- | Combine two 'Dynamic's with a combining function. The result will change
944−-- whenever either (or both) input 'Dynamic' changes.
945−-- More efficient than liftA2.
946−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
947−zipDynWith f da db =
948− let eab = align (updated da) (updated db)
949− ec = flip push eab $ \o -> do
950− (a, b) <- case o of
951− This a -> do
952− b <- sample $ current db
953− return (a, b)
954− That b -> do
955− a <- sample $ current da
956− return (a, b)
957− These a b -> return (a, b)
958− return $ Just $ f a b
959− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
960−
961−instance (Reflex t, Semigroup a) => Semigroup (Dynamic t a) where
962− (<>) = zipDynWith (<>)
963−#if MIN_VERSION_semigroups(0,17,0)
964− stimes n = fmap $ stimes n
965−#else
966− times1p n = fmap $ times1p n
967−#endif
968−
969−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
970− mconcat = distributeListOverDynWith mconcat
971− mempty = constDyn mempty
972− mappend = zipDynWith mappend
973−
974−-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
975−-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
976−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
977−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
978−distributeDMapOverDynPure dm = case DMap.toList dm of
979− [] -> constDyn DMap.empty
980− [k :=> v] -> fmap (DMap.singleton k . Identity) v
981− _ ->
982− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
983− edmPre = merge $ DMap.map updated dm
984− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
985− olds <- sample $ current result
986− return $ DMap.unionWithKey (\_ _ new -> new) olds news
987− in result
988−
989−-- | Convert a list of 'Dynamic's into a 'Dynamic' list.
990−distributeListOverDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
991−distributeListOverDyn = distributeListOverDynWith id
992−
993−-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
994−distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
995−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
996−
997−-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
998−-- unless the second supplied 'Event' occurs simultaneously.
999−difference :: Reflex t => Event t a -> Event t b -> Event t a
1000−difference = alignEventWithMaybe $ \case
1001− This a -> Just a
1002− _ -> Nothing
1003−
1004−alignEventWithMaybe :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
1005−alignEventWithMaybe f ea eb =
1006− fmapMaybe (f <=< dmapToThese)
1007− $ merge
1008− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1009−
1010−filterEventKey
1011− :: forall t m k v a.
1012− ( Reflex t
1013− , MonadFix m
1014− , MonadHold t m
1015− , GEq k
1016− )
1017− => k a
1018− -> Event t (DSum k v)
1019− -> m (Event t (v a))
1020−filterEventKey k kv' = do
1021− let f :: DSum k v -> Maybe (v a)
1022− f (newK :=> newV) = case newK `geq` k of
1023− Just Refl -> Just newV
1024− Nothing -> Nothing
1025− takeWhileJustE f kv'
1026−
1027−
1028−factorEvent
1029− :: forall t m k v a.
1030− ( Reflex t
1031− , MonadFix m
1032− , MonadHold t m
1033− , GEq k
1034− )
1035− => k a
1036− -> Event t (DSum k v)
1037− -> m (Event t (v a), Event t (DSum k (Product v (Compose (Event t) v))))
1038−factorEvent k0 kv' = do
1039− key :: Behavior t (Some k) <- hold (Some.This k0) $ fmapCheap (\(k :=> _) -> Some.This k) kv'
1040− let update = flip push kv' $ \(newKey :=> newVal) -> sample key >>= \case
1041− Some.This oldKey -> case newKey `geq` oldKey of
1042− Just Refl -> return Nothing
1043− Nothing -> do
1044− newInner <- filterEventKey newKey kv'
1045− return $ Just $ newKey :=> Pair newVal (Compose newInner)
1046− eInitial <- filterEventKey k0 kv'
1047− return (eInitial, update)
1048−
1049−--------------------------------------------------------------------------------
1050−-- Accumulator
1051−--------------------------------------------------------------------------------
1052−
1053−-- | An 'Accumulator' type can be built by accumulating occurrences of an
1054−-- 'Event'.
1055−#if __GLASGOW_HASKELL__ < 802
1056−{-# WARNING accum "ghc < 8.2.1 doesn't seem to be able to specialize functions in this class, which can lead to poor performance" #-}
1057−{-# WARNING accumM "ghc < 8.2.1 doesn't seem to be able to specialize functions in this class, which can lead to poor performance" #-}
1058−{-# WARNING accumMaybe "ghc < 8.2.1 doesn't seem to be able to specialize functions in this class, which can lead to poor performance" #-}
1059−{-# WARNING accumMaybeM "ghc < 8.2.1 doesn't seem to be able to specialize functions in this class, which can lead to poor performance" #-}
1060−#endif
1061−class Reflex t => Accumulator t f | f -> t where
1062− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
1063− accum f = accumMaybe $ \v o -> Just $ f v o
1064− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
1065− accumM f = accumMaybeM $ \v o -> Just <$> f v o
1066− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
1067− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
1068− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
1069− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
1070− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
1071− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
1072− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
1073− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1074− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
1075− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
1076−
1077−accumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Dynamic t a)
1078−accumDyn f = accumMaybeDyn $ \v o -> Just $ f v o
1079−accumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Dynamic t a)
1080−accumMDyn f = accumMaybeMDyn $ \v o -> Just <$> f v o
1081−accumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Dynamic t a)
1082−accumMaybeDyn f = accumMaybeMDyn $ \v o -> return $ f v o
1083−accumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Dynamic t a)
1084−accumMaybeMDyn f z e = do
1085− rec let e' = flip push e $ \o -> do
1086− v <- sample $ current d'
1087− f v o
1088− d' <- holdDyn z e'
1089− return d'
1090−mapAccumDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1091−mapAccumDyn f = mapAccumMaybeDyn $ \v o -> bimap Just Just $ f v o
1092−mapAccumMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1093−mapAccumMDyn f = mapAccumMaybeMDyn $ \v o -> bimap Just Just <$> f v o
1094−mapAccumMaybeDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1095−mapAccumMaybeDyn f = mapAccumMaybeMDyn $ \v o -> return $ f v o
1096−mapAccumMaybeMDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Dynamic t a, Event t c)
1097−mapAccumMaybeMDyn f z e = do
1098− rec let e' = flip push e $ \o -> do
1099− v <- sample $ current d'
1100− result <- f v o
1101− return $ case result of
1102− (Nothing, Nothing) -> Nothing
1103− _ -> Just result
1104− d' <- holdDyn z $ fmapMaybe fst e'
1105− return (d', fmapMaybe snd e')
1106−
1107−{-# INLINE accumB #-}
1108−accumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (Behavior t a)
1109−accumB f = accumMaybeB $ \v o -> Just $ f v o
1110−{-# INLINE accumMB #-}
1111−accumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (Behavior t a)
1112−accumMB f = accumMaybeMB $ \v o -> Just <$> f v o
1113−{-# INLINE accumMaybeB #-}
1114−accumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (Behavior t a)
1115−accumMaybeB f = accumMaybeMB $ \v o -> return $ f v o
1116−{-# INLINE accumMaybeMB #-}
1117−accumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (Behavior t a)
1118−accumMaybeMB f z e = do
1119− rec let e' = flip push e $ \o -> do
1120− v <- sample d'
1121− f v o
1122− d' <- hold z e'
1123− return d'
1124−{-# INLINE mapAccumB #-}
1125−mapAccumB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1126−mapAccumB f = mapAccumMaybeB $ \v o -> bimap Just Just $ f v o
1127−{-# INLINE mapAccumMB #-}
1128−mapAccumMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1129−mapAccumMB f = mapAccumMaybeMB $ \v o -> bimap Just Just <$> f v o
1130−{-# INLINE mapAccumMaybeB #-}
1131−mapAccumMaybeB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1132−mapAccumMaybeB f = mapAccumMaybeMB $ \v o -> return $ f v o
1133−
1134−{-# INLINE mapAccumMaybeMB #-}
1135−mapAccumMaybeMB :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (Behavior t a, Event t c)
1136−mapAccumMaybeMB f z e = do
1137− rec let e' = flip push e $ \o -> do
1138− v <- sample d'
1139− result <- f v o
1140− return $ case result of
1141− (Nothing, Nothing) -> Nothing
1142− _ -> Just result
1143− d' <- hold z $ fmapMaybe fst e'
1144− return (d', fmapMaybe snd e')
1145−
1146−-- | Accumulate occurrences of an 'Event', producing an output occurrence each
1147−-- time. Discard the underlying 'Accumulator'.
1148−{-# INLINE mapAccum_ #-}
1149−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)
1150−mapAccum_ f z e = do
1151− (_, result) <- mapAccumB f z e
1152− return result
1153−
1154−-- | Accumulate occurrences of an 'Event', possibly producing an output
1155−-- occurrence each time. Discard the underlying 'Accumulator'.
1156−{-# INLINE mapAccumMaybe_ #-}
1157−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)
1158−mapAccumMaybe_ f z e = do
1159− (_, result) <- mapAccumMaybeB f z e
1160− return result
1161−
1162−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
1163−-- an output occurrence each time. Discard the underlying 'Accumulator'.
1164−{-# INLINE mapAccumM_ #-}
1165−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)
1166−mapAccumM_ f z e = do
1167− (_, result) <- mapAccumMB f z e
1168− return result
1169−
1170−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
1171−-- producing an output occurrence each time. Discard the underlying
1172−-- 'Accumulator'.
1173−{-# INLINE mapAccumMaybeM_ #-}
1174−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)
1175−mapAccumMaybeM_ f z e = do
1176− (_, result) <- mapAccumMaybeMB f z e
1177− return result
1178−
1179−instance Reflex t => Accumulator t (Dynamic t) where
1180− accumMaybeM = accumMaybeMDyn
1181− mapAccumMaybeM = mapAccumMaybeMDyn
1182−
1183−instance Reflex t => Accumulator t (Behavior t) where
1184− accumMaybeM = accumMaybeMB
1185− mapAccumMaybeM = mapAccumMaybeMB
1186−
1187−instance Reflex t => Accumulator t (Event t) where
1188− accumMaybeM f z e = updated <$> accumMaybeM f z e
1189− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
1190−
1191−-- | Create a new 'Event' by combining each occurrence with the next value of the
1192−-- list using the supplied function. If the list runs out of items, all
1193−-- subsequent 'Event' occurrences will be ignored.
1194−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
1195−zipListWithEvent f l e = do
1196− let f' a b = case a of
1197− h:t -> (Just t, Just $ f h b)
1198− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
1199− mapAccumMaybe_ f' l e
1200−
1201−-- | Assign a number to each occurrence of the given 'Event', starting from 0
1202−{-# INLINE numberOccurrences #-}
1203−numberOccurrences :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Event t (b, a))
1204−numberOccurrences = numberOccurrencesFrom 0
1205−
1206−-- | Assign a number to each occurrence of the given 'Event'
1207−{-# INLINE numberOccurrencesFrom #-}
1208−numberOccurrencesFrom :: (Reflex t, MonadHold t m, MonadFix m, Num b) => b -> Event t a -> m (Event t (b, a))
1209−numberOccurrencesFrom = mapAccum_ (\n a -> let !next = n + 1 in (next, (n, a)))
1210−
1211−-- | Assign a number to each occurrence of the given 'Event'; discard the occurrences' values
1212−{-# INLINE numberOccurrencesFrom_ #-}
1213−numberOccurrencesFrom_ :: (Reflex t, MonadHold t m, MonadFix m, Num b) => b -> Event t a -> m (Event t b)
1214−numberOccurrencesFrom_ = mapAccum_ (\n _ -> let !next = n + 1 in (next, n))
1215−
1216−-- | This is used to sample the value of a 'Behavior' using an 'Event'.
1217−--
1218−-- The '<@>' operator is intended to be used in conjunction with
1219−-- the 'Applicative' instance for 'Behavior'.
1220−--
1221−-- This is useful when we want to combine the values of one 'Event' and
1222−-- the value of several 'Behavior's at the time the 'Event' is firing.
1223−--
1224−-- If we have:
1225−--
1226−-- > f :: a -> b -> c -> d
1227−-- > b1 :: Behavior t a
1228−-- > b2 :: Behavior t b
1229−-- > e :: Event t c
1230−--
1231−-- then we can do:
1232−--
1233−-- > f <$> b1 <*> b2 <@> e :: Event t d
1234−--
1235−-- in order to apply the function 'f' to the relevant values.
1236−--
1237−-- The alternative would be something like:
1238−--
1239−-- > attachWith (\(x1, x2) y -> f x1 x2 y) ((,) <$> b1 <*> b2) e :: Event t d
1240−--
1241−-- or a variation involing a custom data type to hold the combination of
1242−-- 'Behavior's even when that combination might only ever be used by 'f'.
1243−--
1244−-- A more suggestive example might be:
1245−--
1246−-- > handleMouse <$> bPlayerState <*> bMousePosition <@> eMouseClick :: Event t (GameState -> GameState)
1247−--
1248−(<@>) :: Reflex t => Behavior t (a -> b) -> Event t a -> Event t b
1249−(<@>) b = push $ \x -> do
1250− f <- sample b
1251− return . Just . f $ x
1252−infixl 4 <@>
1253−
1254−-- | An version of '<@>' that does not use the value of the 'Event'.
1255−--
1256−-- Alternatively, it is 'tag' in operator form.
1257−--
1258−-- This is useful when we want to combine the values of several
1259−-- 'Behavior's at particular points in time using an 'Applicative'
1260−-- style syntax.
1261−--
1262−-- If we have:
1263−--
1264−-- > g :: a -> b -> d
1265−-- > b1 :: Behavior t a
1266−-- > b2 :: Behavior t b
1267−-- > e :: Event t c
1268−--
1269−-- where 'e' is firing at the points in time of interest.
1270−--
1271−-- Then we can use '<@':
1272−--
1273−-- > g <$> b1 <*> b2 <@ e :: Event t d
1274−--
1275−-- to combine the values of 'b1' and 'b2' at each of those points of time,
1276−-- with the function 'g' being used to combine the values.
1277−--
1278−-- This is the same as '<@>' except that the 'Event' is being used only
1279−-- to act as a trigger.
1280−(<@) :: (Reflex t) => Behavior t b -> Event t a -> Event t b
1281−(<@) = tag
1282−infixl 4 <@
1283−
1284−------------------
1285−-- Cheap Functions
1286−------------------
1287−
1288−{-# INLINE pushAlwaysCheap #-}
1289−pushAlwaysCheap :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
1290−pushAlwaysCheap f = pushCheap (fmap Just . f)
1291−
1292−{-# INLINE fmapMaybeCheap #-}
1293−fmapMaybeCheap :: Reflex t => (a -> Maybe b) -> Event t a -> Event t b
1294−fmapMaybeCheap f = pushCheap $ return . f
1295−
1296−{-# INLINE fforMaybeCheap #-}
1297−fforMaybeCheap :: Reflex t => Event t a -> (a -> Maybe b) -> Event t b
1298−fforMaybeCheap = flip fmapMaybeCheap
1299−
1300−{-# INLINE fforCheap #-}
1301−fforCheap :: Reflex t => Event t a -> (a -> b) -> Event t b
1302−fforCheap = flip fmapCheap
1303−
1304−{-# INLINE fmapCheap #-}
1305−fmapCheap :: Reflex t => (a -> b) -> Event t a -> Event t b
1306−fmapCheap f = pushCheap $ return . Just . f
1307−
1308−{-# INLINE tagCheap #-}
1309−tagCheap :: Reflex t => Behavior t b -> Event t a -> Event t b
1310−tagCheap b = pushAlwaysCheap $ \_ -> sample b
1311−
1312−{-# INLINE mergeWithCheap #-}
1313−mergeWithCheap :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
1314−mergeWithCheap = mergeWithCheap' id
1315−
1316−{-# INLINE mergeWithCheap' #-}
1317−mergeWithCheap' :: Reflex t => (a -> b) -> (b -> b -> b) -> [Event t a] -> Event t b
1318−mergeWithCheap' f g = mergeWithFoldCheap' $ foldl1 g . fmap f
1319−
1320−{-# INLINE mergeWithFoldCheap' #-}
1321−mergeWithFoldCheap' :: Reflex t => (NonEmpty a -> b) -> [Event t a] -> Event t b
1322−mergeWithFoldCheap' f es =
1323− fmapCheap (f . (\(h : t) -> h :| t) . IntMap.elems)
1324− . mergeInt
1325− . IntMap.fromDistinctAscList
1326− $ zip [0 :: Int ..] es
1327−
1328−--------------------------------------------------------------------------------
1329−-- Deprecated functions
1330−--------------------------------------------------------------------------------
1331−
1332−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
1333−-- occurs. If both occur at the same time they are combined using 'mappend'.
1334−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
1335−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
1336−appendEvents = alignWith $ mergeThese mappend
1337−
1338−-- | Alias for 'headE'
1339−{-# DEPRECATED onceE "Use 'headE' instead" #-}
1340−onceE :: MonadHold t m => Event t a -> m (Event t a)
1341−onceE = headE
1342−
1343−-- | Run both sides of a 'These' monadically, combining the results.
1344−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
1345−#ifdef USE_TEMPLATE_HASKELL
1346−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
1347−#endif
1348−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
1349−sequenceThese t = case t of
1350− This ma -> fmap This ma
1351− These ma mb -> liftM2 These ma mb
1352− That mb -> fmap That mb
1353−
1354−{-# DEPRECATED switchPromptly "Use 'switchHoldPromptly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1355−switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1356−switchPromptly = switchHoldPromptly
1357−{-# DEPRECATED switchPromptOnly "Use 'switchHoldPromptOnly' instead. The 'switchHold*' naming convention was chosen because those functions are more closely related to each other than they are to 'switch'. " #-}
1358−switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
1359−switchPromptOnly = switchHoldPromptOnly
src/Reflex/Collection.hsdeleted−121
@@ -1,121 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE LambdaCase #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE RecursiveDo #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−#ifdef USE_REFLEX_OPTIMIZER
11−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
12−#endif
13−module Reflex.Collection
14− (
15− -- * Widgets on Collections
16− listHoldWithKey
17− , listWithKey
18− , listWithKey'
19− , listWithKeyShallowDiff
20− , listViewWithKey
21− , selectViewListWithKey
22− , selectViewListWithKey_
23− -- * List Utils
24− , list
25− , simpleList
26− ) where
27−
28−import Control.Monad.Identity
29−import Data.Align
30−import Data.Functor.Misc
31−import Data.Map (Map)
32−import qualified Data.Map as Map
33−import Data.Map.Misc
34−import Data.These
35−
36−import Reflex.Class
37−import Reflex.Adjustable.Class
38−import Reflex.Dynamic
39−import Reflex.PostBuild.Class
40−
41−listHoldWithKey :: forall t m k v a. (Ord k, Adjustable t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
42−listHoldWithKey m0 m' f = do
43− let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
44− dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
45− (a0, a') <- sequenceDMapWithAdjust dm0 dm'
46− fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
47−
48−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
49−listWithKey :: forall t k v m a. (Ord k, Adjustable t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
50−listWithKey vals mkChild = do
51− postBuild <- getPostBuild
52− let childValChangedSelector = fanMap $ updated vals
53− -- We keep track of changes to children values in the mkChild function we pass to listHoldWithKey
54− -- The other changes we need to keep track of are child insertions and deletions. diffOnlyKeyChanges
55− -- keeps track of insertions and deletions but ignores value changes, since they're already accounted for.
56− diffOnlyKeyChanges olds news = flip Map.mapMaybe (align olds news) $ \case
57− This _ -> Just Nothing
58− These _ _ -> Nothing
59− That new -> Just $ Just new
60− rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
61− let changeVals :: Event t (Map k (Maybe v))
62− changeVals = attachWith diffOnlyKeyChanges (current sentVals) $ leftmost
63− [ updated vals
64− , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
65− ]
66− listHoldWithKey Map.empty changeVals $ \k v ->
67− mkChild k =<< holdDyn v (select childValChangedSelector $ Const2 k)
68−
69−{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
70−listWithKey' :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
71−listWithKey' = listWithKeyShallowDiff
72−
73−-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
74−listWithKeyShallowDiff :: (Ord k, Adjustable t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
75−listWithKeyShallowDiff initialVals valsChanged mkChild = do
76− let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
77− sentVals <- foldDyn applyMap (void initialVals) $ fmap (fmap void) valsChanged
78− let relevantPatch patch _ = case patch of
79− Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
80− Just _ -> Nothing -- We don't want to let spurious re-creations of items through
81− listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
82− mkChild k v $ select childValChangedSelector $ Const2 k
83−
84−--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
85−-- | Create a dynamically-changing set of Event-valued widgets.
86−-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
87−listViewWithKey :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
88−listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
89−
90−listViewWithKey' :: (Ord k, Adjustable t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
91−listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
92−
93−-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
94−selectViewListWithKey :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
95− => Dynamic t k -- ^ Current selection key
96− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
97− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
98− -> m (Event t (k, a)) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
99−selectViewListWithKey selection vals mkChild = do
100− let selectionDemux = demux selection -- For good performance, this value must be shared across all children
101− selectChild <- listWithKey vals $ \k v -> do
102− let selected = demuxed selectionDemux k
103− selectSelf <- mkChild k v selected
104− return $ fmap ((,) k) selectSelf
105− return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
106−
107−selectViewListWithKey_ :: forall t m k v a. (Adjustable t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
108− => Dynamic t k -- ^ Current selection key
109− -> Dynamic t (Map k v) -- ^ Dynamic key/value map
110− -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
111− -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
112−selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
113−
114−-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
115−-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
116−list :: (Ord k, Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
117−list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
118−
119−-- | Create a dynamically-changing set of widgets from a Dynamic list.
120−simpleList :: (Adjustable t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
121−simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
src/Reflex/Dynamic.hsdeleted−680
@@ -1,680 +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 RecursiveDo #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE StandaloneDeriving #-}
14−{-# LANGUAGE TypeFamilies #-}
15−{-# LANGUAGE TypeOperators #-}
16−{-# LANGUAGE UndecidableInstances #-}
17−#ifdef USE_REFLEX_OPTIMIZER
18−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
19−#endif
20−-- | This module contains various functions for working with 'Dynamic' values.
21−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
22−module Reflex.Dynamic
23− ( -- * Basics
24− Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
25− , current
26− , updated
27− , holdDyn
28− , mapDynM
29− , forDynM
30− , constDyn
31− , count
32− , toggle
33− , switchDyn
34− , switchPromptlyDyn
35− , tagPromptlyDyn
36− , attachPromptlyDyn
37− , attachPromptlyDynWith
38− , attachPromptlyDynWithMaybe
39− , maybeDyn
40− , eitherDyn
41− , factorDyn
42− , scanDyn
43− , scanDynMaybe
44− , holdUniqDyn
45− , holdUniqDynBy
46− , improvingMaybe
47− , foldDyn
48− , foldDynM
49− , foldDynMaybe
50− , foldDynMaybeM
51− , joinDynThroughMap
52− , traceDyn
53− , traceDynWith
54− , splitDynPure
55− , distributeMapOverDynPure
56− , distributeDMapOverDynPure
57− , distributeListOverDynPure
58− , Demux
59− , demux
60− , demuxed
61− -- Things that probably aren't very useful:
62− , HList (..)
63− , FHList (..)
64− , collectDynPure
65− , RebuildSortedHList (..)
66− , IsHList (..)
67− , AllAreFunctors (..)
68− , HListPtr (..)
69− , distributeFHListOverDynPure
70− -- Unsafe
71− , unsafeDynamic
72− -- * Deprecated functions
73− , apDyn
74− , attachDyn
75− , attachDynWith
76− , attachDynWithMaybe
77− , collectDyn
78− , combineDyn
79− , distributeDMapOverDyn
80− , distributeFHListOverDyn
81− , forDyn
82− , getDemuxed
83− , joinDyn
84− , mapDyn
85− , mconcatDyn
86− , nubDyn
87− , splitDyn
88− , tagDyn
89− , uniqDyn
90− , uniqDynBy
91− ) where
92−
93−import Data.Functor.Compose
94−import Data.Functor.Misc
95−import Reflex.Class
96−
97−import Control.Applicative ((<*>))
98−import Control.Monad
99−import Control.Monad.Fix
100−import Control.Monad.Identity
101−import Data.Align
102−import Data.Dependent.Map (DMap)
103−import qualified Data.Dependent.Map as DMap
104−import Data.Dependent.Sum (DSum (..))
105−import Data.Functor.Product
106−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
107−import Data.Map (Map)
108−import Data.Maybe
109−import Data.Monoid hiding (Product)
110−import Data.These
111−
112−import Debug.Trace
113−
114−-- | Map a sampling function over a 'Dynamic'.
115−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)
116−mapDynM f d = buildDynamic (f =<< sample (current d)) $ pushAlways f (updated d)
117−
118−-- | Flipped version of 'mapDynM'
119−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)
120−forDynM d f = mapDynM f d
121−
122−-- | Create a new 'Dynamic' that only signals changes if the values actually
123−-- changed.
124−holdUniqDyn :: (Reflex t, MonadHold t m, MonadFix m, Eq a) => Dynamic t a -> m (Dynamic t a)
125−holdUniqDyn = holdUniqDynBy (==)
126−
127−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
128−-- changes and the given function returns 'False' when given both the old and
129−-- the new values.
130−holdUniqDynBy :: (Reflex t, MonadHold t m, MonadFix m) => (a -> a -> Bool) -> Dynamic t a -> m (Dynamic t a)
131−holdUniqDynBy eq = scanDynMaybe id (\new old -> if new `eq` old then Nothing else Just new)
132−
133−-- | Dynamic Maybe that can only update from Nothing to Just or Just to Just (i.e., cannot revert to Nothing)
134−improvingMaybe :: (Reflex t, MonadHold t m, MonadFix m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe a))
135−improvingMaybe = scanDynMaybe id (\new _ -> if isJust new then Just new else Nothing)
136−
137−-- | Create a 'Dynamic' that accumulates values from another 'Dynamic'. This
138−-- function does not force its input 'Dynamic' until the output 'Dynamic' is
139−-- forced.
140−scanDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> b) -> Dynamic t a -> m (Dynamic t b)
141−scanDyn z f = scanDynMaybe z (\a b -> Just $ f a b)
142−
143−-- | Like 'scanDyn', but the the accumulator function may decline to update the
144−-- result 'Dynamic''s value.
145−scanDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b) -> (a -> b -> Maybe b) -> Dynamic t a -> m (Dynamic t b)
146−scanDynMaybe z f d = do
147− rec d' <- buildDynamic (z <$> sample (current d)) $ flip push (updated d) $ \a -> do
148− b <- sample $ current d'
149− return $ f a b
150− return d'
151−
152−-- | Create a 'Dynamic' using the initial value and change it each time the
153−-- 'Event' occurs using a folding function on the previous value and the value
154−-- of the 'Event'.
155−foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
156−foldDyn = accumDyn . flip
157−
158−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
159−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
160−foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
161−foldDynM = accumMDyn . flip
162−
163−-- | Create a 'Dynamic' using the provided initial value and change it each time
164−-- the provided 'Event' occurs, using a function to combine the old value with
165−-- the 'Event''s value. If the function returns 'Nothing', the value is not
166−-- changed; this is distinct from returning 'Just' the old value, since the
167−-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
168−-- in the 'Nothing' case.
169−foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
170−foldDynMaybe = accumMaybeDyn . flip
171−
172−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
173−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
174−foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
175−foldDynMaybeM = accumMaybeMDyn . flip
176−
177−-- | Create a new 'Dynamic' that counts the occurrences of the 'Event'.
178−count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
179−count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
180−
181−-- | Create a new 'Dynamic' using the initial value that flips its
182−-- value every time the 'Event' occurs.
183−toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
184−toggle = foldDyn (const not)
185−
186−-- | Switches to the new 'Event' whenever it receives one. Only the old event is
187−-- considered the moment a new one is switched in; the output event will fire at
188−-- that moment if only if the old event does.
189−--
190−-- Prefer this to 'switchPromptlyDyn' where possible. The lack of doing double
191−-- work when the outer and (new) inner fires means this imposes fewer "timing
192−-- requirements" and thus is far more easy to use without introducing fresh
193−-- failure cases. 'switchDyn' is also more performant.
194−switchDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
195−switchDyn d = switch (current d)
196−
197−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
198−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
199−-- old and new inner Events fire simultaneously, the output will fire with the
200−-- value of the *new* 'Event'.
201−--
202−-- Prefer 'switchDyn' to this where possible. The timing requirements that
203−-- switching before imposes are likely to bring down your app unless you are
204−-- very careful. 'switchDyn' is also more performant.
205−switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
206−switchPromptlyDyn de =
207− let eLag = switch $ current de
208− eCoincidences = coincidence $ updated de
209− in leftmost [eCoincidences, eLag]
210−
211−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
212−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
213−splitDynPure d = (fmap fst d, fmap snd d)
214−
215−-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
216−-- non-'Dynamic' elements.
217−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
218−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
219−
220−-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
221−-- non-'Dynamic' elements, preserving the order of the elements.
222−distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
223−distributeListOverDynPure =
224− fmap (map fromDSum . DMap.toAscList) .
225− distributeDMapOverDynPure .
226− DMap.fromDistinctAscList .
227− zipWith toDSum [0..]
228− where
229− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
230− toDSum k v = Const2 k :=> v
231− fromDSum :: DSum (Const2 Int a) Identity -> a
232− fromDSum (Const2 _ :=> Identity v) = v
233−
234−--TODO: Generalize this to functors other than Maps
235−-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
236−-- with the current values of the 'Dynamic's in a map.
237−joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
238−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
239−
240−-- | Print the value of the 'Dynamic' when it is first read and on each
241−-- subsequent change that is observed (as traceEvent), prefixed with the
242−-- provided string. This should /only/ be used for debugging.
243−--
244−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
245−-- else in the system is depending on it.
246−traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
247−traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
248−
249−-- | Print the result of applying the provided function to the value
250−-- of the 'Dynamic' when it is first read and on each subsequent change
251−-- that is observed (as traceEvent). This should /only/ be used for
252−-- debugging.
253−--
254−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
255−-- else in the system is depending on it.
256−traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
257−traceDynWith f d =
258− let e' = traceEventWith f $ updated d
259− getV0 = do
260− x <- sample $ current d
261− trace (f x) $ return x
262− in unsafeBuildDynamic getV0 e'
263−
264−-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
265−-- each time the 'Event' occurs.
266−--
267−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
268−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
269−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
270−-- will be used, since the 'Behavior' won't be updated until the end of the frame.
271−-- Additionally, this means that the output 'Event' may not be used to directly change
272−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
273−-- cyclic data flows, generally `tag (current d) e` is preferred.
274−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
275−tagPromptlyDyn = attachPromptlyDynWith const
276−
277−-- | Attach the current value of the 'Dynamic' to the value of the
278−-- 'Event' each time it occurs.
279−--
280−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
281−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
282−attachPromptlyDyn = attachPromptlyDynWith (,)
283−
284−-- | Combine the current value of the 'Dynamic' with the value of the
285−-- 'Event' each time it occurs.
286−--
287−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
288−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
289−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
290−
291−-- | Create a new 'Event' by combining the value at each occurrence with the
292−-- current value of the 'Dynamic' value and possibly filtering if the combining
293−-- function returns 'Nothing'.
294−--
295−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
296−-- (current d)`. See 'tagPromptlyDyn' for details.
297−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
298−attachPromptlyDynWithMaybe f d e =
299− let e' = attach (current d) e
300− in fforMaybe (align e' $ updated d) $ \case
301− This (a, b) -> f a b -- Only the tagging event is firing, so use that
302− These (_, b) a -> f a b -- Both events are firing, so use the newer value
303− That _ -> Nothing -- The tagging event isn't firing, so don't fire
304−
305−-- | Factor a @Dynamic t (Maybe a)@ into a @Dynamic t (Maybe (Dynamic t a))@,
306−-- such that the outer 'Dynamic' is updated only when the 'Maybe''s constructor
307−-- chages from 'Nothing' to 'Just' or vice-versa. Whenever the constructor
308−-- becomes 'Just', an inner 'Dynamic' will be provided, whose value will track
309−-- the 'a' inside the 'Just'; when the constructor becomes 'Nothing', the
310−-- existing inner 'Dynamic' will become constant, and will not change when the
311−-- outer constructor changes back to 'Nothing'.
312−maybeDyn :: forall t a m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Maybe a) -> m (Dynamic t (Maybe (Dynamic t a)))
313−maybeDyn = fmap (fmap unpack) . eitherDyn . fmap pack
314− where pack = \case
315− Nothing -> Left ()
316− Just a -> Right a
317− unpack = \case
318− Left _ -> Nothing
319− Right a -> Just a
320−
321−eitherDyn :: forall t a b m. (Reflex t, MonadFix m, MonadHold t m) => Dynamic t (Either a b) -> m (Dynamic t (Either (Dynamic t a) (Dynamic t b)))
322−eitherDyn = fmap (fmap unpack) . factorDyn . fmap eitherToDSum
323− where unpack :: DSum (EitherTag a b) (Compose (Dynamic t) Identity) -> Either (Dynamic t a) (Dynamic t b)
324− unpack = \case
325− LeftTag :=> Compose a -> Left $ coerceDynamic a
326− RightTag :=> Compose b -> Right $ coerceDynamic b
327−
328−factorDyn :: forall t m k v. (Reflex t, MonadHold t m, GEq k)
329− => Dynamic t (DSum k v) -> m (Dynamic t (DSum k (Compose (Dynamic t) v)))
330−factorDyn d = buildDynamic (sample (current d) >>= holdKey) update where
331− update :: Event t (DSum k (Compose (Dynamic t) v))
332− update = flip push (updated d) $ \(newKey :=> newVal) -> do
333− (oldKey :=> _) <- sample (current d)
334− case newKey `geq` oldKey of
335− Just Refl -> return Nothing
336− Nothing -> Just <$> holdKey (newKey :=> newVal)
337−
338− holdKey (k :=> v) = do
339− inner' <- filterEventKey k (updated d)
340− inner <- holdDyn v inner'
341− return $ k :=> Compose inner
342−--------------------------------------------------------------------------------
343−-- Demux
344−--------------------------------------------------------------------------------
345−
346−-- | Represents a time changing value together with an 'EventSelector' that can
347−-- efficiently detect when the underlying 'Dynamic' has a particular value.
348−-- This is useful for representing data like the current selection of a long
349−-- list.
350−--
351−-- Semantically,
352−--
353−-- > demuxed (demux d) k === fmap (== k) d
354−--
355−-- However, the when getDemuxed is used multiple times, the complexity is only
356−-- /O(log(n))/, rather than /O(n)/ for fmap.
357−data Demux t k = Demux { demuxValue :: Behavior t k
358− , demuxSelector :: EventSelector t (Const2 k Bool)
359− }
360−
361−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
362−-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
363−demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
364−demux k = Demux (current k)
365− (fan $ attachWith (\k0 k1 -> if k0 == k1
366− then DMap.empty
367− else DMap.fromList [Const2 k0 :=> Identity False,
368− Const2 k1 :=> Identity True])
369− (current k) (updated k))
370−
371−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
372−-- faster than) mapping over the original 'Dynamic' and checking whether it is
373−-- equal to the given key.
374−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
375−demuxed d k =
376− let e = select (demuxSelector d) (Const2 k)
377− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
378−
379−--------------------------------------------------------------------------------
380−-- collectDyn
381−--------------------------------------------------------------------------------
382−
383−--TODO: This whole section is badly in need of cleanup
384−
385−-- | A heterogeneous list whose type and length are fixed statically. This is
386−-- reproduced from the 'HList' package due to integration issues, and because
387−-- very little other functionality from that library is needed.
388−data HList (l::[*]) where
389− HNil :: HList '[]
390− HCons :: e -> HList l -> HList (e ': l)
391−
392−infixr 2 `HCons`
393−
394−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
395−type instance HRevApp '[] l = l
396−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
397−
398−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
399−hRevApp HNil l = l
400−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
401−
402−hReverse :: HList l -> HList (HRevApp l '[])
403−hReverse l = hRevApp l HNil
404−
405−hBuild :: (HBuild' '[] r) => r
406−hBuild = hBuild' HNil
407−
408−class HBuild' l r where
409− hBuild' :: HList l -> r
410−
411−instance (l' ~ HRevApp l '[])
412− => HBuild' l (HList l') where
413− hBuild' = hReverse
414−
415−instance HBuild' (a ': l) r
416− => HBuild' l (a->r) where
417− hBuild' l x = hBuild' (HCons x l)
418−
419−-- | Like 'HList', but with a functor wrapping each element.
420−data FHList f l where
421− FHNil :: FHList f '[]
422− FHCons :: f e -> FHList f l -> FHList f (e ': l)
423−
424−instance GEq (HListPtr l) where
425− HHeadPtr `geq` HHeadPtr = Just Refl
426− HHeadPtr `geq` HTailPtr _ = Nothing
427− HTailPtr _ `geq` HHeadPtr = Nothing
428− HTailPtr a `geq` HTailPtr b = a `geq` b
429−
430−instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
431− HHeadPtr `gcompare` HHeadPtr = GEQ
432− HHeadPtr `gcompare` HTailPtr _ = GLT
433− HTailPtr _ `gcompare` HHeadPtr = GGT
434− HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
435−
436−-- | A typed index into a typed heterogeneous list.
437−data HListPtr l a where
438− HHeadPtr :: HListPtr (h ': t) h
439− HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
440−
441−deriving instance Eq (HListPtr l a)
442−deriving instance Ord (HListPtr l a)
443−
444−fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
445−fhlistToDMap = DMap.fromList . go
446− where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
447− go = \case
448− FHNil -> []
449− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
450−
451−-- | This class allows 'HList's and 'FHlist's to be built from regular lists;
452−-- they must be contiguous and sorted.
453−class RebuildSortedHList l where
454− rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
455− rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
456−
457−instance RebuildSortedHList '[] where
458− rebuildSortedFHList l = case l of
459− [] -> FHNil
460− _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
461− rebuildSortedHList l = case l of
462− [] -> HNil
463− _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
464−
465−instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
466− rebuildSortedFHList l = case l of
467− ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
468− _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
469− rebuildSortedHList l = case l of
470− ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
471− _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
472−
473−dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
474−dmapToHList = rebuildSortedHList . DMap.toList
475−
476−-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
477−-- single 'Dynamic' whose value represents the current values of all of the
478−-- input 'Dynamic's.
479−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
480−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
481−
482−-- | Indicates that all elements in a type-level list are applications of the
483−-- same functor.
484−class AllAreFunctors (f :: a -> *) (l :: [a]) where
485− type FunctorList f l :: [*]
486− toFHList :: HList (FunctorList f l) -> FHList f l
487− fromFHList :: FHList f l -> HList (FunctorList f l)
488−
489−instance AllAreFunctors f '[] where
490− type FunctorList f '[] = '[]
491− toFHList l = case l of
492− HNil -> FHNil
493−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
494− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
495−#endif
496− fromFHList FHNil = HNil
497−
498−instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
499− type FunctorList f (h ': t) = f h ': FunctorList f t
500− toFHList l = case l of
501− a `HCons` b -> a `FHCons` toFHList b
502−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
503− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
504−#endif
505− fromFHList (a `FHCons` b) = a `HCons` fromFHList b
506−
507−-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
508−-- single 'Dynamic' whose value represents all the current values of the input's
509−-- consitutent 'Dynamic's.
510−collectDynPure :: ( RebuildSortedHList (HListElems b)
511− , IsHList a, IsHList b
512− , AllAreFunctors (Dynamic t) (HListElems b)
513− , Reflex t
514− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
515− ) => a -> Dynamic t b
516−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
517−
518−-- | Poor man's 'Generic's for product types only.
519−class IsHList a where
520− type HListElems a :: [*]
521− toHList :: a -> HList (HListElems a)
522− fromHList :: HList (HListElems a) -> a
523−
524−instance IsHList (a, b) where
525− type HListElems (a, b) = [a, b]
526− toHList (a, b) = hBuild a b
527− fromHList l = case l of
528− a `HCons` b `HCons` HNil -> (a, b)
529−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
530− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
531−#endif
532−
533−instance IsHList (a, b, c, d) where
534− type HListElems (a, b, c, d) = [a, b, c, d]
535− toHList (a, b, c, d) = hBuild a b c d
536− fromHList l = case l of
537− a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
538−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
539− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
540−#endif
541−
542−instance IsHList (a, b, c, d, e, f) where
543− type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
544− toHList (a, b, c, d, e, f) = hBuild a b c d e f
545− fromHList l = case l of
546− a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
547−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
548− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
549−#endif
550−
551−--------------------------------------------------------------------------------
552−-- Deprecated functions
553−--------------------------------------------------------------------------------
554−
555−-- | Map a function over a 'Dynamic'.
556−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
557−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
558−mapDyn f = return . fmap f
559−
560−-- | Flipped version of 'mapDyn'.
561−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
562−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
563−forDyn a = return . ffor a
564−
565−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
566−-- of the tuple.
567−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
568−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
569−splitDyn = return . splitDynPure
570−
571−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
572−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
573−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
574−mconcatDyn = return . mconcat
575−
576−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
577−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
578−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
579−distributeDMapOverDyn = return . distributeDMapOverDynPure
580−
581−-- | Merge two 'Dynamic's into a new one using the provided function. The new
582−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
583−-- its value.
584−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
585−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
586−combineDyn f a b = return $ zipDynWith f a b
587−
588−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
589−--
590−-- > do
591−-- > person <- Person `mapDyn` dynFirstName
592−-- > `apDyn` dynListName
593−-- > `apDyn` dynAge
594−-- > `apDyn` dynAddress
595−{-# 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" #-}
596−#ifdef USE_TEMPLATE_HASKELL
597−{-# ANN apDyn "HLint: ignore Use fmap" #-}
598−#endif
599−apDyn :: forall t m a b. (Reflex t, Monad m)
600− => m (Dynamic t (a -> b))
601− -> Dynamic t a
602− -> m (Dynamic t b)
603−apDyn m a = fmap (<*> a) m
604−
605−--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
606−-- | This function no longer needs to be monadic, so it has been replaced by
607−-- 'demuxed', which is pure.
608−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
609−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
610−getDemuxed d = return . demuxed d
611−
612−-- | This function no longer needs to be monadic, so it has been replaced by
613−-- 'distributeFHListOverDynPure', which is pure.
614−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
615−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
616−distributeFHListOverDyn = return . distributeFHListOverDynPure
617−
618−-- | This function no longer needs to be monadic, so it has been replaced by
619−-- 'collectDynPure', which is pure.
620−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
621−collectDyn :: ( RebuildSortedHList (HListElems b)
622− , IsHList a, IsHList b
623− , AllAreFunctors (Dynamic t) (HListElems b)
624− , Reflex t, Monad m
625− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
626− ) => a -> m (Dynamic t b)
627−collectDyn = return . collectDynPure
628−
629−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
630−-- semantics.
631−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
632−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
633−tagDyn = tagPromptlyDyn
634−
635−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
636−-- semantics.
637−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
638−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
639−attachDyn = attachPromptlyDyn
640−
641−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
642−-- semantics.
643−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
644−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
645−attachDynWith = attachPromptlyDynWith
646−
647−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
648−-- its semantics.
649−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
650−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
651−attachDynWithMaybe = attachPromptlyDynWithMaybe
652−
653−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
654−-- current value will always be equal to the current value's current value, and
655−-- will change whenever either the inner or the outer (or both) values change.
656−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
657−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
658−joinDyn = join
659−
660−-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
661−-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
662−-- circumstances. Also, note that, unlike 'nub', this function does not prevent
663−-- all recurrences of a value, only consecutive recurrences.
664−{-# DEPRECATED nubDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
665−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
666−nubDyn = uniqDyn
667−
668−-- | WARNING: This function is only pure if @a@'s 'Eq' instance tests
669−-- representational equality. Use 'holdUniqDyn' instead, which is pure in all
670−-- circumstances.
671−{-# DEPRECATED uniqDyn "Use 'holdUniqDyn' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
672−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
673−uniqDyn = uniqDynBy (==)
674−
675−-- | WARNING: This function is impure. Use 'holdUniqDynBy' instead.
676−{-# DEPRECATED uniqDynBy "Use 'holdUniqDynBy' instead; note that it returns a MonadHold action rather than a pure Dynamic" #-}
677−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
678−uniqDynBy eq d =
679− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
680− in unsafeDynamic (current d) e'
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` snd a `AppE` b) (ConE 'FHNil) exprs
51− param = foldr (\a b -> ConP 'HCons [VarP (fst a), b]) (ConP 'HNil []) 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−
21−-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
22−-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
23−-- the value inside a 'UniqDynamic' is always evaluated to
24−-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
25−--
26−-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
27−-- unnecessary update propagation; this is much more efficient than performing
28−-- full comparisons. However, when the 'UniqDynamic' is converted back into a
29−-- regular 'Dynamic', a full comparison is performed.
30−newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
31−
32−-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
33−uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
34−uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip pushCheap (updated d) $ \new -> do
35− old <- sample $ current d --TODO: Is it better to sample ourselves here?
36− return $ unsafeJustChanged old new
37−
38−-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
39−-- final check using the output type's 'Eq' instance to ensure deterministic
40−-- behavior.
41−--
42−-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
43−-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
44−-- eliminate more 'updated' occurrences than it should. For example, NaN values
45−-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
46−-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
47−-- to lose changes from NaN to NaN.
48−fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
49−fromUniqDynamic (UniqDynamic d) = unsafeDynamic (current d) e'
50− where
51− -- Only consider values different if they fail both pointer equality /and/
52− -- 'Eq' equality. This is to make things a bit more deterministic in the
53− -- case of unlawful 'Eq' instances. However, it is still possible to
54− -- achieve nondeterminism by constructing elements that are identical in
55− -- value, unequal according to 'Eq', and nondeterministically equal or
56− -- nonequal by pointer quality. I suspect that it is impossible to make the
57− -- behavior deterministic in this case.
58− superEq a b = a `unsafePtrEq` b || a == b
59− e' = attachWithMaybe (\x x' -> if x' `superEq` x then Nothing else Just x') (current d) (updated d)
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 <- accumMaybeMDyn 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) <- mapAccumMaybeMDyn 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−7
@@ -1,7 +0,0 @@
1−module Reflex.DynamicWriter
2− {-# DEPRECATED "Use 'Reflex.DynamicWriter.Class' and 'Reflex.DynamicWrite.Base' instead, or just import 'Reflex'" #-}
3− ( module X
4− ) where
5−
6−import Reflex.DynamicWriter.Base as X
7−import Reflex.DynamicWriter.Class as X
src/Reflex/DynamicWriter/Base.hsdeleted−232
@@ -1,232 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−{-# LANGUAGE StandaloneDeriving #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−module Reflex.DynamicWriter.Base
17− ( DynamicWriterT (..)
18− , runDynamicWriterT
19− , withDynamicWriterT
20− ) where
21−
22−import Control.Monad.Exception
23−import Control.Monad.Identity
24−import Control.Monad.IO.Class
25−import Control.Monad.Reader
26−import Control.Monad.Ref
27−import Control.Monad.State.Strict
28−import Data.Align
29−import Data.Dependent.Map (DMap)
30−import qualified Data.Dependent.Map as DMap
31−import Data.FastMutableIntMap
32−import Data.Functor.Misc
33−import Data.IntMap (IntMap)
34−import qualified Data.IntMap as IntMap
35−import Data.Map (Map)
36−import qualified Data.Map as Map
37−import Data.Semigroup
38−import Data.Some (Some)
39−import Data.These
40−
41−import Reflex.Adjustable.Class
42−import Reflex.Class
43−import Reflex.DynamicWriter.Class
44−import Reflex.EventWriter.Class (EventWriter, tellEvent)
45−import Reflex.Host.Class
46−import qualified Reflex.Patch.MapWithMove as MapWithMove
47−import Reflex.PerformEvent.Class
48−import Reflex.PostBuild.Class
49−import Reflex.Query.Class
50−import Reflex.Requester.Class
51−import Reflex.TriggerEvent.Class
52−
53−instance MonadTrans (DynamicWriterT t w) where
54− lift = DynamicWriterT . lift
55−
56−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')
57−mapIncrementalMapValues f = unsafeMapIncremental (fmap f) (fmap f)
58−
59−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
60−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
61− where changedValues = fmap (PatchMap . fmap Just) $ mergeMapIncremental $ mapIncrementalMapValues updated a
62− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
63−
64−mergeIntMapDynIncremental :: Reflex t => Incremental t (PatchIntMap (Dynamic t v)) -> Incremental t (PatchIntMap v)
65−mergeIntMapDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
66− where changedValues = fmap (PatchIntMap . fmap Just) $ mergeIntMapIncremental $ mapIncrementalMapValues updated a
67− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchIntMap m) -> PatchIntMap <$> mapM (mapM (sample . current)) m
68−
69−mergeDynIncrementalWithMove :: forall t k v. (Reflex t, Ord k) => Incremental t (PatchMapWithMove k (Dynamic t v)) -> Incremental t (PatchMapWithMove k v)
70−mergeDynIncrementalWithMove a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ alignWith f addedAndRemovedValues changedValues
71− where changedValues = mergeMapIncrementalWithMove $ mapIncrementalMapValues updated a
72− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ fmap unsafePatchMapWithMove . mapM (mapM (sample . current)) . unPatchMapWithMove
73− f :: These (PatchMapWithMove k v) (Map k v) -> PatchMapWithMove k v
74− f x = unsafePatchMapWithMove $
75− let (p, changed) = case x of
76− This p_ -> (unPatchMapWithMove p_, mempty)
77− That c -> (mempty, c)
78− These p_ c -> (unPatchMapWithMove p_, c)
79− (pWithNewVals, noLongerMoved) = flip runState [] $ forM p $ MapWithMove.nodeInfoMapMFrom $ \case
80− MapWithMove.From_Insert v -> return $ MapWithMove.From_Insert v
81− MapWithMove.From_Delete -> return MapWithMove.From_Delete
82− MapWithMove.From_Move k -> case Map.lookup k changed of
83− Nothing -> return $ MapWithMove.From_Move k
84− Just v -> do
85− modify (k:)
86− return $ MapWithMove.From_Insert v
87− noLongerMovedMap = Map.fromList $ fmap (, ()) noLongerMoved
88− in Map.differenceWith (\e _ -> Just $ MapWithMove.nodeInfoSetTo Nothing e) pWithNewVals noLongerMovedMap --TODO: Check if any in the second map are not covered?
89−
90−-- | A basic implementation of 'MonadDynamicWriter'.
91−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a }
92− deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadAsyncException, MonadException) -- The list is kept in reverse order
93−
94−deriving instance MonadHold t m => MonadHold t (DynamicWriterT t w m)
95−deriving instance MonadSample t m => MonadSample t (DynamicWriterT t w m)
96−
97−
98−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
99− type Ref (DynamicWriterT t w m) = Ref m
100− newRef = lift . newRef
101− readRef = lift . readRef
102− writeRef r = lift . writeRef r
103−
104−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
105− atomicModifyRef r = lift . atomicModifyRef r
106−
107−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
108− newEventWithTrigger = lift . newEventWithTrigger
109− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
110−
111−-- | Run a 'DynamicWriterT' action. The dynamic writer output will be provided
112−-- along with the result of the action.
113−runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
114−runDynamicWriterT (DynamicWriterT a) = do
115− (result, ws) <- runStateT a []
116− return (result, mconcat $ reverse ws)
117−
118−instance (Monad m, Monoid w, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
119− tellDyn w = DynamicWriterT $ modify (w :)
120−
121−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
122− ask = lift ask
123− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
124− reader = lift . reader
125−
126−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
127− type Performable (DynamicWriterT t w m) = Performable m
128− performEvent_ = lift . performEvent_
129− performEvent = lift . performEvent
130−
131−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
132− newTriggerEvent = lift newTriggerEvent
133− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
134− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
135−
136−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
137− getPostBuild = lift getPostBuild
138−
139−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
140− get = lift get
141− put = lift . put
142−
143−newtype DynamicWriterTLoweredResult t w v a = DynamicWriterTLoweredResult (v a, Dynamic t w)
144−
145−-- | When the execution of a 'DynamicWriterT' action is adjusted using
146−-- 'Adjustable', the 'Dynamic' output of that action will also be updated to
147−-- match.
148−instance (Adjustable t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => Adjustable t (DynamicWriterT t w m) where
149− runWithReplace a0 a' = do
150− (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
151− tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
152− return (fst result0, fst <$> result')
153− traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustImpl traverseIntMapWithKeyWithAdjust mergeIntMapDynIncremental
154− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap weakenPatchDMapWith mergeDynIncremental
155− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeDynIncrementalWithMove
156−
157−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)
158− => ( (forall a. k a -> v a -> m (DynamicWriterTLoweredResult t w v' a))
159− -> DMap k v
160− -> Event t (p k v)
161− -> m (DMap k (DynamicWriterTLoweredResult t w v'), Event t (p k (DynamicWriterTLoweredResult t w v')))
162− )
163− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> v' a) -> p k (DynamicWriterTLoweredResult t w v') -> p k v')
164− -> ((forall a. DynamicWriterTLoweredResult t w v' a -> Dynamic t w) -> p k (DynamicWriterTLoweredResult t w v') -> p' (Some k) (Dynamic t w))
165− -> (Incremental t (p' (Some k) (Dynamic t w)) -> Incremental t (p' (Some k) w))
166− -> (forall a. k a -> v a -> DynamicWriterT t w m (v' a))
167− -> DMap k v
168− -> Event t (p k v)
169− -> DynamicWriterT t w m (DMap k v', Event t (p k v'))
170−traverseDMapWithKeyWithAdjustImpl base mapPatch weakenPatchWith mergeMyDynIncremental f (dm0 :: DMap k v) dm' = do
171− (result0, result') <- lift $ base (\k v -> fmap DynamicWriterTLoweredResult $ runDynamicWriterT $ f k v) dm0 dm'
172− let getValue (DynamicWriterTLoweredResult (v, _)) = v
173− getWritten (DynamicWriterTLoweredResult (_, w)) = w
174− liftedResult0 = DMap.map getValue result0
175− liftedResult' = ffor result' $ mapPatch getValue
176− liftedWritten0 :: Map (Some k) (Dynamic t w)
177− liftedWritten0 = weakenDMapWith getWritten result0
178− liftedWritten' = ffor result' $ weakenPatchWith getWritten
179− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
180− i <- holdIncremental liftedWritten0 liftedWritten'
181− tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
182− return (liftedResult0, liftedResult')
183−
184−traverseIntMapWithKeyWithAdjustImpl :: forall t w v' p p' v m. (PatchTarget (p' (Dynamic t w)) ~ IntMap (Dynamic t w), PatchTarget (p' w) ~ IntMap w, Patch (p' w), Patch (p' (Dynamic t w)), MonadFix m, Monoid w, Reflex t, MonadHold t m, Functor p, p ~ p')
185− => ( (IntMap.Key -> v -> m (v', Dynamic t w))
186− -> IntMap v
187− -> Event t (p v)
188− -> m (IntMap (v', Dynamic t w), Event t (p (v', Dynamic t w)))
189− )
190− -> (Incremental t (p' (Dynamic t w)) -> Incremental t (p' w))
191− -> (IntMap.Key -> v -> DynamicWriterT t w m v')
192− -> IntMap v
193− -> Event t (p v)
194− -> DynamicWriterT t w m (IntMap v', Event t (p v'))
195−traverseIntMapWithKeyWithAdjustImpl base mergeMyDynIncremental f (dm0 :: IntMap v) dm' = do
196− (result0, result') <- lift $ base (\k v -> runDynamicWriterT $ f k v) dm0 dm'
197− let liftedResult0 = fmap fst result0
198− liftedResult' = fmap (fmap fst) result'
199− liftedWritten0 :: IntMap (Dynamic t w)
200− liftedWritten0 = fmap snd result0
201− liftedWritten' = fmap (fmap snd) result'
202− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
203− i <- holdIncremental liftedWritten0 liftedWritten'
204− tellDyn $ fmap (mconcat . IntMap.elems) $ incrementalToDynamic $ mergeMyDynIncremental i
205− return (liftedResult0, liftedResult')
206−
207−-- | Map a function over the output of a 'DynamicWriterT'.
208−withDynamicWriterT :: (Monoid w, Monoid w', Reflex t, MonadHold t m, MonadFix m)
209− => (w -> w')
210− -> DynamicWriterT t w m a
211− -> DynamicWriterT t w' m a
212−withDynamicWriterT f dw = do
213− (r, d) <- lift $ do
214− (r, d) <- runDynamicWriterT dw
215− let d' = fmap f d
216− return (r, d')
217− tellDyn d
218− return r
219−
220−instance Requester t m => Requester t (DynamicWriterT t w m) where
221− type Request (DynamicWriterT t w m) = Request m
222− type Response (DynamicWriterT t w m) = Response m
223− requesting = lift . requesting
224− requesting_ = lift . requesting_
225−
226−instance (MonadQuery t q m, Monad m) => MonadQuery t q (DynamicWriterT t w m) where
227− tellQueryIncremental = lift . tellQueryIncremental
228− askQueryResult = lift askQueryResult
229− queryIncremental = lift . queryIncremental
230−
231−instance EventWriter t w m => EventWriter t w (DynamicWriterT t v m) where
232− tellEvent = lift . tellEvent
src/Reflex/DynamicWriter/Class.hsdeleted−24
@@ -1,24 +0,0 @@
1−-- | This module defines the 'MonadDynamicWriter' class.
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE UndecidableInstances #-}
7−#ifdef USE_REFLEX_OPTIMIZER
8−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9−#endif
10−module Reflex.DynamicWriter.Class
11− ( MonadDynamicWriter (..)
12− ) where
13−
14−import Control.Monad.Reader (ReaderT, lift)
15−import Reflex.Class (Dynamic)
16−
17−-- | 'MonadDynamicWriter' efficiently collects 'Dynamic' values using 'tellDyn'
18−-- and combines them monoidally to provide a 'Dynamic' result.
19−class (Monad m, Monoid w) => MonadDynamicWriter t w m | m -> t w where
20− tellDyn :: Dynamic t w -> m ()
21−
22−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
23− tellDyn = lift . tellDyn
24−
src/Reflex/EventWriter.hsdeleted−7
@@ -1,7 +0,0 @@
1−module Reflex.EventWriter
2− {-# DEPRECATED "Use 'Reflex.EventWriter.Class' and 'Reflex.EventWriter.Base' instead, or just import 'Reflex'" #-}
3− ( module X
4− ) where
5−
6−import Reflex.EventWriter.Base as X
7−import Reflex.EventWriter.Class as X
src/Reflex/EventWriter/Base.hsdeleted−306
@@ -1,306 +0,0 @@
1−-- | This module provides 'EventWriterT', the standard implementation of
2−-- 'EventWriter'.
3−{-# LANGUAGE CPP #-}
4−{-# LANGUAGE ExistentialQuantification #-}
5−{-# LANGUAGE FlexibleInstances #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE Rank2Types #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE TypeOperators #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−module Reflex.EventWriter.Base
17− ( EventWriterT (..)
18− , runEventWriterT
19− , runWithReplaceEventWriterTWith
20− , sequenceDMapWithAdjustEventWriterTWith
21− , mapEventWriterT
22− , withEventWriterT
23− ) where
24−
25−import Reflex.Adjustable.Class
26−import Reflex.Class
27−import Reflex.EventWriter.Class (EventWriter, tellEvent)
28−import Reflex.DynamicWriter.Class (MonadDynamicWriter, tellDyn)
29−import Reflex.Host.Class
30−import Reflex.PerformEvent.Class
31−import Reflex.PostBuild.Class
32−import Reflex.Query.Class
33−import Reflex.Requester.Class
34−import Reflex.TriggerEvent.Class
35−
36−import Control.Monad.Exception
37−import Control.Monad.Identity
38−import Control.Monad.Primitive
39−import Control.Monad.Reader
40−import Control.Monad.Ref
41−import Control.Monad.State.Strict
42−import Data.Dependent.Map (DMap, DSum (..))
43−import qualified Data.Dependent.Map as DMap
44−import Data.Functor.Compose
45−import Data.Functor.Misc
46−import Data.GADT.Compare (GCompare (..), GEq (..), GOrdering (..))
47−import Data.IntMap.Strict (IntMap)
48−import qualified Data.IntMap.Strict as IntMap
49−import Data.List.NonEmpty (NonEmpty (..))
50−import Data.Map (Map)
51−import qualified Data.Map as Map
52−import Data.Semigroup
53−import Data.Some (Some)
54−import Data.Tuple
55−import Data.Type.Equality hiding (apply)
56−
57−import Unsafe.Coerce
58−
59−{-# DEPRECATED TellId "Do not construct this directly; use tellId instead" #-}
60−newtype TellId w x
61− = TellId Int -- ^ WARNING: Do not construct this directly; use 'tellId' instead
62− deriving (Show, Eq, Ord, Enum)
63−
64−tellId :: Int -> TellId w w
65−tellId = TellId
66−{-# INLINE tellId #-}
67−
68−tellIdRefl :: TellId w x -> w :~: x
69−tellIdRefl _ = unsafeCoerce Refl
70−
71−withTellIdRefl :: TellId w x -> (w ~ x => r) -> r
72−withTellIdRefl tid r = case tellIdRefl tid of
73− Refl -> r
74−
75−instance GEq (TellId w) where
76− a `geq` b =
77− withTellIdRefl a $
78− withTellIdRefl b $
79− if a == b
80− then Just Refl
81− else Nothing
82−
83−instance GCompare (TellId w) where
84− a `gcompare` b =
85− withTellIdRefl a $
86− withTellIdRefl b $
87− case a `compare` b of
88− LT -> GLT
89− EQ -> GEQ
90− GT -> GGT
91−
92−data EventWriterState t w = EventWriterState
93− { _eventWriterState_nextId :: {-# UNPACK #-} !Int -- Always negative (and decreasing over time)
94− , _eventWriterState_told :: ![DSum (TellId w) (Event t)] -- In increasing order
95− }
96−
97−-- | A basic implementation of 'EventWriter'.
98−newtype EventWriterT t w m a = EventWriterT { unEventWriterT :: StateT (EventWriterState t w) m a }
99− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
100−
101−-- | Run a 'EventWriterT' action.
102−runEventWriterT :: forall t m w a. (Reflex t, Monad m, Semigroup w) => EventWriterT t w m a -> m (a, Event t w)
103−runEventWriterT (EventWriterT a) = do
104− (result, requests) <- runStateT a $ EventWriterState (-1) []
105− let combineResults :: DMap (TellId w) Identity -> w
106− combineResults = sconcat
107− . (\(h : t) -> h :| t) -- Unconditional; 'merge' guarantees that it will only fire with non-empty DMaps
108− . DMap.foldlWithKey (\vs tid (Identity v) -> withTellIdRefl tid $ v : vs) [] -- This is where we finally reverse the DMap to get things in the correct order
109− return (result, fmap combineResults $ merge $ DMap.fromDistinctAscList $ _eventWriterState_told requests) --TODO: We can probably make this fromDistinctAscList more efficient by knowing the length in advance, but this will require exposing internals of DMap; also converting it to use a strict list might help
110−
111−instance (Reflex t, Monad m, Semigroup w) => EventWriter t w (EventWriterT t w m) where
112− tellEvent w = EventWriterT $ modify $ \old ->
113− let myId = _eventWriterState_nextId old
114− in EventWriterState
115− { _eventWriterState_nextId = pred myId
116− , _eventWriterState_told = (tellId myId :=> w) : _eventWriterState_told old
117− }
118−
119−instance MonadTrans (EventWriterT t w) where
120− lift = EventWriterT . lift
121−
122−instance MonadSample t m => MonadSample t (EventWriterT t w m) where
123− sample = lift . sample
124−
125−instance MonadHold t m => MonadHold t (EventWriterT t w m) where
126− {-# INLINABLE hold #-}
127− hold v0 = lift . hold v0
128− {-# INLINABLE holdDyn #-}
129− holdDyn v0 = lift . holdDyn v0
130− {-# INLINABLE holdIncremental #-}
131− holdIncremental v0 = lift . holdIncremental v0
132− {-# INLINABLE buildDynamic #-}
133− buildDynamic a0 = lift . buildDynamic a0
134− {-# INLINABLE headE #-}
135− headE = lift . headE
136−
137−instance (Reflex t, Adjustable t m, MonadHold t m, Semigroup w) => Adjustable t (EventWriterT t w m) where
138− runWithReplace = runWithReplaceEventWriterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
139− traverseIntMapWithKeyWithAdjust = sequenceIntMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') mergeIntIncremental coincidencePatchIntMap
140− traverseDMapWithKeyWithAdjust = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith mergeMapIncremental coincidencePatchMap
141− traverseDMapWithKeyWithAdjustWithMove = sequenceDMapWithAdjustEventWriterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith mergeMapIncrementalWithMove coincidencePatchMapWithMove
142−
143−instance Requester t m => Requester t (EventWriterT t w m) where
144− type Request (EventWriterT t w m) = Request m
145− type Response (EventWriterT t w m) = Response m
146− requesting = lift . requesting
147− requesting_ = lift . requesting_
148−
149−-- | Given a function like 'runWithReplace' for the underlying monad, implement
150−-- 'runWithReplace' for 'EventWriterT'. This is necessary when the underlying
151−-- monad doesn't have a 'Adjustable' instance or to override the default
152−-- 'Adjustable' behavior.
153−runWithReplaceEventWriterTWith :: forall m t w a b. (Reflex t, MonadHold t m, Semigroup w)
154− => (forall a' b'. m a' -> Event t (m b') -> EventWriterT t w m (a', Event t b'))
155− -> EventWriterT t w m a
156− -> Event t (EventWriterT t w m b)
157− -> EventWriterT t w m (a, Event t b)
158−runWithReplaceEventWriterTWith f a0 a' = do
159− (result0, result') <- f (runEventWriterT a0) $ fmapCheap runEventWriterT a'
160− tellEvent =<< switchHoldPromptOnly (snd result0) (fmapCheap snd result')
161− return (fst result0, fmapCheap fst result')
162−
163−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceIntMapWithAdjust'.
164−sequenceIntMapWithAdjustEventWriterTWith
165− :: forall t m p w v v'
166− . ( Reflex t
167− , MonadHold t m
168− , Semigroup w
169− , Functor p
170− , Patch (p (Event t w))
171− , PatchTarget (p (Event t w)) ~ IntMap (Event t w)
172− , Patch (p w)
173− , PatchTarget (p w) ~ IntMap w
174− )
175− => ( (IntMap.Key -> v -> m (Event t w, v'))
176− -> IntMap v
177− -> Event t (p v)
178− -> EventWriterT t w m (IntMap (Event t w, v'), Event t (p (Event t w, v')))
179− )
180− -> (Incremental t (p (Event t w)) -> Event t (PatchTarget (p w)))
181− -> (Event t (p (Event t w)) -> Event t (p w))
182− -> (IntMap.Key -> v -> EventWriterT t w m v')
183− -> IntMap v
184− -> Event t (p v)
185− -> EventWriterT t w m (IntMap v', Event t (p v'))
186−sequenceIntMapWithAdjustEventWriterTWith base mergePatchIncremental coincidencePatch f dm0 dm' = do
187− let f' :: IntMap.Key -> v -> m (Event t w, v')
188− f' k v = swap <$> runEventWriterT (f k v)
189− (children0, children') <- base f' dm0 dm'
190− let result0 = fmap snd children0
191− result' = fmapCheap (fmap snd) children'
192− requests0 :: IntMap (Event t w)
193− requests0 = fmap fst children0
194− requests' :: Event t (p (Event t w))
195− requests' = fmapCheap (fmap fst) children'
196− e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
197− tellEvent $ fforMaybeCheap e $ \m ->
198− case IntMap.elems m of
199− [] -> Nothing
200− h : t -> Just $ sconcat $ h :| t
201− return (result0, result')
202−
203−-- | Like 'runWithReplaceEventWriterTWith', but for 'sequenceDMapWithAdjust'.
204−sequenceDMapWithAdjustEventWriterTWith
205− :: forall t m p p' w k v v'
206− . ( Reflex t
207− , MonadHold t m
208− , Semigroup w
209− , Patch (p' (Some k) (Event t w))
210− , PatchTarget (p' (Some k) (Event t w)) ~ Map (Some k) (Event t w)
211− , GCompare k
212− , Patch (p' (Some k) w)
213− , PatchTarget (p' (Some k) w) ~ Map (Some k) w
214− )
215− => ( (forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a))
216− -> DMap k v
217− -> Event t (p k v)
218− -> EventWriterT t w m (DMap k (Compose ((,) (Event t w)) v'), Event t (p k (Compose ((,) (Event t w)) v')))
219− )
220− -> ((forall a. Compose ((,) (Event t w)) v' a -> v' a) -> p k (Compose ((,) (Event t w)) v') -> p k v')
221− -> ((forall a. Compose ((,) (Event t w)) v' a -> Event t w) -> p k (Compose ((,) (Event t w)) v') -> p' (Some k) (Event t w))
222− -> (Incremental t (p' (Some k) (Event t w)) -> Event t (PatchTarget (p' (Some k) w)))
223− -> (Event t (p' (Some k) (Event t w)) -> Event t (p' (Some k) w))
224− -> (forall a. k a -> v a -> EventWriterT t w m (v' a))
225− -> DMap k v
226− -> Event t (p k v)
227− -> EventWriterT t w m (DMap k v', Event t (p k v'))
228−sequenceDMapWithAdjustEventWriterTWith base mapPatch weakenPatchWith mergePatchIncremental coincidencePatch f dm0 dm' = do
229− let f' :: forall a. k a -> v a -> m (Compose ((,) (Event t w)) v' a)
230− f' k v = Compose . swap <$> runEventWriterT (f k v)
231− (children0, children') <- base f' dm0 dm'
232− let result0 = DMap.map (snd . getCompose) children0
233− result' = fforCheap children' $ mapPatch $ snd . getCompose
234− requests0 :: Map (Some k) (Event t w)
235− requests0 = weakenDMapWith (fst . getCompose) children0
236− requests' :: Event t (p' (Some k) (Event t w))
237− requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
238− e <- switchHoldPromptOnlyIncremental mergePatchIncremental coincidencePatch requests0 requests'
239− tellEvent $ fforMaybeCheap e $ \m ->
240− case Map.elems m of
241− [] -> Nothing
242− h : t -> Just $ sconcat $ h :| t
243− return (result0, result')
244−
245−instance PerformEvent t m => PerformEvent t (EventWriterT t w m) where
246− type Performable (EventWriterT t w m) = Performable m
247− performEvent_ = lift . performEvent_
248− performEvent = lift . performEvent
249−
250−instance PostBuild t m => PostBuild t (EventWriterT t w m) where
251− getPostBuild = lift getPostBuild
252−
253−instance TriggerEvent t m => TriggerEvent t (EventWriterT t w m) where
254− newTriggerEvent = lift newTriggerEvent
255− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
256− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
257−
258−instance MonadReader r m => MonadReader r (EventWriterT t w m) where
259− ask = lift ask
260− local f (EventWriterT a) = EventWriterT $ mapStateT (local f) a
261− reader = lift . reader
262−
263−instance MonadRef m => MonadRef (EventWriterT t w m) where
264− type Ref (EventWriterT t w m) = Ref m
265− newRef = lift . newRef
266− readRef = lift . readRef
267− writeRef r = lift . writeRef r
268−
269−instance MonadAtomicRef m => MonadAtomicRef (EventWriterT t w m) where
270− atomicModifyRef r = lift . atomicModifyRef r
271−
272−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (EventWriterT t w m) where
273− newEventWithTrigger = lift . newEventWithTrigger
274− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
275−
276−instance (MonadQuery t q m, Monad m) => MonadQuery t q (EventWriterT t w m) where
277− tellQueryIncremental = lift . tellQueryIncremental
278− askQueryResult = lift askQueryResult
279− queryIncremental = lift . queryIncremental
280−
281−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (EventWriterT t v m) where
282− tellDyn = lift . tellDyn
283−
284−instance PrimMonad m => PrimMonad (EventWriterT t w m) where
285− type PrimState (EventWriterT t w m) = PrimState m
286− primitive = lift . primitive
287−
288−-- | Map a function over the output of a 'EventWriterT'.
289−withEventWriterT :: (Semigroup w, Semigroup w', Reflex t, MonadHold t m)
290− => (w -> w')
291− -> EventWriterT t w m a
292− -> EventWriterT t w' m a
293−withEventWriterT f ew = do
294− (r, e) <- lift $ do
295− (r, e) <- runEventWriterT ew
296− let e' = fmap f e
297− return (r, e')
298− tellEvent e
299− return r
300−
301−-- | Change the monad underlying an EventWriterT
302−mapEventWriterT
303− :: (forall x. m x -> n x)
304− -> EventWriterT t w m a
305− -> EventWriterT t w n a
306−mapEventWriterT f (EventWriterT a) = EventWriterT $ mapStateT f a
src/Reflex/EventWriter/Class.hsdeleted−27
@@ -1,27 +0,0 @@
1−-- | This module defines the 'EventWriter' class.
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE UndecidableInstances #-}
7−#ifdef USE_REFLEX_OPTIMIZER
8−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
9−#endif
10−module Reflex.EventWriter.Class
11− ( EventWriter (..)
12− ) where
13−
14−import Control.Monad.Reader (ReaderT, lift)
15−import Data.Semigroup (Semigroup)
16−
17−import Reflex.Class (Event)
18−
19−
20−-- | 'EventWriter' efficiently collects 'Event' values using 'tellEvent'
21−-- and combines them via 'Semigroup' to provide an 'Event' result.
22−class (Monad m, Semigroup w) => EventWriter t w m | m -> t w where
23− tellEvent :: Event t w -> m ()
24−
25−
26−instance EventWriter t w m => EventWriter t w (ReaderT r m) where
27− tellEvent = lift . tellEvent
src/Reflex/FastWeak.hsdeleted−146
@@ -1,146 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE LambdaCase #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−#if GHCJS_FAST_WEAK
5−{-# LANGUAGE ForeignFunctionInterface #-}
6−{-# LANGUAGE JavaScriptFFI #-}
7−#endif
8−module Reflex.FastWeak
9− ( FastWeakTicket
10− , FastWeak
11− , mkFastWeakTicket
12− , getFastWeakTicketValue
13− , getFastWeakTicketWeak
14− , getFastWeakValue
15− , getFastWeakTicket
16− , emptyFastWeak
17−#ifdef GHCJS_FAST_WEAK
18− --TODO: Move these elsewhere
19− , unsafeFromRawJSVal
20− , unsafeToRawJSVal
21− , js_isNull
22−#endif
23− ) where
24−
25−import GHC.Exts (Any)
26−import Unsafe.Coerce
27−
28−#ifdef GHCJS_FAST_WEAK
29−import GHCJS.Types
30−#else
31−import Control.Exception (evaluate)
32−import System.IO.Unsafe
33−import System.Mem.Weak
34−#endif
35−
36−
37−#ifdef GHCJS_FAST_WEAK
38−newtype FastWeakTicket a = FastWeakTicket JSVal
39−
40−newtype FastWeak a = FastWeak JSVal
41−
42−-- Just designed to mirror JSVal, so that we can unsafeCoerce between the two
43−data Val a = Val { unVal :: a }
44−
45−-- | Coerce a JSVal that represents the heap object of a value of type 'a' into a value of type 'a'
46−unsafeFromRawJSVal :: JSVal -> a
47−unsafeFromRawJSVal v = unVal (unsafeCoerce v)
48−
49−unsafeToRawJSVal :: a -> JSVal
50−unsafeToRawJSVal v = unsafeCoerce (Val v)
51−#else
52−data FastWeakTicket a = FastWeakTicket
53− { _fastWeakTicket_val :: !a
54− , _fastWeakTicket_weak :: {-# UNPACK #-} !(Weak a)
55− }
56−
57−type FastWeak a = Weak a
58−#endif
59−
60−-- This needs to be in IO so we know that we've relinquished the ticket
61−getFastWeakTicketValue :: FastWeakTicket a -> IO a
62−#ifdef GHCJS_FAST_WEAK
63−getFastWeakTicketValue t = do
64− v <- js_ticketVal t
65− return $ unsafeFromRawJSVal v
66−
67−foreign import javascript unsafe "$r = $1.val;" js_ticketVal :: FastWeakTicket a -> IO JSVal
68−#else
69−getFastWeakTicketValue = return . _fastWeakTicket_val
70−#endif
71−
72−getFastWeakValue :: FastWeak a -> IO (Maybe a)
73−#ifdef GHCJS_FAST_WEAK
74−getFastWeakValue w = do
75− r <- js_weakVal w
76− case js_isNull r of
77− True -> return Nothing
78− False -> return $ Just $ unsafeFromRawJSVal r
79−
80−foreign import javascript unsafe "$1 === null" js_isNull :: JSVal -> Bool
81−
82−foreign import javascript unsafe "$r = ($1.ticket === null) ? null : $1.ticket.val;" js_weakVal :: FastWeak a -> IO JSVal
83−#else
84−getFastWeakValue = deRefWeak
85−#endif
86−
87−getFastWeakTicket :: forall a. FastWeak a -> IO (Maybe (FastWeakTicket a))
88−#ifdef GHCJS_FAST_WEAK
89−getFastWeakTicket w = do
90− r <- js_weakTicket w
91− case js_isNull r of
92− True -> return Nothing
93− False -> return $ Just $ FastWeakTicket r
94−
95−foreign import javascript unsafe "$r = $1.ticket;" js_weakTicket :: FastWeak a -> IO JSVal
96−#else
97−getFastWeakTicket w = do
98− deRefWeak w >>= \case
99− Nothing -> return Nothing
100− Just v -> return $ Just $ FastWeakTicket
101− { _fastWeakTicket_val = v
102− , _fastWeakTicket_weak = w
103− }
104−#endif
105−
106−-- I think it's fine if this is lazy - it'll retain the 'a', but so would the output; we just need to make sure it's forced before we start relying on the associated FastWeak to actually be weak
107−mkFastWeakTicket :: a -> IO (FastWeakTicket a)
108−#ifdef GHCJS_FAST_WEAK
109−mkFastWeakTicket v = js_fastWeakTicket (unsafeToRawJSVal v)
110−
111−foreign import javascript unsafe "$r = new h$FastWeakTicket($1);" js_fastWeakTicket :: JSVal -> IO (FastWeakTicket a)
112−#else
113−mkFastWeakTicket v = do
114− v' <- evaluate v
115− w <- mkWeakPtr v' Nothing
116− return $ FastWeakTicket
117− { _fastWeakTicket_val = v'
118− , _fastWeakTicket_weak = w
119− }
120−#endif
121−
122−-- Needs IO so that it can force the value - otherwise, could end up with a reference to the Ticket, which would retain the value
123−#ifdef GHCJS_FAST_WEAK
124−foreign import javascript unsafe "$r = $1.weak;" getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
125−#else
126−getFastWeakTicketWeak :: FastWeakTicket a -> IO (FastWeak a)
127−getFastWeakTicketWeak = return . _fastWeakTicket_weak
128−#endif
129−
130−-- | A weak reference that is always empty
131−emptyFastWeak :: FastWeak a
132−emptyFastWeak = unsafeCoerce w
133− where w :: FastWeak Any
134−#ifdef GHCJS_FAST_WEAK
135− w = js_emptyWeak
136−#else
137− w = unsafePerformIO $ do
138− w' <- mkWeakPtr undefined Nothing
139− finalize w'
140− return w'
141−#endif
142−{-# NOINLINE emptyFastWeak #-}
143−
144−#ifdef GHCJS_FAST_WEAK
145−foreign import javascript unsafe "$r = new h$FastWeak(null);" js_emptyWeak :: FastWeak Any
146−#endif
src/Reflex/FunctorMaybe.hsdeleted−43
@@ -1,43 +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.IntMap (IntMap)
8−import qualified Data.IntMap as IntMap
9−import Data.Map (Map)
10−import qualified Data.Map as Map
11−import Data.Maybe
12−
13−--TODO: See if there's a better class in the standard libraries already
14−
15−-- | A class for values that combines filtering and mapping using 'Maybe'.
16−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
17−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
18−-- and e.g. 'Event' is instance of neither.
19−--
20−-- A definition of 'fmapMaybe' must satisfy the following laws:
21−--
22−-- [/identity/]
23−-- @'fmapMaybe' 'Just' ≡ 'id'@
24−--
25−-- [/composition/]
26−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
27−class FunctorMaybe f where
28− -- | Combined mapping and filtering function.
29− fmapMaybe :: (a -> Maybe b) -> f a -> f b
30−
31−-- | @fmapMaybe = (=<<)
32−instance FunctorMaybe Maybe where
33− fmapMaybe = (=<<)
34−
35−-- | @fmapMaybe = mapMaybe@
36−instance FunctorMaybe [] where
37− fmapMaybe = mapMaybe
38−
39−instance FunctorMaybe (Map k) where
40− fmapMaybe = Map.mapMaybe
41−
42−instance FunctorMaybe IntMap where
43− fmapMaybe = IntMap.mapMaybe
src/Reflex/Host/Class.hsdeleted−280
@@ -1,280 +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.Monad.Fix
34−import Control.Monad.Identity
35−import Control.Monad.Ref
36−import Control.Monad.Trans
37−import Control.Monad.Trans.Cont (ContT)
38−import Control.Monad.Trans.Except (ExceptT)
39−import Control.Monad.Trans.Reader (ReaderT)
40−import Control.Monad.Trans.RWS (RWST)
41−import Control.Monad.Trans.State (StateT)
42−import qualified Control.Monad.Trans.State.Strict as Strict
43−import Control.Monad.Trans.Writer (WriterT)
44−import Data.Dependent.Sum (DSum (..))
45−import Data.GADT.Compare
46−
47−-- | Framework implementation support class for the reflex implementation
48−-- represented by @t@.
49−class ( Reflex t
50− , MonadReflexCreateTrigger t (HostFrame t)
51− , MonadSample t (HostFrame t)
52− , MonadHold t (HostFrame t)
53− , MonadFix (HostFrame t)
54− , MonadSubscribeEvent t (HostFrame t)
55− ) => ReflexHost t where
56− type EventTrigger t :: * -> *
57− type EventHandle t :: * -> *
58− type HostFrame t :: * -> *
59−
60−-- | Monad in which Events can be 'subscribed'. This forces all underlying
61−-- event sources to be initialized, so that the event will fire whenever it
62−-- ought to. Events must be subscribed before they are read using readEvent
63−class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
64− -- | Subscribe to an event and set it up if needed.
65− --
66− -- This function will create a new 'EventHandle' from an 'Event'. This handle
67− -- may then be used via 'readEvent' in the read callback of
68− -- 'fireEventsAndRead'.
69− --
70− -- If the event wasn't subscribed to before (either manually or through a
71− -- dependent event or behavior) then this function will cause the event and
72− -- all dependencies of this event to be set up. For example, if the event was
73− -- created by 'newEventWithTrigger', then it's callback will be executed.
74− --
75− -- It's safe to call this function multiple times.
76− subscribeEvent :: Event t a -> m (EventHandle t a)
77−
78−-- | Monad that allows to read events' values.
79−class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
80− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling
81− -- 'subscribeEvent').
82− --
83− -- After event propagation is done, all events can be in two states: either
84− -- they are firing with some value or they are not firing. In the former
85− -- case, this function returns @Just act@, where @act@ in an action to read
86− -- the current value of the event. In the latter case, the function returns
87− -- @Nothing@.
88− --
89− -- This function is normally used in the calllback for 'fireEventsAndRead'.
90− readEvent :: EventHandle t a -> m (Maybe (m a))
91−
92−-- | A monad where new events feed from external sources can be created.
93−class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
94− -- | Creates a root 'Event' (one that is not based on any other event).
95− --
96− -- When a subscriber first subscribes to an event (building another event that
97− -- depends on the subscription) the given callback function is run and passed
98− -- a trigger. The callback function can then set up the event source in IO.
99− -- After this is done, the callback function must return an accompanying
100− -- teardown action.
101− --
102− -- Any time between setup and teardown the trigger can be used to fire the
103− -- event, by passing it to 'fireEventsAndRead'.
104− --
105− -- Note: An event may be set up multiple times. So after the teardown action
106− -- is executed, the event may still be set up again in the future.
107− newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
108− newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
109−
110−-- | 'MonadReflexHost' designates monads that can run reflex frames.
111−class ( ReflexHost t
112− , MonadReflexCreateTrigger t m
113− , MonadSubscribeEvent t m
114− , MonadReadEvent t (ReadPhase m)
115− , MonadSample t (ReadPhase m)
116− , MonadHold t (ReadPhase m)
117− ) => MonadReflexHost t m | m -> t where
118− type ReadPhase m :: * -> *
119− -- | Propagate some events firings and read the values of events afterwards.
120− --
121− -- This function will create a new frame to fire the given events. It will
122− -- then update all dependent events and behaviors. After that is done, the
123− -- given callback is executed which allows to read the final values of events
124− -- and check whether they have fired in this frame or not.
125− --
126− -- All events that are given are fired at the same time.
127− --
128− -- This function is typically used in the main loop of a reflex framework
129− -- implementation. The main loop waits for external events to happen (such as
130− -- keyboard input or a mouse click) and then fires the corresponding events
131− -- using this function. The read callback can be used to read output events
132− -- and perform a corresponding response action to the external event.
133− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
134−
135− -- | Run a frame without any events firing.
136− --
137− -- This function should be used when you want to use 'sample' and 'hold' when
138− -- no events are currently firing. Using this function in that case can
139− -- improve performance, since the implementation can assume that no events are
140− -- firing when 'sample' or 'hold' are called.
141− --
142− -- This function is commonly used to set up the basic event network when the
143− -- application starts up.
144− runHostFrame :: HostFrame t a -> m a
145−
146−-- | Like 'fireEventsAndRead', but without reading any events.
147−fireEvents :: MonadReflexHost t m => [DSum (EventTrigger t) Identity] -> m ()
148−fireEvents dm = fireEventsAndRead dm $ return ()
149−{-# INLINE fireEvents #-}
150−
151−-- | Create a new event and store its trigger in an 'IORef' while it's active.
152−--
153−-- An event is only active between the set up (when it's first subscribed to)
154−-- and the teardown phases (when noboby is subscribing the event anymore). This
155−-- function returns an Event and an 'IORef'. As long as the event is active, the
156−-- 'IORef' will contain 'Just' the event trigger to trigger this event. When the
157−-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
158−-- sources to be more efficient, since they don't need to produce events when
159−-- nobody is listening.
160−newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
161−newEventWithTriggerRef = do
162− rt <- newRef Nothing
163− e <- newEventWithTrigger $ \t -> do
164− writeRef rt $ Just t
165− return $ writeRef rt Nothing
166− return (e, rt)
167−{-# INLINE newEventWithTriggerRef #-}
168−
169−-- | Fire the given trigger if it is not 'Nothing'.
170−fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
171−fireEventRef mtRef input = do
172− mt <- readRef mtRef
173− case mt of
174− Nothing -> return ()
175− Just trigger -> fireEvents [trigger :=> Identity input]
176−
177−-- | Fire the given trigger if it is not 'Nothing', and read from the given
178−-- 'EventHandle'.
179−fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
180−fireEventRefAndRead mtRef input e = do
181− mt <- readRef mtRef
182− case mt of
183− Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
184− Just trigger -> fireEventsAndRead [trigger :=> Identity input] $ do
185− mGetValue <- readEvent e
186− case mGetValue of
187− Nothing -> return Nothing
188− Just getValue -> fmap Just getValue
189−
190−
191−--------------------------------------------------------------------------------
192−-- Instances
193−--------------------------------------------------------------------------------
194−
195−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ReaderT r m) where
196− newEventWithTrigger = lift . newEventWithTrigger
197− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
198−
199−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ReaderT r m) where
200− subscribeEvent = lift . subscribeEvent
201−
202−instance MonadReflexHost t m => MonadReflexHost t (ReaderT r m) where
203− type ReadPhase (ReaderT r m) = ReadPhase m
204− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
205− runHostFrame = lift . runHostFrame
206−
207−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (WriterT w m) where
208− newEventWithTrigger = lift . newEventWithTrigger
209− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
210−
211−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (WriterT w m) where
212− subscribeEvent = lift . subscribeEvent
213−
214−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (WriterT w m) where
215− type ReadPhase (WriterT w m) = ReadPhase m
216− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
217− runHostFrame = lift . runHostFrame
218−
219−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StateT s m) where
220− newEventWithTrigger = lift . newEventWithTrigger
221− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
222−
223−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (StateT r m) where
224− subscribeEvent = lift . subscribeEvent
225−
226−instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
227− type ReadPhase (StateT s m) = ReadPhase m
228− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
229− runHostFrame = lift . runHostFrame
230−
231−
232−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
233− newEventWithTrigger = lift . newEventWithTrigger
234− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
235−
236−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
237− subscribeEvent = lift . subscribeEvent
238−
239−instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
240− type ReadPhase (Strict.StateT s m) = ReadPhase m
241− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
242− runHostFrame = lift . runHostFrame
243−
244−
245−
246−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
247− newEventWithTrigger = lift . newEventWithTrigger
248− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
249−
250−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ContT r m) where
251− subscribeEvent = lift . subscribeEvent
252−
253−instance MonadReflexHost t m => MonadReflexHost t (ContT r m) where
254− type ReadPhase (ContT r m) = ReadPhase m
255− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
256− runHostFrame = lift . runHostFrame
257−
258−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ExceptT e m) where
259− newEventWithTrigger = lift . newEventWithTrigger
260− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
261−
262−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ExceptT r m) where
263− subscribeEvent = lift . subscribeEvent
264−
265−instance MonadReflexHost t m => MonadReflexHost t (ExceptT e m) where
266− type ReadPhase (ExceptT e m) = ReadPhase m
267− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
268− runHostFrame = lift . runHostFrame
269−
270−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (RWST r w s m) where
271− newEventWithTrigger = lift . newEventWithTrigger
272− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
273−
274−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (RWST r w s m) where
275− subscribeEvent = lift . subscribeEvent
276−
277−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (RWST r w s m) where
278− type ReadPhase (RWST r w s m) = ReadPhase m
279− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
280− runHostFrame = lift . runHostFrame
src/Reflex/Network.hsdeleted−39
@@ -1,39 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE MultiParamTypeClasses #-}
3−#ifdef USE_REFLEX_OPTIMIZER
4−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
5−#endif
6−module Reflex.Network
7− ( networkView
8− , networkHold
9− , untilReady
10− ) where
11−
12−import Reflex.Class
13−import Reflex.Adjustable.Class
14−import Reflex.NotReady.Class
15−import Reflex.PostBuild.Class
16−
17−-- | Given a Dynamic of network-creating actions, create a network that is recreated whenever the Dynamic updates.
18−-- The returned Event of network results occurs when the Dynamic does.
19−-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events that would typically be flattened (via 'switchPromptly').
20−networkView :: (NotReady t m, Adjustable t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
21−networkView child = do
22− postBuild <- getPostBuild
23− let newChild = leftmost [updated child, tagCheap (current child) postBuild]
24− snd <$> runWithReplace notReady newChild
25−
26−-- | Given an initial network and an Event of network-creating actions, create a network that is recreated whenever the Event fires.
27−-- The returned Dynamic of network results occurs when the Event does.
28−-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
29−networkHold :: (Adjustable t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
30−networkHold child0 newChild = do
31− (result0, newResult) <- runWithReplace child0 newChild
32− holdDyn result0 newResult
33−
34−-- | Render a placeholder network to be shown while another network is not yet
35−-- done building
36−untilReady :: (Adjustable t m, PostBuild t m) => m a -> m b -> m (a, Event t b)
37−untilReady a b = do
38− postBuild <- getPostBuild
39− runWithReplace a $ b <$ postBuild
src/Reflex/NotReady/Class.hsdeleted−68
@@ -1,68 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DefaultSignatures #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−#ifdef USE_REFLEX_OPTIMIZER
10−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11−#endif
12−module Reflex.NotReady.Class
13− ( NotReady(..)
14− ) where
15−
16−import Control.Monad.Reader (ReaderT)
17−import Control.Monad.Trans
18−
19−import Reflex.Class
20−import Reflex.DynamicWriter.Base (DynamicWriterT)
21−import Reflex.EventWriter.Base (EventWriterT)
22−import Reflex.Host.Class
23−import Reflex.PerformEvent.Base (PerformEventT (..))
24−import Reflex.PostBuild.Base (PostBuildT)
25−import Reflex.Query.Base (QueryT)
26−import Reflex.Requester.Base (RequesterT)
27−import Reflex.TriggerEvent.Base (TriggerEventT)
28−
29−class Monad m => NotReady t m | m -> t where
30− notReadyUntil :: Event t a -> m ()
31− default notReadyUntil :: (MonadTrans f, m ~ f m', NotReady t m') => Event t a -> m ()
32− notReadyUntil = lift . notReadyUntil
33−
34− notReady :: m ()
35− default notReady :: (MonadTrans f, m ~ f m', NotReady t m') => m ()
36− notReady = lift notReady
37−
38−instance NotReady t m => NotReady t (ReaderT r m) where
39− notReadyUntil = lift . notReadyUntil
40− notReady = lift notReady
41−
42−instance NotReady t m => NotReady t (PostBuildT t m) where
43− notReadyUntil = lift . notReadyUntil
44− notReady = lift notReady
45−
46−instance NotReady t m => NotReady t (EventWriterT t w m) where
47− notReadyUntil = lift . notReadyUntil
48− notReady = lift notReady
49−
50−instance NotReady t m => NotReady t (DynamicWriterT t w m) where
51− notReadyUntil = lift . notReadyUntil
52− notReady = lift notReady
53−
54−instance NotReady t m => NotReady t (QueryT t q m) where
55− notReadyUntil = lift . notReadyUntil
56− notReady = lift notReady
57−
58−instance (ReflexHost t, NotReady t (HostFrame t)) => NotReady t (PerformEventT t m) where
59− notReadyUntil = PerformEventT . notReadyUntil
60− notReady = PerformEventT notReady
61−
62−instance NotReady t m => NotReady t (RequesterT t request response m) where
63− notReadyUntil = lift . notReadyUntil
64− notReady = lift notReady
65−
66−instance NotReady t m => NotReady t (TriggerEventT t m) where
67− notReadyUntil = lift . notReadyUntil
68− notReady = lift notReady
src/Reflex/Optimizer.hsdeleted−61
@@ -1,61 +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−
20−#if MIN_VERSION_base(4,9,0)
21−import Prelude hiding ((<>))
22−#endif
23−
24−#endif
25−
26−#ifdef ghcjs_HOST_OS
27−
28−-- | The GHCJS build of Reflex.Optimizer just throws an error; instead, the version built with GHC should be used.
29−plugin :: Plugin
30−plugin = error "The GHCJS build of Reflex.Optimizer cannot be used. Instead, build with GHC and use the result with GHCJS."
31−
32−#else
33−
34−-- | The GHC plugin itself. See "GhcPlugins" for more details.
35−plugin :: Plugin
36−plugin = defaultPlugin { installCoreToDos = install }
37−
38−install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
39−install [] p = do
40− liftIO $ putStrLn $ showSDocUnsafe $ ppr p
41− let f = \case
42− simpl@(CoreDoSimplify _ _) -> [CoreDoSpecialising, simpl]
43− x -> [x]
44− return $ makeInlinable : concatMap f p
45−install options@(_:_) p = do
46− warnMsg $ "Reflex.Optimizer: ignoring " <> fromString (show $ length options) <> " command-line options"
47− install [] p
48−
49−makeInlinable :: CoreToDo
50−makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
51− let f v = setIdInfo v $ let i = idInfo v in
52− setInlinePragInfo i $ let p = inlinePragInfo i in
53− if isDefaultInlinePragma p
54− then defaultInlinePragma { inl_inline = Inlinable }
55− else p
56− newBinds = flip map (mg_binds modGuts) $ \case
57− NonRec b e -> NonRec (f b) e
58− Rec bes -> Rec $ map (first f) bes
59− return $ modGuts { mg_binds = newBinds }
60−
61−#endif
src/Reflex/Patch.hsdeleted−39
@@ -1,39 +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 hiding (getDeletions)
11−import Reflex.Patch.DMapWithMove as X (PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove,
12− patchDMapWithMoveToPatchMapWithMoveWith,
13− traversePatchDMapWithMoveWithKey, unPatchDMapWithMove,
14− unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith)
15−import Reflex.Patch.IntMap as X hiding (getDeletions)
16−import Reflex.Patch.Map as X
17−import Reflex.Patch.MapWithMove as X (PatchMapWithMove, patchMapWithMoveNewElements,
18− patchMapWithMoveNewElementsMap, unPatchMapWithMove,
19− unsafePatchMapWithMove)
20−
21−import Data.Semigroup (Semigroup (..), (<>))
22−
23−---- Patches based on commutative groups
24−
25−-- | A 'Group' is a 'Monoid' where every element has an inverse.
26−class (Semigroup q, Monoid q) => Group q where
27− negateG :: q -> q
28− (~~) :: q -> q -> q
29− r ~~ s = r <> negateG s
30−
31−-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
32−class Semigroup q => Additive q where
33−
34−-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
35−newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
36−
37−instance Additive p => Patch (AdditivePatch p) where
38− type PatchTarget (AdditivePatch p) = p
39− apply (AdditivePatch p) q = Just $ p <> q
src/Reflex/Patch/IntMap.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFoldable #-}
3−{-# LANGUAGE DeriveFunctor #-}
4−{-# LANGUAGE DeriveTraversable #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE TypeFamilies #-}
7−module Reflex.Patch.IntMap where
8−
9−import Data.IntMap.Strict (IntMap)
10−import qualified Data.IntMap.Strict as IntMap
11−import Data.Maybe
12−import Data.Semigroup
13−import Reflex.Patch.Class
14−
15−newtype PatchIntMap a = PatchIntMap (IntMap (Maybe a)) deriving (Functor, Foldable, Traversable, Monoid)
16−
17−instance Patch (PatchIntMap a) where
18− type PatchTarget (PatchIntMap a) = IntMap a
19− apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
20− let removes = IntMap.filter isNothing p
21− adds = IntMap.mapMaybe id p
22− in IntMap.union adds $ v `IntMap.difference` removes
23−
24−-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
25−-- If the same key is modified by both patches, the one on the left will take
26−-- precedence.
27−instance Semigroup (PatchIntMap v) where
28− PatchIntMap a <> PatchIntMap b = PatchIntMap $ a `mappend` b --TODO: Add a semigroup instance for Map
29− -- PatchMap is idempotent, so stimes n is id for every n
30−#if MIN_VERSION_semigroups(0,17,0)
31− stimes = stimesIdempotentMonoid
32−#else
33− times1p n x = case compare n 0 of
34− LT -> error "stimesIdempotentMonoid: negative multiplier"
35− EQ -> mempty
36− GT -> x
37−#endif
38−
39−traverseIntMapPatchWithKey :: Applicative t => (Int -> a -> t b) -> PatchIntMap a -> t (PatchIntMap b)
40−traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (\k mv -> traverse (f k) mv) m
41−
42−patchIntMapNewElements :: PatchIntMap a -> [a]
43−patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
44−
45−patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
46−patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
47−
48−getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
49−getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Reflex/Patch/MapWithMove.hsdeleted−228
@@ -1,228 +0,0 @@
1−{-# LANGUAGE DeriveFoldable #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE DeriveTraversable #-}
4−{-# LANGUAGE FlexibleContexts #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE PatternGuards #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE TypeFamilies #-}
9−-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
10−-- another
11−module Reflex.Patch.MapWithMove where
12−
13−import Reflex.Patch.Class
14−
15−import Control.Arrow
16−import Control.Monad.State
17−import Data.Foldable
18−import Data.Function
19−import Data.List
20−import Data.Map (Map)
21−import qualified Data.Map as Map
22−import Data.Maybe
23−import Data.Semigroup
24−import qualified Data.Set as Set
25−import Data.These
26−import Data.Tuple
27−
28−-- | Patch a DMap with additions, deletions, and moves. Invariant: If key @k1@
29−-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
30−-- and vice versa. There should never be any unpaired From/To keys.
31−newtype PatchMapWithMove k v = PatchMapWithMove (Map k (NodeInfo k v)) deriving (Show, Eq, Ord, Functor, Foldable, Traversable)
32−
33−-- | Holds the information about each key: where its new value should come from,
34−-- and where its old value should go to
35−data NodeInfo k v = NodeInfo
36− { _nodeInfo_from :: !(From k v)
37− -- ^ Where do we get the new value for this key?
38− , _nodeInfo_to :: !(To k)
39− -- ^ If the old value is being kept (i.e. moved rather than deleted or
40− -- replaced), where is it going?
41− }
42− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
43−
44−-- | Create a 'PatchMapWithMove', validating it
45−patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
46−patchMapWithMove m = if valid then Just $ PatchMapWithMove m else Nothing
47− where valid = forwardLinks == backwardLinks
48− forwardLinks = Map.mapMaybe _nodeInfo_to m
49− backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, v) ->
50− case _nodeInfo_from v of
51− From_Move from -> Just (from, to)
52− _ -> Nothing
53−
54−-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
55−patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
56−patchMapWithMoveInsertAll m = PatchMapWithMove $ flip fmap m $ \v -> NodeInfo
57− { _nodeInfo_from = From_Insert v
58− , _nodeInfo_to = Nothing
59− }
60−
61−-- | Extract the internal representation of the 'PatchMapWithMove'
62−unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
63−unPatchMapWithMove (PatchMapWithMove p) = p
64−
65−-- | Warning: when using this function, you must ensure that the invariants of
66−-- 'PatchMapWithMove' are preserved; they will not be checked.
67−unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
68−unsafePatchMapWithMove = PatchMapWithMove
69−
70−-- | Describe how a key's new value should be produced
71−data From k v
72− = From_Insert v -- ^ Insert the given value here
73− | From_Delete -- ^ Delete the existing value, if any, from here
74− | From_Move !k -- ^ Move the value here from the given key
75− deriving (Show, Read, Eq, Ord, Functor, Foldable, Traversable)
76−
77−-- | Describe where a key's old value will go. If this is 'Just', that means
78−-- the key's old value will be moved to the given other key; if it is 'Nothing',
79−-- that means it will be deleted.
80−type To = Maybe
81−
82−-- | Apply the insertions, deletions, and moves to a given 'Map'
83−instance Ord k => Patch (PatchMapWithMove k v) where
84− type PatchTarget (PatchMapWithMove k v) = Map k v
85− 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?
86− where insertions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
87− From_Insert v -> Just v
88− From_Move k -> Map.lookup k old
89− From_Delete -> Nothing
90− deletions = flip Map.mapMaybeWithKey p $ \_ ni -> case _nodeInfo_from ni of
91− From_Delete -> Just ()
92− _ -> Nothing
93−
94−-- | Returns all the new elements that will be added to the 'Map'
95−patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
96−patchMapWithMoveNewElements = Map.elems . patchMapWithMoveNewElementsMap
97−
98−patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
99−patchMapWithMoveNewElementsMap (PatchMapWithMove p) = Map.mapMaybe f p
100− where f ni = case _nodeInfo_from ni of
101− From_Insert v -> Just v
102− From_Move _ -> Nothing
103− From_Delete -> Nothing
104−
105−-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
106−-- its values using the given ordering function. The set keys of the 'Map' is
107−-- not changed.
108−patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
109−patchThatSortsMapWith cmp m = PatchMapWithMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
110− where unsorted = Map.toList m
111− sorted = sortBy (cmp `on` snd) unsorted
112− f (to, _) (from, _) = if to == from then Nothing else
113− Just (from, to)
114− reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
115− g (to, _) (from, _) = if to == from then Nothing else
116− let Just movingTo = Map.lookup from reverseMapping
117− in Just (to, NodeInfo (From_Move from) $ Just movingTo)
118−
119−-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
120−-- will produce a 'Map' with the same values as the second 'Map' but with the
121−-- values sorted with the given ordering function.
122−patchThatChangesAndSortsMapWith :: forall k v. (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
123−patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
124− where newList = Map.toList newByIndexUnsorted
125− newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
126−
127−-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
128−-- will produce the second 'Map'.
129−patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
130−patchThatChangesMap oldByIndex newByIndex = patch
131− where oldByValue = Map.fromListWith Set.union $ swap . first Set.singleton <$> Map.toList oldByIndex
132− (insertsAndMoves, unusedValuesByValue) = flip runState oldByValue $ do
133− let f k v = do
134− remainingValues <- get
135− let putRemainingKeys remainingKeys = put $ if Set.null remainingKeys
136− then Map.delete v remainingValues
137− else Map.insert v remainingKeys remainingValues
138− case Map.lookup v remainingValues of
139− Nothing -> return $ NodeInfo (From_Insert v) $ Just undefined -- There's no existing value we can take
140− Just fromKs ->
141− if k `Set.member` fromKs
142− then do
143− putRemainingKeys $ Set.delete k fromKs
144− return $ NodeInfo (From_Move k) $ Just undefined -- There's an existing value, and it's here, so no patch necessary
145− else do
146− (fromK, remainingKeys) <- return . fromJust $ Set.minView fromKs -- There's an existing value, but it's not here; move it here
147− putRemainingKeys remainingKeys
148− return $ NodeInfo (From_Move fromK) $ Just undefined
149− Map.traverseWithKey f newByIndex
150− unusedOldKeys = fold unusedValuesByValue
151− pointlessMove k = \case
152− From_Move k' | k == k' -> True
153− _ -> False
154− keyWasMoved k = if k `Map.member` oldByIndex && not (k `Set.member` unusedOldKeys)
155− then Just undefined
156− else Nothing
157− 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
158−
159−-- | Change the 'From' value of a 'NodeInfo'
160−nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
161−nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
162−
163−-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
164−-- 'Applicative', 'Monad', etc.) action to get the new value
165−nodeInfoMapMFrom :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
166−nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
167−
168−-- | Set the 'To' field of a 'NodeInfo'
169−nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
170−nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
171−
172−-- |Helper data structure used for composing patches using the monoid instance.
173−data Fixup k v
174− = Fixup_Delete
175− | Fixup_Update (These (From k v) (To k))
176−
177−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
178−instance Ord k => Semigroup (PatchMapWithMove k v) where
179− PatchMapWithMove ma <> PatchMapWithMove mb = PatchMapWithMove m
180− where
181− connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
182− h :: (k, (Maybe k, From k v)) -> [(k, Fixup k v)]
183− h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
184− (Just toAfter, From_Move fromBefore)
185− | fromBefore == toAfter
186− -> [(toAfter, Fixup_Delete)]
187− | otherwise
188− -> [ (toAfter, Fixup_Update (This editBefore))
189− , (fromBefore, Fixup_Update (That mToAfter))
190− ]
191− (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
192− (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
193− (Nothing, _) -> []
194− mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
195− mergeFixups _ (Fixup_Update a) (Fixup_Update b)
196− | This x <- a, That y <- b
197− = Fixup_Update $ These x y
198− | That y <- a, This x <- b
199− = Fixup_Update $ These x y
200− mergeFixups _ _ _ = error "PatchMapWithMove: incompatible fixups"
201− fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
202− combineNodeInfos _ nia nib = NodeInfo
203− { _nodeInfo_from = _nodeInfo_from nia
204− , _nodeInfo_to = _nodeInfo_to nib
205− }
206− applyFixup _ ni = \case
207− Fixup_Delete -> Nothing
208− Fixup_Update u -> Just $ NodeInfo
209− { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
210− , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
211− }
212− m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
213− getHere :: These a b -> Maybe a
214− getHere = \case
215− This a -> Just a
216− These a _ -> Just a
217− That _ -> Nothing
218− getThere :: These a b -> Maybe b
219− getThere = \case
220− This _ -> Nothing
221− These _ b -> Just b
222− That b -> Just b
223−
224−--TODO: Figure out how to implement this in terms of PatchDMapWithMove rather than duplicating it here
225−-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
226−instance Ord k => Monoid (PatchMapWithMove k v) where
227− mempty = PatchMapWithMove mempty
228− mappend = (<>)
src/Reflex/PerformEvent/Base.hsdeleted−179
@@ -1,179 +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.Adjustable.Class
29−import Reflex.Host.Class
30−import Reflex.PerformEvent.Class
31−import Reflex.Requester.Base
32−import Reflex.Requester.Class
33−
34−import Control.Lens
35−import Control.Monad.Exception
36−import Control.Monad.Identity
37−import Control.Monad.Primitive
38−import Control.Monad.Reader
39−import Control.Monad.Ref
40−import Data.Coerce
41−import Data.Dependent.Map (DMap)
42−import qualified Data.Dependent.Map as DMap
43−import Data.Dependent.Sum
44−import Data.IntMap.Strict (IntMap)
45−import qualified Data.IntMap.Strict as IntMap
46−import qualified Data.Semigroup as S
47−
48−-- | A function that fires events for the given 'EventTrigger's and then runs
49−-- any followup actions provided via 'PerformEvent'. The given 'ReadPhase'
50−-- action will be run once for the initial trigger execution as well as once for
51−-- each followup.
52−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?
53−
54−-- | Provides a basic implementation of 'PerformEvent'. Note that, despite the
55−-- name, 'PerformEventT' is not an instance of 'MonadTrans'.
56−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequesterT t (HostFrame t) Identity (HostFrame t) a }
57−
58−deriving instance ReflexHost t => Functor (PerformEventT t m)
59−deriving instance ReflexHost t => Applicative (PerformEventT t m)
60−deriving instance ReflexHost t => Monad (PerformEventT t m)
61−deriving instance ReflexHost t => MonadFix (PerformEventT t m)
62−deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
63−deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
64−deriving instance (ReflexHost t, Monoid a) => Monoid (PerformEventT t m a)
65−deriving instance (ReflexHost t, S.Semigroup a) => S.Semigroup (PerformEventT t m a)
66−
67−instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
68− type PrimState (PerformEventT t m) = PrimState (HostFrame t)
69− primitive = PerformEventT . lift . primitive
70−
71−instance (ReflexHost t, Ref m ~ Ref IO) => PerformEvent t (PerformEventT t m) where
72− type Performable (PerformEventT t m) = HostFrame t
73− {-# INLINABLE performEvent_ #-}
74− performEvent_ = PerformEventT . requesting_
75− {-# INLINABLE performEvent #-}
76− performEvent = PerformEventT . requestingIdentity
77−
78−instance (ReflexHost t, PrimMonad (HostFrame t)) => Adjustable t (PerformEventT t m) where
79− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequesterTWith f (coerce outerA0) (coerceEvent outerA')
80− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequesterT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
81− f a0 a' = do
82− result0 <- lift a0
83− result' <- requestingIdentity a'
84− return (result0, result')
85− traverseIntMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseIntMapWithKeyWithAdjustRequesterTWith (defaultAdjustIntBase traverseIntMapPatchWithKey) patchIntMapNewElementsMap mergeIntIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
86− traverseDMapWithKeyWithAdjust f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithKey) mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
87− traverseDMapWithKeyWithAdjustWithMove f outerDm0 outerDm' = PerformEventT $ traverseDMapWithKeyWithAdjustRequesterTWith (defaultAdjustBase traversePatchDMapWithMoveWithKey) mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove (\k v -> unPerformEventT $ f k v) (coerce outerDm0) (coerceEvent outerDm')
88−
89−defaultAdjustBase :: forall t v v2 k' p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
90− => ((forall a. k' a -> v a -> HostFrame t (v2 a)) -> p k' v -> HostFrame t (p k' v2))
91− -> (forall a. k' a -> v a -> HostFrame t (v2 a))
92− -> DMap k' v
93− -> Event t (p k' v)
94− -> RequesterT t (HostFrame t) Identity (HostFrame t) (DMap k' v2, Event t (p k' v2))
95−defaultAdjustBase traversePatchWithKey f' dm0 dm' = do
96− result0 <- lift $ DMap.traverseWithKey f' dm0
97− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
98− return (result0, result')
99−
100−defaultAdjustIntBase :: forall t v v2 p. (Monad (HostFrame t), PrimMonad (HostFrame t), Reflex t)
101− => ((IntMap.Key -> v -> HostFrame t v2) -> p v -> HostFrame t (p v2))
102− -> (IntMap.Key -> v -> HostFrame t v2)
103− -> IntMap v
104− -> Event t (p v)
105− -> RequesterT t (HostFrame t) Identity (HostFrame t) (IntMap v2, Event t (p v2))
106−defaultAdjustIntBase traversePatchWithKey f' dm0 dm' = do
107− result0 <- lift $ IntMap.traverseWithKey f' dm0
108− result' <- requestingIdentity $ ffor dm' $ traversePatchWithKey f'
109− return (result0, result')
110−
111−instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
112− {-# INLINABLE newEventWithTrigger #-}
113− newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
114− {-# INLINABLE newFanEventWithTrigger #-}
115− newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
116−
117−-- | Run a 'PerformEventT' action, returning a 'FireCommand' that allows the
118−-- caller to trigger 'Event's while ensuring that 'performEvent' actions are run
119−-- at the appropriate time.
120−{-# INLINABLE hostPerformEventT #-}
121−hostPerformEventT :: forall t m a.
122− ( Monad m
123− , MonadSubscribeEvent t m
124− , MonadReflexHost t m
125− , MonadRef m
126− , Ref m ~ Ref IO
127− )
128− => PerformEventT t m a
129− -> m (a, FireCommand t m)
130−hostPerformEventT a = do
131− (response, responseTrigger) <- newEventWithTriggerRef
132− (result, eventToPerform) <- runHostFrame $ runRequesterT (unPerformEventT a) response
133− eventToPerformHandle <- subscribeEvent eventToPerform
134− return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
135− let go :: [DSum (EventTrigger t) Identity] -> m [a']
136− go ts = do
137− (result', mToPerform) <- fireEventsAndRead ts $ do
138− mToPerform <- sequence =<< readEvent eventToPerformHandle
139− result' <- readPhase
140− return (result', mToPerform)
141− case mToPerform of
142− Nothing -> return [result']
143− Just toPerform -> do
144− responses <- runHostFrame $ traverseRequesterData (\v -> Identity <$> v) toPerform
145− mrt <- readRef responseTrigger
146− let followupEventTriggers = case mrt of
147− Just rt -> [rt :=> Identity responses]
148− Nothing -> []
149− (result':) <$> go followupEventTriggers
150− go triggers
151−
152−instance ReflexHost t => MonadSample t (PerformEventT t m) where
153− {-# INLINABLE sample #-}
154− sample = PerformEventT . lift . sample
155−
156−instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
157− {-# INLINABLE hold #-}
158− hold v0 v' = PerformEventT $ lift $ hold v0 v'
159− {-# INLINABLE holdDyn #-}
160− holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
161− {-# INLINABLE holdIncremental #-}
162− holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
163− {-# INLINABLE buildDynamic #-}
164− buildDynamic getV0 v' = PerformEventT $ lift $ buildDynamic getV0 v'
165− {-# INLINABLE headE #-}
166− headE = PerformEventT . lift . headE
167−
168−instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
169− type Ref (PerformEventT t m) = Ref (HostFrame t)
170− {-# INLINABLE newRef #-}
171− newRef = PerformEventT . lift . newRef
172− {-# INLINABLE readRef #-}
173− readRef = PerformEventT . lift . readRef
174− {-# INLINABLE writeRef #-}
175− writeRef r = PerformEventT . lift . writeRef r
176−
177−instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
178− {-# INLINABLE atomicModifyRef #-}
179− 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−197
@@ -1,197 +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− -- * Internal
21− , mapIntMapWithAdjustImpl
22− , mapDMapWithAdjustImpl
23− ) where
24−
25−import Reflex.Class
26−import Reflex.Adjustable.Class
27−import Reflex.Host.Class
28−import Reflex.PerformEvent.Class
29−import Reflex.PostBuild.Class
30−import Reflex.TriggerEvent.Class
31−
32−import Control.Applicative (liftA2)
33−import Control.Monad.Exception
34−import Control.Monad.Identity
35−import Control.Monad.Primitive
36−import Control.Monad.Reader
37−import Control.Monad.Ref
38−import qualified Control.Monad.Trans.Control as TransControl
39−import Data.Dependent.Map (DMap)
40−import qualified Data.Dependent.Map as DMap
41−import Data.Functor.Compose
42−import Data.IntMap.Strict (IntMap)
43−import qualified Data.IntMap.Strict as IntMap
44−import qualified Data.Semigroup as S
45−
46−-- | Provides a basic implementation of 'PostBuild'.
47−newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
48−
49−-- | Run a 'PostBuildT' action. An 'Event' should be provided that fires
50−-- immediately after the action is finished running; no other 'Event's should
51−-- fire first.
52−{-# INLINABLE runPostBuildT #-}
53−runPostBuildT :: PostBuildT t m a -> Event t () -> m a
54−runPostBuildT (PostBuildT a) = runReaderT a
55−
56−-- TODO: Monoid and Semigroup can likely be derived once ReaderT has them.
57−instance (Monoid a, Applicative m) => Monoid (PostBuildT t m a) where
58− mempty = pure mempty
59− mappend = liftA2 mappend
60−
61−instance (S.Semigroup a, Applicative m) => S.Semigroup (PostBuildT t m a) where
62− (<>) = liftA2 (S.<>)
63−
64−instance PrimMonad m => PrimMonad (PostBuildT x m) where
65− type PrimState (PostBuildT x m) = PrimState m
66− primitive = lift . primitive
67−
68−instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
69− {-# INLINABLE getPostBuild #-}
70− getPostBuild = PostBuildT ask
71−
72−instance MonadSample t m => MonadSample t (PostBuildT t m) where
73− {-# INLINABLE sample #-}
74− sample = lift . sample
75−
76−instance MonadHold t m => MonadHold t (PostBuildT t m) where
77− {-# INLINABLE hold #-}
78− hold v0 = lift . hold v0
79− {-# INLINABLE holdDyn #-}
80− holdDyn v0 = lift . holdDyn v0
81− {-# INLINABLE holdIncremental #-}
82− holdIncremental v0 = lift . holdIncremental v0
83− {-# INLINABLE buildDynamic #-}
84− buildDynamic a0 = lift . buildDynamic a0
85− {-# INLINABLE headE #-}
86− headE = lift . headE
87−
88−instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
89− type Performable (PostBuildT t m) = Performable m
90− {-# INLINABLE performEvent_ #-}
91− performEvent_ = lift . performEvent_
92− {-# INLINABLE performEvent #-}
93− performEvent = lift . performEvent
94−
95−instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
96− {-# INLINABLE newEventWithTrigger #-}
97− newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
98− {-# INLINABLE newFanEventWithTrigger #-}
99− newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
100−
101−instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
102− {-# INLINABLE newTriggerEvent #-}
103− newTriggerEvent = lift newTriggerEvent
104− {-# INLINABLE newTriggerEventWithOnComplete #-}
105− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
106− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
107−
108−instance MonadRef m => MonadRef (PostBuildT t m) where
109− type Ref (PostBuildT t m) = Ref m
110− {-# INLINABLE newRef #-}
111− newRef = lift . newRef
112− {-# INLINABLE readRef #-}
113− readRef = lift . readRef
114− {-# INLINABLE writeRef #-}
115− writeRef r = lift . writeRef r
116−
117−instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
118− {-# INLINABLE atomicModifyRef #-}
119− atomicModifyRef r = lift . atomicModifyRef r
120−
121−instance (Reflex t, MonadHold t m, MonadFix m, Adjustable t m, PerformEvent t m) => Adjustable t (PostBuildT t m) where
122− runWithReplace a0 a' = do
123− postBuild <- getPostBuild
124− lift $ do
125− rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
126− let voidResult' = fmapCheap (\_ -> ()) result'
127− return result
128− {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
129− traverseIntMapWithKeyWithAdjust = mapIntMapWithAdjustImpl traverseIntMapWithKeyWithAdjust
130− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
131− traverseDMapWithKeyWithAdjust = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjust mapPatchDMap
132− traverseDMapWithKeyWithAdjustWithMove = mapDMapWithAdjustImpl traverseDMapWithKeyWithAdjustWithMove mapPatchDMapWithMove
133−
134−{-# INLINABLE mapIntMapWithAdjustImpl #-}
135−mapIntMapWithAdjustImpl :: forall t m v v' p. (Reflex t, MonadFix m, MonadHold t m, Functor p)
136− => ( (IntMap.Key -> (Event t (), v) -> m v')
137− -> IntMap (Event t (), v)
138− -> Event t (p (Event t (), v))
139− -> m (IntMap v', Event t (p v'))
140− )
141− -> (IntMap.Key -> v -> PostBuildT t m v')
142− -> IntMap v
143− -> Event t (p v)
144− -> PostBuildT t m (IntMap v', Event t (p v'))
145−mapIntMapWithAdjustImpl base f dm0 dm' = do
146− postBuild <- getPostBuild
147− let loweredDm0 = fmap ((,) postBuild) dm0
148− f' :: IntMap.Key -> (Event t (), v) -> m v'
149− f' k (e, v) = do
150− runPostBuildT (f k v) e
151− lift $ do
152− rec (result0, result') <- base f' loweredDm0 loweredDm'
153− cohortDone <- numberOccurrencesFrom_ 1 result'
154− numberedDm' <- numberOccurrencesFrom 1 dm'
155− let postBuild' = fanInt $ fmapCheap (`IntMap.singleton` ()) cohortDone
156− loweredDm' = flip pushAlways numberedDm' $ \(n, p) -> do
157− return $ fmap ((,) (selectInt postBuild' n)) p
158− return (result0, result')
159−
160−{-# INLINABLE mapDMapWithAdjustImpl #-}
161−mapDMapWithAdjustImpl :: forall t m k v v' p. (Reflex t, MonadFix m, MonadHold t m)
162− => ( (forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a))
163− -> DMap k (Compose ((,) (Bool, Event t ())) v)
164− -> Event t (p k (Compose ((,) (Bool, Event t ())) v))
165− -> m (DMap k v', Event t (p k v'))
166− )
167− -> ((forall a. v a -> Compose ((,) (Bool, Event t ())) v a) -> p k v -> p k (Compose ((,) (Bool, Event t ())) v))
168− -> (forall a. k a -> v a -> PostBuildT t m (v' a))
169− -> DMap k v
170− -> Event t (p k v)
171− -> PostBuildT t m (DMap k v', Event t (p k v'))
172−mapDMapWithAdjustImpl base mapPatch f dm0 dm' = do
173− postBuild <- getPostBuild
174− let loweredDm0 = DMap.map (Compose . (,) (False, postBuild)) dm0
175− f' :: forall a. k a -> Compose ((,) (Bool, Event t ())) v a -> m (v' a)
176− f' k (Compose ((shouldHeadE, e), v)) = do
177− eOnce <- if shouldHeadE
178− 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
179− else return e
180− runPostBuildT (f k v) eOnce
181− lift $ do
182− rec (result0, result') <- base f' loweredDm0 loweredDm'
183− let voidResult' = fmapCheap (\_ -> ()) result'
184− let loweredDm' = ffor dm' $ mapPatch (Compose . (,) (True, voidResult'))
185− return (result0, result')
186−
187−--------------------------------------------------------------------------------
188−-- Deprecated functionality
189−--------------------------------------------------------------------------------
190−
191−-- | Deprecated
192−instance TransControl.MonadTransControl (PostBuildT t) where
193− type StT (PostBuildT t) a = TransControl.StT (ReaderT (Event t ())) a
194− {-# INLINABLE liftWith #-}
195− liftWith = TransControl.defaultLiftWith PostBuildT unPostBuildT
196− {-# INLINABLE restoreT #-}
197− restoreT = TransControl.defaultRestoreT PostBuildT
src/Reflex/PostBuild/Class.hsdeleted−42
@@ -1,42 +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−import Control.Monad.State
23−import qualified Control.Monad.State.Strict as Strict
24−
25−-- | 'PostBuild' represents an action that is notified via an 'Event' when it
26−-- has finished executing. Note that the specific definition of "finished" is
27−-- determined by the instance of 'PostBuild', but the intent is to allow
28−-- 'Behavior's and 'Dynamic's to be safely sampled, regardless of where they
29−-- were created, when the post-build 'Event' fires. The post-build 'Event' will
30−-- fire exactly once for an given action.
31−class (Reflex t, Monad m) => PostBuild t m | m -> t where
32− -- | Retrieve the post-build 'Event' for this action.
33− getPostBuild :: m (Event t ())
34−
35−instance PostBuild t m => PostBuild t (ReaderT r m) where
36− getPostBuild = lift getPostBuild
37−
38−instance PostBuild t m => PostBuild t (StateT s m) where
39− getPostBuild = lift getPostBuild
40−
41−instance PostBuild t m => PostBuild t (Strict.StateT s m) where
42− getPostBuild = lift getPostBuild
src/Reflex/Profiled.hsdeleted−213
@@ -1,213 +0,0 @@
1−{-# LANGUAGE EmptyDataDecls #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE InstanceSigs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE StandaloneDeriving #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−module Reflex.Profiled where
12−
13−import Control.Lens hiding (children)
14−import Control.Monad
15−import Control.Monad.Exception
16−import Control.Monad.Fix
17−import Control.Monad.Primitive
18−import Control.Monad.Reader
19−import Control.Monad.Ref
20−import Control.Monad.State.Strict (StateT, execStateT, modify)
21−import Data.Coerce
22−import Data.Dependent.Map (DMap, GCompare)
23−import Data.FastMutableIntMap
24−import Data.IORef
25−import Data.List
26−import Data.Map (Map)
27−import qualified Data.Map.Strict as Map
28−import Data.Monoid ((<>))
29−import Data.Ord
30−import qualified Data.Semigroup as S
31−import Data.Type.Coercion
32−import Foreign.Ptr
33−import GHC.Foreign
34−import GHC.IO.Encoding
35−import GHC.Stack
36−import Reflex.Class
37−import Reflex.Host.Class
38−import Reflex.PerformEvent.Class
39−
40−import System.IO.Unsafe
41−import Unsafe.Coerce
42−
43−data ProfiledTimeline t
44−
45−{-# NOINLINE profilingData #-}
46−profilingData :: IORef (Map (Ptr CostCentreStack) Int)
47−profilingData = unsafePerformIO $ newIORef Map.empty
48−
49−data CostCentreTree = CostCentreTree
50− { _costCentreTree_ownEntries :: !Int
51− , _costCentreTree_cumulativeEntries :: !Int
52− , _costCentreTree_children :: !(Map (Ptr CostCentre) CostCentreTree)
53− }
54− deriving (Show, Eq, Ord)
55−
56−instance S.Semigroup CostCentreTree where
57− (CostCentreTree oa ea ca) <> (CostCentreTree ob eb cb) =
58− CostCentreTree (oa + ob) (ea + eb) $ Map.unionWith (S.<>) ca cb
59−
60−instance Monoid CostCentreTree where
61− mempty = CostCentreTree 0 0 mempty
62− mappend = (S.<>)
63−
64−getCostCentreStack :: Ptr CostCentreStack -> IO [Ptr CostCentre]
65−getCostCentreStack = go []
66− where go l ccs = if ccs == nullPtr
67− then return l
68− else do
69− cc <- ccsCC ccs
70− parent <- ccsParent ccs
71− go (cc : l) parent
72−
73−toCostCentreTree :: Ptr CostCentreStack -> Int -> IO CostCentreTree
74−toCostCentreTree ccs n = do
75− ccList <- getCostCentreStack ccs
76− return $ foldr (\cc child -> CostCentreTree 0 n $ Map.singleton cc child) (CostCentreTree n n mempty) ccList
77−
78−getCostCentreTree :: IO CostCentreTree
79−getCostCentreTree = do
80− vals <- readIORef profilingData
81− mconcat <$> mapM (uncurry toCostCentreTree) (Map.toList vals)
82−
83−formatCostCentreTree :: CostCentreTree -> IO String
84−formatCostCentreTree cct0 = unlines . reverse <$> execStateT (go 0 cct0) []
85− where go :: Int -> CostCentreTree -> StateT [String] IO ()
86− go depth cct = do
87− let children = sortOn (Down . _costCentreTree_cumulativeEntries . snd) $ Map.toList $ _costCentreTree_children cct
88− indent = mconcat $ replicate depth " "
89− forM_ children $ \(cc, childCct) -> do
90− lbl <- liftIO $ peekCString utf8 =<< ccLabel cc
91− mdl <- liftIO $ peekCString utf8 =<< ccModule cc
92− loc <- liftIO $ peekCString utf8 =<< ccSrcSpan cc
93− let description = mdl <> "." <> lbl <> " (" <> loc <> ")"
94− modify $ (:) $ indent <> description <> "\t" <> show (_costCentreTree_cumulativeEntries childCct) <> "\t" <> show (_costCentreTree_ownEntries childCct)
95− go (succ depth) childCct
96−
97−showProfilingData :: IO ()
98−showProfilingData = do
99− putStr =<< formatCostCentreTree =<< getCostCentreTree
100−
101−writeProfilingData :: FilePath -> IO ()
102−writeProfilingData p = do
103− writeFile p =<< formatCostCentreTree =<< getCostCentreTree
104−
105−newtype ProfiledM m a = ProfiledM { runProfiledM :: m a }
106− deriving (Functor, Applicative, Monad, MonadFix, MonadException, MonadAsyncException)
107−
108−profileEvent :: Reflex t => Event t a -> Event t a
109−profileEvent e = unsafePerformIO $ do
110− stack <- getCurrentCCS e
111− let f x = unsafePerformIO $ do
112− modifyIORef' profilingData $ Map.insertWith (+) stack 1
113− return $ return $ Just x
114− return $ pushCheap f e
115−
116−--TODO: Instead of profiling just the input or output of each one, profile all the inputs and all the outputs
117−
118−instance Reflex t => Reflex (ProfiledTimeline t) where
119− newtype Behavior (ProfiledTimeline t) a = Behavior_Profiled { unBehavior_Profiled :: Behavior t a }
120− newtype Event (ProfiledTimeline t) a = Event_Profiled { unEvent_Profiled :: Event t a }
121− newtype Dynamic (ProfiledTimeline t) a = Dynamic_Profiled { unDynamic_Profiled :: Dynamic t a }
122− newtype Incremental (ProfiledTimeline t) p = Incremental_Profiled { unIncremental_Profiled :: Incremental t p }
123− type PushM (ProfiledTimeline t) = ProfiledM (PushM t)
124− type PullM (ProfiledTimeline t) = ProfiledM (PullM t)
125− never = Event_Profiled never
126− constant = Behavior_Profiled . constant
127− push f (Event_Profiled e) = coerce $ push (coerce f) $ profileEvent e -- Profile before rather than after; this way fanout won't count against us
128− pushCheap f (Event_Profiled e) = coerce $ pushCheap (coerce f) $ profileEvent e
129− pull = Behavior_Profiled . pull . coerce
130− merge :: forall k. GCompare k => DMap k (Event (ProfiledTimeline t)) -> Event (ProfiledTimeline t) (DMap k Identity)
131− merge = Event_Profiled . merge . (unsafeCoerce :: DMap k (Event (ProfiledTimeline t)) -> DMap k (Event t))
132− fan (Event_Profiled e) = EventSelector $ coerce $ select (fan $ profileEvent e)
133− switch (Behavior_Profiled b) = coerce $ profileEvent $ switch (coerceBehavior b)
134− coincidence (Event_Profiled e) = coerce $ profileEvent $ coincidence (coerceEvent e)
135− current (Dynamic_Profiled d) = coerce $ current d
136− updated (Dynamic_Profiled d) = coerce $ profileEvent $ updated d
137− unsafeBuildDynamic (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildDynamic a0 a'
138− unsafeBuildIncremental (ProfiledM a0) (Event_Profiled a') = coerce $ unsafeBuildIncremental a0 a'
139− mergeIncremental = Event_Profiled . mergeIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMap k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMap k (Event t)))
140− mergeIncrementalWithMove = Event_Profiled . mergeIncrementalWithMove . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchDMapWithMove k (Event (ProfiledTimeline t))) -> Incremental t (PatchDMapWithMove k (Event t)))
141− currentIncremental (Incremental_Profiled i) = coerce $ currentIncremental i
142− updatedIncremental (Incremental_Profiled i) = coerce $ profileEvent $ updatedIncremental i
143− incrementalToDynamic (Incremental_Profiled i) = coerce $ incrementalToDynamic i
144− behaviorCoercion (c :: Coercion a b) = case behaviorCoercion c :: Coercion (Behavior t a) (Behavior t b) of
145− Coercion -> unsafeCoerce (Coercion :: Coercion (Behavior (ProfiledTimeline t) a) (Behavior (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
146− eventCoercion (c :: Coercion a b) = case eventCoercion c :: Coercion (Event t a) (Event t b) of
147− Coercion -> unsafeCoerce (Coercion :: Coercion (Event (ProfiledTimeline t) a) (Event (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
148− dynamicCoercion (c :: Coercion a b) = case dynamicCoercion c :: Coercion (Dynamic t a) (Dynamic t b) of
149− Coercion -> unsafeCoerce (Coercion :: Coercion (Dynamic (ProfiledTimeline t) a) (Dynamic (ProfiledTimeline t) a)) --TODO: Figure out how to make this typecheck without the unsafeCoerce
150− mergeIntIncremental = Event_Profiled . mergeIntIncremental . (unsafeCoerce :: Incremental (ProfiledTimeline t) (PatchIntMap (Event (ProfiledTimeline t) a)) -> Incremental t (PatchIntMap (Event t a)))
151− fanInt (Event_Profiled e) = coerce $ fanInt $ profileEvent e
152−
153−deriving instance Functor (Dynamic t) => Functor (Dynamic (ProfiledTimeline t))
154−deriving instance Applicative (Dynamic t) => Applicative (Dynamic (ProfiledTimeline t))
155−deriving instance Monad (Dynamic t) => Monad (Dynamic (ProfiledTimeline t))
156−
157−instance MonadHold t m => MonadHold (ProfiledTimeline t) (ProfiledM m) where
158− hold v0 (Event_Profiled v') = ProfiledM $ Behavior_Profiled <$> hold v0 v'
159− holdDyn v0 (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> holdDyn v0 v'
160− holdIncremental v0 (Event_Profiled v') = ProfiledM $ Incremental_Profiled <$> holdIncremental v0 v'
161− buildDynamic (ProfiledM v0) (Event_Profiled v') = ProfiledM $ Dynamic_Profiled <$> buildDynamic v0 v'
162− headE (Event_Profiled e) = ProfiledM $ Event_Profiled <$> headE e
163−
164−instance MonadSample t m => MonadSample (ProfiledTimeline t) (ProfiledM m) where
165− sample (Behavior_Profiled b) = ProfiledM $ sample b
166−
167−instance MonadTrans ProfiledM where
168− lift = ProfiledM
169−
170−instance MonadIO m => MonadIO (ProfiledM m) where
171− liftIO = lift . liftIO
172−
173−instance PerformEvent t m => PerformEvent (ProfiledTimeline t) (ProfiledM m) where
174− type Performable (ProfiledM m) = Performable m
175− performEvent_ = lift . performEvent_ . coerce
176− performEvent = lift . fmap coerce . performEvent . coerce
177−
178−instance MonadRef m => MonadRef (ProfiledM m) where
179− type Ref (ProfiledM m) = Ref m
180− newRef = lift . newRef
181− readRef = lift . readRef
182− writeRef r = lift . writeRef r
183−
184−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger (ProfiledTimeline t) (ProfiledM m) where
185− newEventWithTrigger = lift . fmap coerce . newEventWithTrigger
186− newFanEventWithTrigger f = do
187− es <- lift $ newFanEventWithTrigger f
188− return $ EventSelector $ \k -> coerce $ select es k
189−
190−instance MonadReader r m => MonadReader r (ProfiledM m) where
191− ask = lift ask
192− local f (ProfiledM a) = ProfiledM $ local f a
193− reader = lift . reader
194−
195−instance ReflexHost t => ReflexHost (ProfiledTimeline t) where
196− type EventTrigger (ProfiledTimeline t) = EventTrigger t
197− type EventHandle (ProfiledTimeline t) = EventHandle t
198− type HostFrame (ProfiledTimeline t) = ProfiledM (HostFrame t)
199−
200−instance MonadSubscribeEvent t m => MonadSubscribeEvent (ProfiledTimeline t) (ProfiledM m) where
201− subscribeEvent = lift . subscribeEvent . coerce
202−
203−instance PrimMonad m => PrimMonad (ProfiledM m) where
204− type PrimState (ProfiledM m) = PrimState m
205− primitive = lift . primitive
206−
207−instance MonadReflexHost t m => MonadReflexHost (ProfiledTimeline t) (ProfiledM m) where
208− type ReadPhase (ProfiledM m) = ProfiledM (ReadPhase m)
209− fireEventsAndRead ts r = lift $ fireEventsAndRead ts $ coerce r
210− runHostFrame = lift . runHostFrame . coerce
211−
212−instance MonadReadEvent t m => MonadReadEvent (ProfiledTimeline t) (ProfiledM m) where
213− readEvent = lift . fmap coerce . readEvent
src/Reflex/Pure.hsdeleted−206
@@ -1,206 +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.IntMap (IntMap)
38−import qualified Data.IntMap as IntMap
39−import Data.Maybe
40−import Data.MemoTrie
41−import Data.Monoid
42−import Data.Type.Coercion
43−import Reflex.Class
44−
45−-- | A completely pure-functional 'Reflex' timeline, identifying moments in time
46−-- with the type @t@.
47−data Pure t
48−
49−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
50−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
51−-- exclusively to memoize functions of t, not for any of its other capabilities.
52−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
53−
54− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
55− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
56− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
57− newtype Incremental (Pure t) p = Incremental { unIncremental :: t -> (PatchTarget p, Maybe p) }
58−
59− type PushM (Pure t) = (->) t
60− type PullM (Pure t) = (->) t
61−
62− never :: Event (Pure t) a
63− never = Event $ \_ -> Nothing
64−
65− constant :: a -> Behavior (Pure t) a
66− constant x = Behavior $ \_ -> x
67−
68− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
69− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
70−
71− pushCheap :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
72− pushCheap = push
73−
74− pull :: PullM (Pure t) a -> Behavior (Pure t) a
75− pull = Behavior . memo
76−
77− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
78− -- warning because the GCompare instance is not used; however, removing the
79− -- GCompare instance produces a different warning, due to that constraint
80− -- being present in the original class definition
81−
82− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
83− merge events = Event $ memo $ \t ->
84− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
85− in if DMap.null currentOccurrences
86− then Nothing
87− else Just currentOccurrences
88−
89− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
90− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
91−
92− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
93− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
94−
95− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
96− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
97−
98− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
99− current d = Behavior $ \t -> fst $ unDynamic d t
100−
101− updated :: Dynamic (Pure t) a -> Event (Pure t) a
102− updated d = Event $ \t -> snd $ unDynamic d t
103−
104− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
105− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
106−
107− -- See UNUSED_CONSTRAINT, above.
108−
109− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
110− --a) -> Incremental (Pure t) p a
111− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
112−
113− mergeIncremental = mergeIncrementalImpl
114− mergeIncrementalWithMove = mergeIncrementalImpl
115−
116− currentIncremental i = Behavior $ \t -> fst $ unIncremental i t
117−
118− updatedIncremental i = Event $ \t -> snd $ unIncremental i t
119−
120− incrementalToDynamic i = Dynamic $ \t ->
121− let (old, mPatch) = unIncremental i t
122− e = case mPatch of
123− Nothing -> Nothing
124− Just patch -> apply patch old
125− in (old, e)
126− behaviorCoercion Coercion = Coercion
127− eventCoercion Coercion = Coercion
128− dynamicCoercion Coercion = Coercion
129−
130− fanInt e = EventSelectorInt $ \k -> Event $ \t -> unEvent e t >>= IntMap.lookup k
131−
132− mergeIntIncremental = mergeIntIncrementalImpl
133−
134−mergeIncrementalImpl :: (PatchTarget p ~ DMap k (Event (Pure t)), GCompare k) => Incremental (Pure t) p -> Event (Pure t) (DMap k Identity)
135−mergeIncrementalImpl i = Event $ \t ->
136− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ unIncremental i t
137− in if DMap.null results
138− then Nothing
139− else Just results
140−
141−mergeIntIncrementalImpl :: (PatchTarget p ~ IntMap (Event (Pure t) a)) => Incremental (Pure t) p -> Event (Pure t) (IntMap a)
142−mergeIntIncrementalImpl i = Event $ \t ->
143− let results = IntMap.mapMaybeWithKey (\_ (Event e) -> e t) $ fst $ unIncremental i t
144− in if IntMap.null results
145− then Nothing
146− else Just results
147−
148−instance Functor (Dynamic (Pure t)) where
149− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
150− in (f cur, fmap f upd)
151−
152−instance Applicative (Dynamic (Pure t)) where
153− pure a = Dynamic $ \_ -> (a, Nothing)
154− (<*>) = ap
155−
156−instance Monad (Dynamic (Pure t)) where
157− return = pure
158− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
159− let (curX :: a, updX :: Maybe a) = unDynamic x t
160− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
161− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
162− Nothing -> (Nothing, Nothing)
163− Just nextX -> let (c, u) = unDynamic (f nextX) t
164− in (Just c, u)
165− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
166−
167−instance MonadSample (Pure t) ((->) t) where
168−
169− sample :: Behavior (Pure t) a -> (t -> a)
170− sample = unBehavior
171−
172−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
173−
174− hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
175− hold initialValue e initialTime = Behavior f
176− where f = memo $ \sampleTime ->
177− -- Really, the sampleTime should never be prior to the initialTime,
178− -- because that would mean the Behavior is being sampled before
179− -- being created.
180− if sampleTime <= initialTime
181− then initialValue
182− else let lastTime = pred sampleTime
183− in fromMaybe (f lastTime) $ unEvent e lastTime
184−
185− holdDyn v0 = buildDynamic (return v0)
186−
187− buildDynamic :: (t -> a) -> Event (Pure t) a -> t -> Dynamic (Pure t) a
188− buildDynamic initialValue e initialTime =
189− let Behavior f = hold (initialValue initialTime) e initialTime
190− in Dynamic $ \t -> (f t, unEvent e t)
191−
192− holdIncremental :: Patch p => PatchTarget p -> Event (Pure t) p -> t -> Incremental (Pure t) p
193− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
194− where f = memo $ \sampleTime ->
195− -- Really, the sampleTime should never be prior to the initialTime,
196− -- because that would mean the Behavior is being sampled before
197− -- being created.
198− if sampleTime <= initialTime
199− then initialValue
200− else let lastTime = pred sampleTime
201− lastValue = f lastTime
202− in case unEvent e lastTime of
203− Nothing -> lastValue
204− Just x -> fromMaybe lastValue $ apply x lastValue
205−
206− headE = slowHeadE
src/Reflex/Query/Base.hsdeleted−339
@@ -1,339 +0,0 @@
1−{-# LANGUAGE FlexibleInstances #-}
2−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3−{-# LANGUAGE InstanceSigs #-}
4−{-# LANGUAGE KindSignatures #-}
5−{-# LANGUAGE LambdaCase #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TypeFamilies #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−{-# LANGUAGE StandaloneDeriving #-}
12−
13−module Reflex.Query.Base
14− ( QueryT (..)
15− , runQueryT
16− , mapQuery
17− , mapQueryResult
18− , dynWithQueryT
19− , withQueryT
20− ) where
21−
22−import Control.Applicative (liftA2)
23−import Control.Monad.Exception
24−import Control.Monad.Fix
25−import Control.Monad.Reader
26−import Control.Monad.Ref
27−import Control.Monad.State.Strict
28−import Data.Align
29−import Data.Dependent.Map (DMap, DSum (..))
30−import qualified Data.Dependent.Map as DMap
31−import Data.Foldable
32−import Data.Functor.Compose
33−import Data.Functor.Misc
34−import Data.IntMap (IntMap)
35−import qualified Data.IntMap as IntMap
36−import Data.Map (Map)
37−import qualified Data.Map as Map
38−import Data.Monoid ((<>))
39−import qualified Data.Semigroup as S
40−import Data.Some (Some)
41−import qualified Data.Some as Some
42−import Data.These
43−
44−import Reflex.Class
45−import Reflex.Adjustable.Class
46−import Reflex.DynamicWriter.Class
47−import Reflex.EventWriter.Base
48−import Reflex.EventWriter.Class
49−import Reflex.Host.Class
50−import qualified Reflex.Patch.MapWithMove as MapWithMove
51−import Reflex.PerformEvent.Class
52−import Reflex.PostBuild.Class
53−import Reflex.Query.Class
54−import Reflex.Requester.Class
55−import Reflex.TriggerEvent.Class
56−
57−newtype QueryT t q m a = QueryT { unQueryT :: StateT [Behavior t q] (EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m)) a }
58− deriving (Functor, Applicative, Monad, MonadException, MonadFix, MonadIO, MonadAtomicRef)
59−
60−deriving instance MonadHold t m => MonadHold t (QueryT t q m)
61−deriving instance MonadSample t m => MonadSample t (QueryT t q m)
62−
63−runQueryT :: (MonadFix m, Additive q, Group q, Reflex t) => QueryT t q m a -> Dynamic t (QueryResult q) -> m (a, Incremental t (AdditivePatch q))
64−runQueryT (QueryT a) qr = do
65− ((r, bs), es) <- runReaderT (runEventWriterT (runStateT a mempty)) qr
66− return (r, unsafeBuildIncremental (foldlM (\b c -> (b <>) <$> sample c) mempty bs) (fmapCheap AdditivePatch es))
67−
68−newtype QueryTLoweredResult t q v = QueryTLoweredResult (v, [Behavior t q])
69−
70−getQueryTLoweredResultValue :: QueryTLoweredResult t q v -> v
71−getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
72−
73−getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
74−getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
75−
76−{-
77−let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
78− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
79− bs' = fmapCheap snd $ r'
80− patches = unsafeBuildIncremental (sampleBs bs0) $
81− flip pushCheap bs' $ \bs -> do
82− p <- (~~) <$> sampleBs bs <*> sample (currentIncremental patches)
83− return (Just (AdditivePatch p))
84−-}
85−
86−instance (Reflex t, MonadFix m, Group q, Additive q, Query q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
87− runWithReplace (QueryT a0) a' = do
88− ((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
89− let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
90− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
91− bs' = fmapCheap snd $ r'
92− bbs <- hold bs0 bs'
93− let patches = flip pushAlwaysCheap bs' $ \newBs -> do
94− oldBs <- sample bbs
95− (~~) <$> sampleBs newBs <*> sampleBs oldBs
96− QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
97− QueryT $ lift $ tellEvent patches
98− return (r0, fmapCheap fst r')
99−
100− traverseIntMapWithKeyWithAdjust :: forall v v'. (IntMap.Key -> v -> QueryT t q m v') -> IntMap v -> Event t (PatchIntMap v) -> QueryT t q m (IntMap v', Event t (PatchIntMap v'))
101− traverseIntMapWithKeyWithAdjust f im0 im' = do
102− let f' :: IntMap.Key -> v -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (QueryTLoweredResult t q v')
103− f' k v = fmap QueryTLoweredResult $ flip runStateT [] $ unQueryT $ f k v
104− (result0, result') <- QueryT $ lift $ traverseIntMapWithKeyWithAdjust f' im0 im'
105− let liftedResult0 = IntMap.map getQueryTLoweredResultValue result0
106− liftedResult' = fforCheap result' $ \(PatchIntMap p) -> PatchIntMap $
107− IntMap.map (fmap getQueryTLoweredResultValue) p
108− liftedBs0 :: IntMap [Behavior t q]
109− liftedBs0 = IntMap.map getQueryTLoweredResultWritten result0
110− liftedBs' :: Event t (PatchIntMap [Behavior t q])
111− liftedBs' = fforCheap result' $ \(PatchIntMap p) -> PatchIntMap $
112− IntMap.map (fmap getQueryTLoweredResultWritten) p
113− sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
114− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
115− accumBehaviors :: forall m'. MonadHold t m'
116− => IntMap [Behavior t q]
117− -> PatchIntMap [Behavior t q]
118− -> m' ( Maybe (IntMap [Behavior t q])
119− , Maybe (AdditivePatch q))
120− -- f accumulates the child behavior state we receive from running traverseIntMapWithKeyWithAdjust for the underlying monad.
121− -- When an update occurs, it also computes a patch to communicate to the parent QueryT state.
122− -- bs0 is a Map denoting the behaviors of the current children.
123− -- pbs is a PatchMap denoting an update to the behaviors of the current children
124− accumBehaviors bs0 pbs@(PatchIntMap bs') = do
125− let p k bs = case IntMap.lookup k bs0 of
126− Nothing -> case bs of
127− -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
128− Nothing -> return mempty
129− -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
130− Just newBs -> sampleBs newBs
131− Just oldBs -> case bs of
132− -- 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
133− Nothing -> negateG <$> sampleBs oldBs
134− -- 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
135− -- composed with the sampling the child's new state.
136− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
137− -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
138− -- child patches and wrap them in AdditivePatch.
139− patch <- AdditivePatch . fold <$> IntMap.traverseWithKey p bs'
140− return (apply pbs bs0, Just patch)
141− (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
142− tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
143− return (liftedResult0, liftedResult')
144−
145− 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'))
146− traverseDMapWithKeyWithAdjust f dm0 dm' = do
147− let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
148− f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
149− (result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjust f' dm0 dm'
150− let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
151− liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
152− mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
153− liftedBs0 :: Map (Some k) [Behavior t q]
154− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
155− liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
156− liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
157− Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
158− sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
159− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
160− accumBehaviors :: forall m'. MonadHold t m'
161− => Map (Some k) [Behavior t q]
162− -> PatchMap (Some k) [Behavior t q]
163− -> m' ( Maybe (Map (Some k) [Behavior t q])
164− , Maybe (AdditivePatch q))
165− -- f accumulates the child behavior state we receive from running traverseDMapWithKeyWithAdjust for the underlying monad.
166− -- When an update occurs, it also computes a patch to communicate to the parent QueryT state.
167− -- bs0 is a Map denoting the behaviors of the current children.
168− -- pbs is a PatchMap denoting an update to the behaviors of the current children
169− accumBehaviors bs0 pbs@(PatchMap bs') = do
170− let p k bs = case Map.lookup k bs0 of
171− Nothing -> case bs of
172− -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
173− Nothing -> return mempty
174− -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
175− Just newBs -> sampleBs newBs
176− Just oldBs -> case bs of
177− -- 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
178− Nothing -> negateG <$> sampleBs oldBs
179− -- 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
180− -- composed with the sampling the child's new state.
181− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
182− -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
183− -- child patches and wrap them in AdditivePatch.
184− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
185− return (apply pbs bs0, Just patch)
186− (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
187− tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
188− return (liftedResult0, liftedResult')
189−
190− 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'))
191− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
192− let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
193− f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
194− (result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjustWithMove f' dm0 dm'
195− let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
196− liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
197− liftedBs0 :: Map (Some k) [Behavior t q]
198− liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
199− liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
200− liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose) {- \(PatchDMap p) -> PatchMapWithMove $
201− Map.fromDistinctAscList $ (\(k :=> mr) -> (Some.This k, fmap (fmap (getQueryTLoweredResultWritten . getCompose)) mr)) <$> DMap.toList p -}
202− sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
203− sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
204− accumBehaviors' :: forall m'. MonadHold t m'
205− => Map (Some k) [Behavior t q]
206− -> PatchMapWithMove (Some k) [Behavior t q]
207− -> m' ( Maybe (Map (Some k) [Behavior t q])
208− , Maybe (AdditivePatch q))
209− -- f accumulates the child behavior state we receive from running traverseDMapWithKeyWithAdjustWithMove for the underlying monad.
210− -- When an update occurs, it also computes a patch to communicate to the parent QueryT state.
211− -- bs0 is a Map denoting the behaviors of the current children.
212− -- pbs is a PatchMapWithMove denoting an update to the behaviors of the current children
213− accumBehaviors' bs0 pbs = do
214− let bs' = unPatchMapWithMove pbs
215− p k bs = case Map.lookup k bs0 of
216− Nothing -> case MapWithMove._nodeInfo_from bs of
217− -- If the update is to delete the state for a child that doesn't exist, the patch is mempty.
218− MapWithMove.From_Delete -> return mempty
219− -- If the update is to update the state for a child that doesn't exist, the patch is the sample of the new state.
220− MapWithMove.From_Insert newBs -> sampleBs newBs
221− MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
222− Nothing -> return mempty
223− Just newBs -> sampleBs newBs
224− Just oldBs -> case MapWithMove._nodeInfo_from bs of
225− -- 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
226− MapWithMove.From_Delete -> negateG <$> sampleBs oldBs
227− -- 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
228− -- composed with the sampling the child's new state.
229− MapWithMove.From_Insert newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
230− MapWithMove.From_Move k'
231− | k' == k -> return mempty
232− | otherwise -> case Map.lookup k' bs0 of
233− -- If we are moving from a non-existent key, that is a delete
234− Nothing -> negateG <$> sampleBs oldBs
235− Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
236− -- we compute the patch by iterating over the update PatchMap and proceeding by cases. Then we fold over the
237− -- child patches and wrap them in AdditivePatch.
238− patch <- AdditivePatch . fold <$> Map.traverseWithKey p bs'
239− return (apply pbs bs0, Just patch)
240− (qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
241− tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
242− return (liftedResult0, liftedResult')
243−
244−instance MonadTrans (QueryT t q) where
245− lift = QueryT . lift . lift . lift
246−
247−instance PostBuild t m => PostBuild t (QueryT t q m) where
248− getPostBuild = lift getPostBuild
249−
250−instance (MonadAsyncException m) => MonadAsyncException (QueryT t q m) where
251− mask f = QueryT $ mask $ \unMask -> unQueryT $ f $ QueryT . unMask . unQueryT
252−
253−instance TriggerEvent t m => TriggerEvent t (QueryT t q m) where
254− newTriggerEvent = lift newTriggerEvent
255− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
256− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
257−
258−instance PerformEvent t m => PerformEvent t (QueryT t q m) where
259− type Performable (QueryT t q m) = Performable m
260− performEvent_ = lift . performEvent_
261− performEvent = lift . performEvent
262−
263−instance MonadRef m => MonadRef (QueryT t q m) where
264− type Ref (QueryT t q m) = Ref m
265− newRef = QueryT . newRef
266− readRef = QueryT . readRef
267− writeRef r = QueryT . writeRef r
268−
269−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (QueryT t q m) where
270− newEventWithTrigger = QueryT . newEventWithTrigger
271− newFanEventWithTrigger a = QueryT . lift $ newFanEventWithTrigger a
272−
273−-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
274−instance (Monoid a, Monad m) => Monoid (QueryT t q m a) where
275− mempty = pure mempty
276− mappend = (<>)
277−
278−instance (S.Semigroup a, Monad m) => S.Semigroup (QueryT t q m a) where
279− (<>) = liftA2 (S.<>)
280−
281−-- | withQueryT's QueryMorphism argument needs to be a group homomorphism in order to behave correctly
282−withQueryT :: (MonadFix m, PostBuild t m, Group q, Group q', Additive q, Additive q', Query q')
283− => QueryMorphism q q'
284− -> QueryT t q m a
285− -> QueryT t q' m a
286−withQueryT f a = do
287− r' <- askQueryResult
288− (result, q) <- lift $ runQueryT a $ mapQueryResult f <$> r'
289− tellQueryIncremental $ unsafeBuildIncremental
290− (fmap (mapQuery f) (sample (currentIncremental q)))
291− (fmapCheap (AdditivePatch . mapQuery f . unAdditivePatch) $ updatedIncremental q)
292− return result
293−
294−-- | dynWithQueryT's (Dynamic t QueryMorphism) argument needs to be a group homomorphism at all times in order to behave correctly
295−dynWithQueryT :: (MonadFix m, PostBuild t m, Group q, Additive q, Group q', Additive q', Query q')
296− => Dynamic t (QueryMorphism q q')
297− -> QueryT t q m a
298− -> QueryT t q' m a
299−dynWithQueryT f q = do
300− r' <- askQueryResult
301− (result, q') <- lift $ runQueryT q $ zipDynWith mapQueryResult f r'
302− tellQueryIncremental $ zipDynIncrementalWith mapQuery f q'
303− return result
304− where zipDynIncrementalWith g da ib =
305− let eab = align (updated da) (updatedIncremental ib)
306− ec = flip push eab $ \case
307− This a -> do
308− aOld <- sample $ current da
309− b <- sample $ currentIncremental ib
310− return $ Just $ AdditivePatch (g a b ~~ g aOld b)
311− That (AdditivePatch b) -> do
312− a <- sample $ current da
313− return $ Just $ AdditivePatch $ g a b
314− These a (AdditivePatch b) -> do
315− aOld <- sample $ current da
316− bOld <- sample $ currentIncremental ib
317− return $ Just $ AdditivePatch $ mconcat [ g a bOld, negateG (g aOld bOld), g a b]
318− in unsafeBuildIncremental (g <$> sample (current da) <*> sample (currentIncremental ib)) ec
319−
320−instance (Monad m, Group q, Additive q, Query q, Reflex t) => MonadQuery t q (QueryT t q m) where
321− tellQueryIncremental q = do
322− QueryT (modify (currentIncremental q:))
323− QueryT (lift (tellEvent (fmapCheap unAdditivePatch (updatedIncremental q))))
324− askQueryResult = QueryT ask
325− queryIncremental q = do
326− tellQueryIncremental q
327− zipDynWith crop (incrementalToDynamic q) <$> askQueryResult
328−
329−instance Requester t m => Requester t (QueryT t q m) where
330− type Request (QueryT t q m) = Request m
331− type Response (QueryT t q m) = Response m
332− requesting = lift . requesting
333− requesting_ = lift . requesting_
334−
335−instance EventWriter t w m => EventWriter t w (QueryT t q m) where
336− tellEvent = lift . tellEvent
337−
338−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (QueryT t q m) where
339− tellDyn = lift . tellDyn
src/Reflex/Query/Class.hsdeleted−99
@@ -1,99 +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− , mapQuery
19− , mapQueryResult
20− ) where
21−
22−import Control.Category (Category)
23−import qualified Control.Category as Cat
24−import Control.Monad.Reader
25−import Data.Bits
26−import Data.Data
27−import Data.Ix
28−import Data.Map.Monoidal (MonoidalMap)
29−import qualified Data.Map.Monoidal as MonoidalMap
30−import Data.Semigroup
31−import Foreign.Storable
32−
33−import Reflex.Class
34−
35−class (Monoid (QueryResult a), Semigroup (QueryResult a)) => Query a where
36− type QueryResult a :: *
37− crop :: a -> QueryResult a -> QueryResult a
38−
39−instance (Ord k, Query v) => Query (MonoidalMap k v) where
40− type QueryResult (MonoidalMap k v) = MonoidalMap k (QueryResult v)
41− crop q r = MonoidalMap.intersectionWith (flip crop) r q
42−
43−-- | NB: QueryMorphism's must be group homomorphisms when acting on the query type
44−-- and compatible with the query relationship when acting on the query result
45−data QueryMorphism q q' = QueryMorphism
46− { _queryMorphism_mapQuery :: q -> q'
47− , _queryMorphism_mapQueryResult :: QueryResult q' -> QueryResult q
48− }
49−
50−instance Category QueryMorphism where
51− id = QueryMorphism id id
52− qm . qm' = QueryMorphism
53− { _queryMorphism_mapQuery = mapQuery qm . mapQuery qm'
54− , _queryMorphism_mapQueryResult = mapQueryResult qm' . mapQueryResult qm
55− }
56−
57−mapQuery :: QueryMorphism q q' -> q -> q'
58−mapQuery = _queryMorphism_mapQuery
59−
60−mapQueryResult :: QueryMorphism q q' -> QueryResult q' -> QueryResult q
61−mapQueryResult = _queryMorphism_mapQueryResult
62−
63−-- | This type keeps track of the multiplicity of elements of the view selector that are being used by the app
64−newtype SelectedCount = SelectedCount { unSelectedCount :: Int }
65− deriving (Eq, Ord, Show, Read, Integral, Num, Bounded, Enum, Real, Ix, Bits, FiniteBits, Storable, Data)
66−
67−instance Semigroup SelectedCount where
68− SelectedCount a <> SelectedCount b = SelectedCount (a + b)
69−
70−instance Monoid SelectedCount where
71− mempty = SelectedCount 0
72− mappend = (<>)
73−
74−instance Group SelectedCount where
75− negateG (SelectedCount a) = SelectedCount (negate a)
76−
77−instance Additive SelectedCount
78−
79−-- | 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.
80−combineSelectedCounts :: SelectedCount -> SelectedCount -> Maybe SelectedCount
81−combineSelectedCounts (SelectedCount i) (SelectedCount j) = if i == negate j then Nothing else Just $ SelectedCount (i + j)
82−
83−class (Group q, Additive q, Query q) => MonadQuery t q m | m -> q t where
84− tellQueryIncremental :: Incremental t (AdditivePatch q) -> m ()
85− askQueryResult :: m (Dynamic t (QueryResult q))
86− queryIncremental :: Incremental t (AdditivePatch q) -> m (Dynamic t (QueryResult q))
87−
88−instance (Monad m, MonadQuery t q m) => MonadQuery t q (ReaderT r m) where
89− tellQueryIncremental = lift . tellQueryIncremental
90− askQueryResult = lift askQueryResult
91− queryIncremental = lift . queryIncremental
92−
93−tellQueryDyn :: (Reflex t, MonadQuery t q m) => Dynamic t q -> m ()
94−tellQueryDyn d = tellQueryIncremental $ unsafeBuildIncremental (sample (current d)) $ attachWith (\old new -> AdditivePatch $ new ~~ old) (current d) (updated d)
95−
96−queryDyn :: (Reflex t, Monad m, MonadQuery t q m) => Dynamic t q -> m (Dynamic t (QueryResult q))
97−queryDyn q = do
98− tellQueryDyn q
99− zipDynWith crop q <$> askQueryResult
src/Reflex/Requester/Base.hsdeleted−429
@@ -1,429 +0,0 @@
1−-- | This module provides 'RequesterT', the standard implementation of
2−-- 'Requester'.
3−{-# LANGUAGE AllowAmbiguousTypes #-}
4−{-# LANGUAGE CPP #-}
5−{-# LANGUAGE EmptyDataDecls #-}
6−{-# LANGUAGE ExistentialQuantification #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE GADTs #-}
9−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10−{-# LANGUAGE MultiParamTypeClasses #-}
11−{-# LANGUAGE Rank2Types #-}
12−{-# LANGUAGE RecursiveDo #-}
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.Requester.Base
22− ( RequesterT (..)
23− , runRequesterT
24− , runWithReplaceRequesterTWith
25− , traverseIntMapWithKeyWithAdjustRequesterTWith
26− , traverseDMapWithKeyWithAdjustRequesterTWith
27− , RequesterData
28− , RequesterDataKey
29− , traverseRequesterData
30− , requesterDataToList
31− , singletonRequesterData
32− ) where
33−
34−import Reflex.Class
35−import Reflex.Adjustable.Class
36−import Reflex.Dynamic
37−import Reflex.Host.Class
38−import Reflex.PerformEvent.Class
39−import Reflex.PostBuild.Class
40−import Reflex.Requester.Class
41−import Reflex.TriggerEvent.Class
42−
43−import Control.Applicative (liftA2)
44−import Control.Monad.Exception
45−import Control.Monad.Identity
46−import Control.Monad.Reader
47−import Control.Monad.Ref
48−import Control.Monad.State.Strict
49−import Data.Bits
50−import Data.Coerce
51−import Data.Dependent.Map (DMap, DSum (..))
52−import qualified Data.Dependent.Map as DMap
53−import Data.Functor.Compose
54−import Data.Functor.Misc
55−import Data.IntMap.Strict (IntMap)
56−import qualified Data.IntMap.Strict as IntMap
57−import Data.Map (Map)
58−import qualified Data.Map as Map
59−import Data.Monoid ((<>))
60−import Data.Proxy
61−import qualified Data.Semigroup as S
62−import Data.Some (Some)
63−import qualified Data.Some as Some
64−import Data.Type.Equality
65−import Data.Unique.Tag
66−
67−import GHC.Exts (Any)
68−import Unsafe.Coerce
69−
70−--TODO: Make this module type-safe
71−
72−newtype TagMap (f :: * -> *) = TagMap (IntMap Any)
73−
74−newtype RequesterData f = RequesterData (TagMap (Entry f))
75−
76−data RequesterDataKey a where
77− RequesterDataKey_Single :: {-# UNPACK #-} !(MyTag (Single a)) -> RequesterDataKey a
78− RequesterDataKey_Multi :: {-# UNPACK #-} !(MyTag Multi) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a --TODO: Don't put a second Int here (or in the other Multis); use a single Int instead
79− RequesterDataKey_Multi2 :: {-# UNPACK #-} !(MyTag (Multi2 k)) -> !(Some k) -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
80− RequesterDataKey_Multi3 :: {-# UNPACK #-} !(MyTag Multi3) -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> !(RequesterDataKey a) -> RequesterDataKey a
81−
82−singletonRequesterData :: RequesterDataKey a -> f a -> RequesterData f
83−singletonRequesterData rdk v = case rdk of
84− RequesterDataKey_Single k -> RequesterData $ singletonTagMap k $ Entry v
85− RequesterDataKey_Multi k k' k'' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ singletonRequesterData k'' v
86− RequesterDataKey_Multi2 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ Map.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
87− RequesterDataKey_Multi3 k k' k'' k''' -> RequesterData $ singletonTagMap k $ Entry $ IntMap.singleton k' $ IntMap.singleton k'' $ singletonRequesterData k''' v
88−
89−requesterDataToList :: RequesterData f -> [DSum RequesterDataKey f]
90−requesterDataToList (RequesterData m) = do
91− k :=> Entry e <- tagMapToList m
92− case myKeyType k of
93− MyTagType_Single -> return $ RequesterDataKey_Single k :=> e
94− MyTagType_Multi -> do
95− (k', e') <- IntMap.toList e
96− k'' :=> e'' <- requesterDataToList e'
97− return $ RequesterDataKey_Multi k k' k'' :=> e''
98− MyTagType_Multi2 -> do
99− (k', e') <- Map.toList e
100− (k'', e'') <- IntMap.toList e'
101− k''' :=> e''' <- requesterDataToList e''
102− return $ RequesterDataKey_Multi2 k k' k'' k''' :=> e'''
103− MyTagType_Multi3 -> do
104− (k', e') <- IntMap.toList e
105− (k'', e'') <- IntMap.toList e'
106− k''' :=> e''' <- requesterDataToList e''
107− return $ RequesterDataKey_Multi3 k k' k'' k''' :=> e'''
108−
109−singletonTagMap :: forall f a. MyTag a -> f a -> TagMap f
110−singletonTagMap (MyTag k) v = TagMap $ IntMap.singleton k $ (unsafeCoerce :: f a -> Any) v
111−
112−tagMapToList :: forall f. TagMap f -> [DSum MyTag f]
113−tagMapToList (TagMap m) = f <$> IntMap.toList m
114− where f :: (Int, Any) -> DSum MyTag f
115− f (k, v) = MyTag k :=> (unsafeCoerce :: Any -> f a) v
116−
117−traverseTagMapWithKey :: forall t f g. Applicative t => (forall a. MyTag a -> f a -> t (g a)) -> TagMap f -> t (TagMap g)
118−traverseTagMapWithKey f (TagMap m) = TagMap <$> IntMap.traverseWithKey g m
119− where
120− g :: Int -> Any -> t Any
121− g k v = (unsafeCoerce :: g a -> Any) <$> f (MyTag k) ((unsafeCoerce :: Any -> f a) v)
122−
123−-- | Runs in reverse to accommodate for the fact that we accumulate it in reverse
124−traverseRequesterData :: forall m request response. Applicative m => (forall a. request a -> m (response a)) -> RequesterData request -> m (RequesterData response)
125−traverseRequesterData f (RequesterData m) = RequesterData <$> traverseTagMapWithKey go m --TODO: reverse this, since our tags are in reverse order
126− where go :: forall x. MyTag x -> Entry request x -> m (Entry response x)
127− go k (Entry request) = Entry <$> case myKeyType k of
128− MyTagType_Single -> f request
129− MyTagType_Multi -> traverse (traverseRequesterData f) request
130− MyTagType_Multi2 -> traverse (traverse (traverseRequesterData f)) request
131− MyTagType_Multi3 -> traverse (traverse (traverseRequesterData f)) request
132−
133−data MyTagType :: * -> * where
134− MyTagType_Single :: MyTagType (Single a)
135− MyTagType_Multi :: MyTagType Multi
136− MyTagType_Multi2 :: MyTagType (Multi2 k)
137− MyTagType_Multi3 :: MyTagType Multi3
138−
139−myKeyType :: MyTag x -> MyTagType x
140−myKeyType (MyTag k) = case k .&. 0x3 of
141− 0x0 -> unsafeCoerce MyTagType_Single
142− 0x1 -> unsafeCoerce MyTagType_Multi
143− 0x2 -> unsafeCoerce MyTagType_Multi2
144− 0x3 -> unsafeCoerce MyTagType_Multi3
145− t -> error $ "Reflex.Requester.Base.myKeyType: no such key type" <> show t
146−
147−data Single a
148−data Multi
149−data Multi2 (k :: * -> *)
150−data Multi3
151−
152−class MyTagTypeOffset x where
153− myTagTypeOffset :: proxy x -> Int
154−
155−instance MyTagTypeOffset (Single a) where
156− myTagTypeOffset _ = 0x0
157−
158−instance MyTagTypeOffset Multi where
159− myTagTypeOffset _ = 0x1
160−
161−instance MyTagTypeOffset (Multi2 k) where
162− myTagTypeOffset _ = 0x2
163−
164−instance MyTagTypeOffset Multi3 where
165− myTagTypeOffset _ = 0x3
166−
167−type family EntryContents request a where
168− EntryContents request (Single a) = request a
169− EntryContents request Multi = IntMap (RequesterData request)
170− EntryContents request (Multi2 k) = Map (Some k) (IntMap (RequesterData request))
171− EntryContents request Multi3 = IntMap (IntMap (RequesterData request))
172−
173−newtype Entry request x = Entry { unEntry :: EntryContents request x }
174−
175−{-# INLINE singleEntry #-}
176−singleEntry :: f a -> Entry f (Single a)
177−singleEntry = Entry
178−
179−{-# INLINE multiEntry #-}
180−multiEntry :: IntMap (RequesterData f) -> Entry f Multi
181−multiEntry = Entry
182−
183−{-# INLINE unMultiEntry #-}
184−unMultiEntry :: Entry f Multi -> IntMap (RequesterData f)
185−unMultiEntry = unEntry
186−
187−-- | We use a hack here to pretend we have x ~ request a; we don't want to use a GADT, because GADTs (even with zero-size existential contexts) can't be newtypes
188−-- WARNING: This type should never be exposed. In particular, this is extremely unsound if a MyTag from one run of runRequesterT is ever compared against a MyTag from another
189−newtype MyTag x = MyTag Int deriving (Show, Eq, Ord, Enum)
190−
191−newtype MyTagWrap (f :: * -> *) x = MyTagWrap Int deriving (Show, Eq, Ord, Enum)
192−
193−{-# INLINE castMyTagWrap #-}
194−castMyTagWrap :: MyTagWrap f (Entry f x) -> MyTagWrap g (Entry g x)
195−castMyTagWrap = coerce
196−
197−instance GEq MyTag where
198− (MyTag a) `geq` (MyTag b) =
199− if a == b
200− then Just $ unsafeCoerce Refl
201− else Nothing
202−
203−instance GCompare MyTag where
204− (MyTag a) `gcompare` (MyTag b) =
205− case a `compare` b of
206− LT -> GLT
207− EQ -> unsafeCoerce GEQ
208− GT -> GGT
209−
210−instance GEq (MyTagWrap f) where
211− (MyTagWrap a) `geq` (MyTagWrap b) =
212− if a == b
213− then Just $ unsafeCoerce Refl
214− else Nothing
215−
216−instance GCompare (MyTagWrap f) where
217− (MyTagWrap a) `gcompare` (MyTagWrap b) =
218− case a `compare` b of
219− LT -> GLT
220− EQ -> unsafeCoerce GEQ
221− GT -> GGT
222−
223−data RequesterState t (request :: * -> *) = RequesterState
224− { _requesterState_nextMyTag :: {-# UNPACK #-} !Int -- Starts at -4 and goes down by 4 each time, to accommodate two 'type' bits at the bottom
225− , _requesterState_requests :: ![(Int, Event t Any)]
226− }
227−
228−-- | A basic implementation of 'Requester'.
229−newtype RequesterT t request (response :: * -> *) m a = RequesterT { unRequesterT :: StateT (RequesterState t request) (ReaderT (EventSelectorInt t Any) m) a }
230− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException
231−-- MonadAsyncException can't be derived on ghc-8.0.1; we use base-4.9.1 as a proxy for ghc-8.0.2
232−#if MIN_VERSION_base(4,9,1)
233− , MonadAsyncException
234−#endif
235− )
236−
237−deriving instance MonadSample t m => MonadSample t (RequesterT t request response m)
238−deriving instance MonadHold t m => MonadHold t (RequesterT t request response m)
239−deriving instance PostBuild t m => PostBuild t (RequesterT t request response m)
240−deriving instance TriggerEvent t m => TriggerEvent t (RequesterT t request response m)
241−
242−-- TODO: Monoid and Semigroup can likely be derived once StateT has them.
243−instance (Monoid a, Monad m) => Monoid (RequesterT t request response m a) where
244− mempty = pure mempty
245− mappend = liftA2 mappend
246−
247−instance (S.Semigroup a, Monad m) => S.Semigroup (RequesterT t request response m a) where
248− (<>) = liftA2 (S.<>)
249−
250−
251−-- | Run a 'RequesterT' action. The resulting 'Event' will fire whenever
252−-- requests are made, and responses should be provided in the input 'Event'.
253−-- The 'Tag' keys will be used to return the responses to the same place the
254−-- requests were issued.
255−
256−runRequesterT :: (Reflex t, Monad m)
257− => RequesterT t request response m a
258− -> Event t (RequesterData response) --TODO: This DMap will be in reverse order, so we need to make sure the caller traverses it in reverse
259− -> m (a, Event t (RequesterData request)) --TODO: we need to hide these 'MyTag's here, because they're unsafe to mix in the wild
260−runRequesterT (RequesterT a) responses = do
261− (result, s) <- runReaderT (runStateT a $ RequesterState (-4) []) $ fanInt $
262− coerceEvent responses
263− return (result, fmapCheap (RequesterData . TagMap) $ mergeInt $ IntMap.fromDistinctAscList $ _requesterState_requests s)
264−
265−instance (Reflex t, Monad m) => Requester t (RequesterT t request response m) where
266− type Request (RequesterT t request response m) = request
267− type Response (RequesterT t request response m) = response
268− requesting = fmap coerceEvent . responseFromTag . castMyTagWrap <=< tagRequest . (coerceEvent :: Event t (request a) -> Event t (Entry request (Single a)))
269− requesting_ = void . tagRequest . fmapCheap singleEntry
270−
271−{-# INLINE tagRequest #-}
272−tagRequest :: forall m x t request response. (Monad m, MyTagTypeOffset x) => Event t (Entry request x) -> RequesterT t request response m (MyTagWrap request (Entry request x))
273−tagRequest req = do
274− old <- RequesterT get
275− let n = _requesterState_nextMyTag old .|. myTagTypeOffset (Proxy :: Proxy x)
276− t = MyTagWrap n
277− RequesterT $ put $ RequesterState
278− { _requesterState_nextMyTag = _requesterState_nextMyTag old - 0x4
279− , _requesterState_requests = (n, (unsafeCoerce :: Event t (Entry request x) -> Event t Any) req) : _requesterState_requests old
280− }
281− return t
282−
283−{-# INLINE responseFromTag #-}
284−responseFromTag :: Monad m => MyTagWrap response (Entry response x) -> RequesterT t request response m (Event t (Entry response x))
285−responseFromTag (MyTagWrap t) = do
286− responses :: EventSelectorInt t Any <- RequesterT ask
287− return $ (unsafeCoerce :: Event t Any -> Event t (Entry response x)) $ selectInt responses t
288−
289−instance MonadTrans (RequesterT t request response) where
290− lift = RequesterT . lift . lift
291−
292−instance PerformEvent t m => PerformEvent t (RequesterT t request response m) where
293− type Performable (RequesterT t request response m) = Performable m
294− performEvent_ = lift . performEvent_
295− performEvent = lift . performEvent
296−
297−instance MonadRef m => MonadRef (RequesterT t request response m) where
298− type Ref (RequesterT t request response m) = Ref m
299− newRef = lift . newRef
300− readRef = lift . readRef
301− writeRef r = lift . writeRef r
302−
303−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequesterT t request response m) where
304− newEventWithTrigger = lift . newEventWithTrigger
305− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
306−
307−instance MonadReader r m => MonadReader r (RequesterT t request response m) where
308− ask = lift ask
309− local f (RequesterT a) = RequesterT $ mapStateT (mapReaderT $ local f) a
310− reader = lift . reader
311−
312−instance (Reflex t, Adjustable t m, MonadHold t m, MonadFix m) => Adjustable t (RequesterT t request response m) where
313− runWithReplace = runWithReplaceRequesterTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
314− traverseIntMapWithKeyWithAdjust = traverseIntMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseIntMapWithKeyWithAdjust f dm0 dm') patchIntMapNewElementsMap mergeIntIncremental
315− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
316− traverseDMapWithKeyWithAdjust = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjust f dm0 dm') mapPatchDMap weakenPatchDMapWith patchMapNewElementsMap mergeMapIncremental
317− traverseDMapWithKeyWithAdjustWithMove = traverseDMapWithKeyWithAdjustRequesterTWith (\f dm0 dm' -> lift $ traverseDMapWithKeyWithAdjustWithMove f dm0 dm') mapPatchDMapWithMove weakenPatchDMapWithMoveWith patchMapWithMoveNewElementsMap mergeMapIncrementalWithMove
318−
319−requesting' :: (MyTagTypeOffset x, Monad m) => Event t (Entry request x) -> RequesterT t request response m (Event t (Entry response x))
320−requesting' = responseFromTag . castMyTagWrap <=< tagRequest
321−
322−{-# INLINABLE runWithReplaceRequesterTWith #-}
323−runWithReplaceRequesterTWith :: forall m t request response a b. (Reflex t, MonadHold t m
324− , MonadFix m
325− )
326− => (forall a' b'. m a' -> Event t (m b') -> RequesterT t request response m (a', Event t b'))
327− -> RequesterT t request response m a
328− -> Event t (RequesterT t request response m b)
329− -> RequesterT t request response m (a, Event t b)
330−runWithReplaceRequesterTWith f a0 a' = do
331− rec na' <- numberOccurrencesFrom 1 a'
332− responses <- fmap (fmapCheap unMultiEntry) $ requesting' $ fmapCheap multiEntry $ switchPromptlyDyn requests --TODO: Investigate whether we can really get rid of the prompt stuff here
333− let responses' = fanInt responses
334− ((result0, requests0), v') <- f (runRequesterT a0 (selectInt responses' 0)) $ fmapCheap (\(n, a) -> fmap ((,) n) $ runRequesterT a $ selectInt responses' n) na'
335− requests <- holdDyn (fmapCheap (IntMap.singleton 0) requests0) $ fmapCheap (\(n, (_, reqs)) -> fmapCheap (IntMap.singleton n) reqs) v'
336− return (result0, fmapCheap (fst . snd) v')
337−
338−{-# INLINE traverseIntMapWithKeyWithAdjustRequesterTWith #-}
339−traverseIntMapWithKeyWithAdjustRequesterTWith :: forall t request response m v v' p.
340− ( Reflex t
341− , MonadHold t m
342− , PatchTarget (p (Event t (IntMap (RequesterData request)))) ~ IntMap (Event t (IntMap (RequesterData request)))
343− , Patch (p (Event t (IntMap (RequesterData request))))
344− , Functor p
345− , MonadFix m
346− )
347− => ( (IntMap.Key -> (IntMap.Key, v) -> m (Event t (IntMap (RequesterData request)), v'))
348− -> IntMap (IntMap.Key, v)
349− -> Event t (p (IntMap.Key, v))
350− -> RequesterT t request response m (IntMap (Event t (IntMap (RequesterData request)), v'), Event t (p (Event t (IntMap (RequesterData request)), v')))
351− )
352− -> (p (Event t (IntMap (RequesterData request))) -> IntMap (Event t (IntMap (RequesterData request))))
353− -> (Incremental t (p (Event t (IntMap (RequesterData request)))) -> Event t (IntMap (IntMap (RequesterData request))))
354− -> (IntMap.Key -> v -> RequesterT t request response m v')
355− -> IntMap v
356− -> Event t (p v)
357− -> RequesterT t request response m (IntMap v', Event t (p v'))
358−traverseIntMapWithKeyWithAdjustRequesterTWith base patchNewElements mergePatchIncremental f dm0 dm' = do
359− rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
360− let responses :: EventSelectorInt t (IntMap (RequesterData response))
361− responses = fanInt $ fmapCheap unpack response
362− unpack :: Entry response Multi3 -> IntMap (IntMap (RequesterData response))
363− unpack = unEntry
364− pack :: IntMap (IntMap (RequesterData request)) -> Entry request Multi3
365− pack = Entry
366− f' :: IntMap.Key -> (Int, v) -> m (Event t (IntMap (RequesterData request)), v')
367− f' k (n, v) = do
368− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ selectInt responses k --TODO: Instead of doing fmapMaybeCheap, can we share a fanInt across all instances of a given key, or at least the ones that are adjacent in time?
369− return (fmapCheap (IntMap.singleton n) myRequests, result)
370− ndm' <- numberOccurrencesFrom 1 dm'
371− (children0, children') <- base f' (fmap ((,) 0) dm0) $ fmap (\(n, dm) -> fmap ((,) n) dm) ndm' --TODO: Avoid this somehow, probably by adding some sort of per-cohort information passing to Adjustable
372− let result0 = fmap snd children0
373− result' = fforCheap children' $ fmap snd
374− requests0 :: IntMap (Event t (IntMap (RequesterData request)))
375− requests0 = fmap fst children0
376− requests' :: Event t (p (Event t (IntMap (RequesterData request))))
377− requests' = fforCheap children' $ fmap fst
378− promptRequests :: Event t (IntMap (IntMap (RequesterData request)))
379− promptRequests = coincidence $ fmapCheap (mergeInt . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
380− requests <- holdIncremental requests0 requests'
381− return (result0, result')
382−
383−{-# INLINE traverseDMapWithKeyWithAdjustRequesterTWith #-}
384−traverseDMapWithKeyWithAdjustRequesterTWith :: forall k t request response m v v' p p'.
385− ( GCompare k
386− , Reflex t
387− , MonadHold t m
388− , PatchTarget (p' (Some k) (Event t (IntMap (RequesterData request)))) ~ Map (Some k) (Event t (IntMap (RequesterData request)))
389− , Patch (p' (Some k) (Event t (IntMap (RequesterData request))))
390− , MonadFix m
391− )
392− => (forall k' v1 v2. GCompare k'
393− => (forall a. k' a -> v1 a -> m (v2 a))
394− -> DMap k' v1
395− -> Event t (p k' v1)
396− -> RequesterT t request response m (DMap k' v2, Event t (p k' v2))
397− )
398− -> (forall v1 v2. (forall a. v1 a -> v2 a) -> p k v1 -> p k v2)
399− -> (forall v1 v2. (forall a. v1 a -> v2) -> p k v1 -> p' (Some k) v2)
400− -> (forall v2. p' (Some k) v2 -> Map (Some k) v2)
401− -> (forall a. Incremental t (p' (Some k) (Event t a)) -> Event t (Map (Some k) a))
402− -> (forall a. k a -> v a -> RequesterT t request response m (v' a))
403− -> DMap k v
404− -> Event t (p k v)
405− -> RequesterT t request response m (DMap k v', Event t (p k v'))
406−traverseDMapWithKeyWithAdjustRequesterTWith base mapPatch weakenPatchWith patchNewElements mergePatchIncremental f dm0 dm' = do
407− rec response <- requesting' $ fmapCheap pack $ promptRequests `mappend` mergePatchIncremental requests --TODO: Investigate whether we can really get rid of the prompt stuff here
408− let responses :: EventSelector t (Const2 (Some k) (IntMap (RequesterData response)))
409− responses = fanMap $ fmapCheap unpack response
410− unpack :: Entry response (Multi2 k) -> Map (Some k) (IntMap (RequesterData response))
411− unpack = unEntry
412− pack :: Map (Some k) (IntMap (RequesterData request)) -> Entry request (Multi2 k)
413− pack = Entry
414− f' :: forall a. k a -> Compose ((,) Int) v a -> m (Compose ((,) (Event t (IntMap (RequesterData request)))) v' a)
415− f' k (Compose (n, v)) = do
416− (result, myRequests) <- runRequesterT (f k v) $ fmapMaybeCheap (IntMap.lookup n) $ select responses (Const2 (Some.This k))
417− return $ Compose (fmapCheap (IntMap.singleton n) myRequests, result)
418− ndm' <- numberOccurrencesFrom 1 dm'
419− (children0, children') <- base f' (DMap.map (\v -> Compose (0, v)) dm0) $ fmap (\(n, dm) -> mapPatch (\v -> Compose (n, v)) dm) ndm'
420− let result0 = DMap.map (snd . getCompose) children0
421− result' = fforCheap children' $ mapPatch $ snd . getCompose
422− requests0 :: Map (Some k) (Event t (IntMap (RequesterData request)))
423− requests0 = weakenDMapWith (fst . getCompose) children0
424− requests' :: Event t (p' (Some k) (Event t (IntMap (RequesterData request))))
425− requests' = fforCheap children' $ weakenPatchWith $ fst . getCompose
426− promptRequests :: Event t (Map (Some k) (IntMap (RequesterData request)))
427− promptRequests = coincidence $ fmapCheap (mergeMap . patchNewElements) requests' --TODO: Create a mergeIncrementalPromptly, and use that to eliminate this 'coincidence'
428− requests <- holdIncremental requests0 requests'
429− return (result0, result')
src/Reflex/Requester/Class.hsdeleted−73
@@ -1,73 +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 RecursiveDo #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−module Reflex.Requester.Class
17− ( Requester (..)
18− , withRequesting
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− -- | Emit a request whenever the given 'Event' fires, and return responses in
40− -- the resulting 'Event'.
41− requesting :: Event t (Request m a) -> m (Event t (Response m a))
42− -- | Emit a request whenever the given 'Event' fires, and ignore all responses.
43− requesting_ :: Event t (Request m a) -> m ()
44−
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− requesting = lift . requesting
50− requesting_ = lift . requesting_
51−
52−instance Requester t m => Requester t (StateT s m) where
53− type Request (StateT s m) = Request m
54− type Response (StateT s m) = Response m
55− requesting = lift . requesting
56− requesting_ = lift . requesting_
57−
58−instance Requester t m => Requester t (Lazy.StateT s m) where
59− type Request (Lazy.StateT s m) = Request m
60− type Response (Lazy.StateT s m) = Response m
61− requesting = lift . requesting
62− requesting_ = lift . requesting_
63−
64−-- | Emit a request whenever the given 'Event' fires, and unwrap the responses
65−-- before returning them. @Response m@ must be 'Identity'.
66−requestingIdentity :: (Requester t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
67−requestingIdentity = fmap coerceEvent . requesting
68−
69−withRequesting :: (Requester t m, MonadFix m) => (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
70−withRequesting f = do
71− rec response <- requesting request
72− (request, result) <- f response
73− return result
src/Reflex/Spider.hsdeleted−17
@@ -1,17 +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− , runSpiderHostForTimeline
11− , newSpiderTimeline
12− , withSpiderTimeline
13− -- * Deprecated
14− , SpiderEnv
15− ) where
16−
17−import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hsdeleted−2634
@@ -1,2634 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ConstraintKinds #-}
4−{-# LANGUAGE EmptyDataDecls #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6−{-# LANGUAGE FlexibleContexts #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE GADTs #-}
10−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11−{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MultiParamTypeClasses #-}
13−{-# LANGUAGE MultiWayIf #-}
14−{-# LANGUAGE RankNTypes #-}
15−{-# LANGUAGE RoleAnnotations #-}
16−{-# LANGUAGE ScopedTypeVariables #-}
17−{-# LANGUAGE TypeFamilies #-}
18−{-# LANGUAGE TypeOperators #-}
19−{-# LANGUAGE UndecidableInstances #-}
20−#ifdef USE_REFLEX_OPTIMIZER
21−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
22−#endif
23−-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
24−-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
25−module Reflex.Spider.Internal (module Reflex.Spider.Internal) where
26−
27−#if MIN_VERSION_base(4,10,0)
28−import Control.Applicative (liftA2)
29−#endif
30−import Control.Concurrent
31−import Control.Exception
32−import Control.Monad hiding (forM, forM_, mapM, mapM_)
33−import Control.Monad.Exception
34−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
35−import Control.Monad.Primitive
36−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_)
37−import Control.Monad.Ref
38−import Data.Align
39−import Data.Coerce
40−import Data.Dependent.Map (DMap, DSum (..))
41−import qualified Data.Dependent.Map as DMap
42−import Data.FastMutableIntMap (FastMutableIntMap, PatchIntMap (..))
43−import qualified Data.FastMutableIntMap as FastMutableIntMap
44−import Data.Foldable hiding (concat, elem, sequence_)
45−import Data.Functor.Constant
46−import Data.Functor.Misc
47−import Data.Functor.Product
48−import Data.GADT.Compare
49−import Data.IntMap.Strict (IntMap)
50−import qualified Data.IntMap.Strict as IntMap
51−import Data.IORef
52−import Data.Maybe
53−import Data.Monoid ((<>))
54−import Data.Proxy
55−import Data.These
56−import Data.Traversable
57−import GHC.Exts
58−import GHC.IORef (IORef (..))
59−import GHC.Stack
60−import Reflex.FastWeak
61−import Reflex.FunctorMaybe
62−import System.IO.Unsafe
63−import System.Mem.Weak
64−import Unsafe.Coerce
65−
66−#ifdef DEBUG_CYCLES
67−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
68−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
69−import qualified Data.List.NonEmpty as NonEmpty
70−import Data.Monoid (mempty)
71−import Data.Tree (Forest, Tree (..), drawForest)
72−#endif
73−
74−import Data.FastWeakBag (FastWeakBag)
75−import qualified Data.FastWeakBag as FastWeakBag
76−import Data.Reflection
77−import Data.Some (Some)
78−import qualified Data.Some as Some
79−import Data.Type.Coercion
80−import Data.WeakBag (WeakBag, WeakBagTicket, _weakBag_children)
81−import qualified Data.WeakBag as WeakBag
82−import qualified Reflex.Class
83−import qualified Reflex.Class as R
84−import qualified Reflex.Host.Class
85−import Reflex.NotReady.Class
86−import Reflex.Patch
87−import qualified Reflex.Patch.DMapWithMove as PatchDMapWithMove
88−
89−#ifdef DEBUG_TRACE_EVENTS
90−import qualified Data.ByteString.Char8 as BS8
91−import System.IO (stderr)
92−#endif
93−
94−#ifdef DEBUG_TRACE_EVENTS
95−
96−withStackOneLine :: (BS8.ByteString -> a) -> a
97−withStackOneLine expr = unsafePerformIO $ do
98− stack <- currentCallStack
99− return (expr (BS8.pack (unwords (reverse stack))))
100−
101−#endif
102−
103−debugPropagate :: Bool
104−
105−debugInvalidateHeight :: Bool
106−
107−debugInvalidate :: Bool
108−
109−#ifdef DEBUG
110−
111−#define DEBUG_NODEIDS
112−
113−debugPropagate = True
114−
115−debugInvalidateHeight = False
116−
117−debugInvalidate = True
118−
119−class HasNodeId a where
120− getNodeId :: a -> Int
121−
122−instance HasNodeId (Hold x p a) where
123− getNodeId = holdNodeId
124−
125−instance HasNodeId (PushSubscribed x a b) where
126− getNodeId = pushSubscribedNodeId
127−
128−instance HasNodeId (SwitchSubscribed x a) where
129− getNodeId = switchSubscribedNodeId
130−
131−instance HasNodeId (FanSubscribed x a) where
132− getNodeId = fanSubscribedNodeId
133−
134−instance HasNodeId (CoincidenceSubscribed x a) where
135− getNodeId = coincidenceSubscribedNodeId
136−
137−instance HasNodeId (RootSubscribed x a) where
138− getNodeId = rootSubscribedNodeId
139−
140−instance HasNodeId (Pull x a) where
141− getNodeId = pullNodeId
142−
143−{-# INLINE showNodeId #-}
144−showNodeId :: HasNodeId a => a -> String
145−showNodeId = ("#"<>) . show . getNodeId
146−
147−showSubscriberType :: Subscriber x a -> String
148−showSubscriberType = \case
149− SubscriberPush _ _ -> "SubscriberPush"
150− SubscriberMerge _ _ -> "SubscriberMerge"
151− SubscriberFan _ -> "SubscriberFan"
152− SubscriberMergeChange _ -> "SubscriberMergeChange"
153− SubscriberHold _ -> "SubscriberHold"
154− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
155− SubscriberSwitch _ -> "SubscriberSwitch"
156− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
157− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
158− SubscriberHandle -> "SubscriberHandle"
159−
160−showEventType :: Event x a -> String
161−showEventType = \case
162− EventRoot _ _ -> "EventRoot"
163− EventNever -> "EventNever"
164− EventPush _ -> "EventPush"
165− EventMerge _ -> "EventMerge"
166− EventFan _ _ -> "EventFan"
167− EventSwitch _ -> "EventSwitch"
168− EventCoincidence _ -> "EventCoincidence"
169− EventHold _ -> "EventHold"
170− EventDyn _ -> "EventDyn"
171− EventHoldIdentity _ -> "EventHoldIdentity"
172− EventDynIdentity _ -> "EventDynIdentity"
173−
174−#else
175−
176−debugPropagate = False
177−
178−debugInvalidateHeight = False
179−
180−debugInvalidate = False
181−
182−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
183−{-# INLINE showNodeId #-}
184−showNodeId :: a -> String
185−showNodeId _ = ""
186−
187−#endif
188−
189−#ifdef DEBUG_NODEIDS
190−{-# NOINLINE nextNodeIdRef #-}
191−nextNodeIdRef :: IORef Int
192−nextNodeIdRef = unsafePerformIO $ newIORef 1
193−
194−newNodeId :: IO Int
195−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
196−
197−{-# NOINLINE unsafeNodeId #-}
198−unsafeNodeId :: a -> Int
199−unsafeNodeId a = unsafePerformIO $ do
200− touch a
201− newNodeId
202−#endif
203−
204−--------------------------------------------------------------------------------
205−-- EventSubscription
206−--------------------------------------------------------------------------------
207−
208−--NB: Once you subscribe to an Event, you must always hold on the the WHOLE EventSubscription you get back
209−-- If you do not retain the subscription, you may be prematurely unsubscribed from the parent event.
210−data EventSubscription x = EventSubscription
211− { _eventSubscription_unsubscribe :: !(IO ())
212− , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
213− }
214−
215−unsubscribe :: EventSubscription x -> IO ()
216−unsubscribe (EventSubscription u _) = u
217−
218−--------------------------------------------------------------------------------
219−-- Event
220−--------------------------------------------------------------------------------
221−
222−newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
223−
224−{-# INLINE subscribeAndRead #-}
225−subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
226−subscribeAndRead = unEvent
227−
228−{-# RULES
229−"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
230−"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
231−"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
232− #-}
233−
234−-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
235−-- caching; if the computation function is very cheap, this is (much) more
236−-- efficient than 'push'
237−{-# INLINE [1] pushCheap #-}
238−pushCheap :: (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
239−pushCheap !f e = Event $ \sub -> do
240− (subscription, occ) <- subscribeAndRead e $ sub
241− { subscriberPropagate = \a -> do
242− mb <- f a
243− mapM_ (subscriberPropagate sub) mb
244− }
245− occ' <- join <$> mapM f occ
246− return (subscription, occ')
247−
248−-- | A subscriber that never triggers other 'Event's
249−{-# INLINE terminalSubscriber #-}
250−terminalSubscriber :: (a -> EventM x ()) -> Subscriber x a
251−terminalSubscriber p = Subscriber
252− { subscriberPropagate = p
253− , subscriberInvalidateHeight = \_ -> return ()
254− , subscriberRecalculateHeight = \_ -> return ()
255− }
256−
257−-- | Subscribe to an Event only for the duration of one occurrence
258−{-# INLINE subscribeAndReadHead #-}
259−subscribeAndReadHead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
260−subscribeAndReadHead e sub = do
261− subscriptionRef <- liftIO $ newIORef $ error "subscribeAndReadHead: not initialized"
262− (subscription, occ) <- subscribeAndRead e $ sub
263− { subscriberPropagate = \a -> do
264− liftIO $ unsubscribe =<< readIORef subscriptionRef
265− subscriberPropagate sub a
266− }
267− liftIO $ case occ of
268− Nothing -> writeIORef subscriptionRef $! subscription
269− Just _ -> unsubscribe subscription
270− return (subscription, occ)
271−
272−--TODO: Make this lazy in its input event
273−headE :: (MonadIO m, Defer (SomeMergeInit x) m) => Event x a -> m (Event x a)
274−headE originalE = do
275− parent <- liftIO $ newIORef $ Just originalE
276− defer $ SomeMergeInit $ do --TODO: Rename SomeMergeInit appropriately
277− let clearParent = liftIO $ writeIORef parent Nothing
278− (_, occ) <- subscribeAndReadHead originalE $ terminalSubscriber $ \_ -> clearParent
279− when (isJust occ) clearParent
280− return $ Event $ \sub -> do
281− liftIO (readIORef parent) >>= \case
282− Nothing -> return (EventSubscription (return ()) $ EventSubscribed zeroRef $ toAny (), Nothing)
283− Just e -> subscribeAndReadHead e sub
284−
285−data CacheSubscribed x a
286− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(FastWeakBag (Subscriber x a))
287− , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
288− , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
289− }
290−
291−-- | Construct an 'Event' whose value is guaranteed not to be recomputed
292−-- repeatedly
293−--
294−--TODO: Try a caching strategy where we subscribe directly to the parent when
295−--there's only one subscriber, and then build our own FastWeakBag only when a second
296−--subscriber joins
297−{-# NOINLINE [0] cacheEvent #-}
298−cacheEvent :: forall x a. HasSpiderTimeline x => Event x a -> Event x a
299−cacheEvent e =
300−#ifdef DEBUG_TRACE_EVENTS
301− withStackOneLine $ \callSite -> Event $
302−#else
303− Event $
304−#endif
305− let mSubscribedRef :: IORef (FastWeak (CacheSubscribed x a))
306− !mSubscribedRef = unsafeNewIORef e emptyFastWeak
307− in \sub -> {-# SCC "cacheEvent" #-} do
308−#ifdef DEBUG_TRACE_EVENTS
309− unless (BS8.null callSite) $ liftIO $ BS8.hPutStrLn stderr callSite
310−#endif
311− subscribedTicket <- liftIO (readIORef mSubscribedRef >>= getFastWeakTicket) >>= \case
312− Just subscribedTicket -> return subscribedTicket
313− Nothing -> do
314− subscribers <- liftIO FastWeakBag.empty
315− occRef <- liftIO $ newIORef Nothing -- This should never be read prior to being set below
316− (parentSub, occ) <- subscribeAndRead e $ Subscriber
317− { subscriberPropagate = \a -> do
318− liftIO $ writeIORef occRef $ Just a
319− scheduleClear occRef
320− propagateFast a subscribers
321− , subscriberInvalidateHeight = \old -> do
322− FastWeakBag.traverse subscribers $ invalidateSubscriberHeight old
323− , subscriberRecalculateHeight = \new -> do
324− FastWeakBag.traverse subscribers $ recalculateSubscriberHeight new
325− }
326− when (isJust occ) $ do
327− liftIO $ writeIORef occRef occ -- Set the initial value of occRef; we don't need to do this if occ is Nothing
328− scheduleClear occRef
329− let !subscribed = CacheSubscribed
330− { _cacheSubscribed_subscribers = subscribers
331− , _cacheSubscribed_parent = parentSub
332− , _cacheSubscribed_occurrence = occRef
333− }
334− subscribedTicket <- liftIO $ mkFastWeakTicket subscribed
335− liftIO $ writeIORef mSubscribedRef =<< getFastWeakTicketWeak subscribedTicket
336− return subscribedTicket
337− liftIO $ do
338− subscribed <- getFastWeakTicketValue subscribedTicket
339− ticket <- FastWeakBag.insert sub $ _cacheSubscribed_subscribers subscribed
340− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
341− let es = EventSubscription
342− { _eventSubscription_unsubscribe = do
343− FastWeakBag.remove ticket
344− isEmpty <- FastWeakBag.isEmpty $ _cacheSubscribed_subscribers subscribed
345− when isEmpty $ do
346− writeIORef mSubscribedRef emptyFastWeak
347− unsubscribe $ _cacheSubscribed_parent subscribed
348− touch ticket
349− touch subscribedTicket
350− , _eventSubscription_subscribed = EventSubscribed
351− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
352− , eventSubscribedRetained = toAny subscribedTicket
353− }
354− }
355− return (es, occ)
356−
357−subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
358−subscribe e s = fst <$> subscribeAndRead e s
359−
360−{-# INLINE wrap #-}
361−wrap :: MonadIO m => (t -> EventSubscribed x) -> (Subscriber x a -> m (WeakBagTicket, t, Maybe a)) -> Subscriber x a -> m (EventSubscription x, Maybe a)
362−wrap tag getSpecificSubscribed sub = do
363− (sln, subd, occ) <- getSpecificSubscribed sub
364− let es = tag subd
365− return (EventSubscription (WeakBag.remove sln >> touch sln) es, occ)
366−
367−eventRoot :: GCompare k => k a -> Root x k -> Event x a
368−eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
369−
370−eventNever :: Event x a
371−eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
372−
373−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
374−eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
375−
376−eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
377−eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
378−
379−eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
380−eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
381−
382−eventHold :: Hold x p -> Event x p
383−eventHold !h = Event $ subscribeHoldEvent h
384−
385−eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
386−eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
387−
388−{-# INLINE subscribeCoincidenceInner #-}
389−subscribeCoincidenceInner :: HasSpiderTimeline x => Event x a -> Height -> CoincidenceSubscribed x a -> EventM x (Maybe a, Height, EventSubscribed x)
390−subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
391− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
392− (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
393− innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
394− let height = max innerHeight outerHeight
395− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
396− return (innerOcc, height, innerSubd)
397−
398−--------------------------------------------------------------------------------
399−-- Subscriber
400−--------------------------------------------------------------------------------
401−
402−data Subscriber x a = Subscriber
403− { subscriberPropagate :: !(a -> EventM x ())
404− , subscriberInvalidateHeight :: !(Height -> IO ())
405− , subscriberRecalculateHeight :: !(Height -> IO ())
406− }
407−
408−--TODO: Move this comment to WeakBag
409−-- These function are constructor functions that are marked NOINLINE so they are
410−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
411−-- so any weak references that are attached to the constructors will have their
412−-- finalizer run. Using the opaque constructor, does not see the
413−-- constructor application, so it behaves like an IORef and cannot be fused away.
414−--
415−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
416−-- the weak pointer to it in some cases.
417−
418−newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
419−newSubscriberHold h = return $ Subscriber
420− { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
421− , subscriberInvalidateHeight = \_ -> return ()
422− , subscriberRecalculateHeight = \_ -> return ()
423− }
424−
425−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
426−newSubscriberFan subscribed = return $ Subscriber
427− { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
428− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
429− tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
430− liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
431− scheduleClear $ fanSubscribedOccurrence subscribed
432− let f _ (Pair (Identity v) subsubs) = do
433− propagate v $ _fanSubscribedChildren_list subsubs
434− return $ Constant ()
435− _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
436− return ()
437− , subscriberInvalidateHeight = \old -> do
438− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
439− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
440− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
441− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
442− , subscriberRecalculateHeight = \new -> do
443− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
444− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
445− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
446− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
447− }
448−
449−newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
450−newSubscriberSwitch subscribed = return $ Subscriber
451− { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
452− tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
453− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
454− scheduleClear $ switchSubscribedOccurrence subscribed
455− propagate a $ switchSubscribedSubscribers subscribed
456− , subscriberInvalidateHeight = \_ -> do
457− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
458− oldHeight <- readIORef $ switchSubscribedHeight subscribed
459− when (oldHeight /= invalidHeight) $ do
460− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
461− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
462− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
463− , subscriberRecalculateHeight = \new -> do
464− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
465− updateSwitchHeight new subscribed
466− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
467− }
468−
469−newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
470−newSubscriberCoincidenceOuter subscribed = return $ Subscriber
471− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
472− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
473− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
474− tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
475− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
476− tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
477− tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
478− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
479− scheduleClear $ coincidenceSubscribedInnerParent subscribed
480− case occ of
481− Nothing -> do
482− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
483− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
484− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
485− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
486− Just o -> do -- Since it's already firing, no need to adjust height
487− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
488− scheduleClear $ coincidenceSubscribedOccurrence subscribed
489− propagate o $ coincidenceSubscribedSubscribers subscribed
490− , subscriberInvalidateHeight = \_ -> do
491− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
492− invalidateCoincidenceHeight subscribed
493− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
494− , subscriberRecalculateHeight = \_ -> do
495− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
496− recalculateCoincidenceHeight subscribed
497− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
498− }
499−
500−newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
501−newSubscriberCoincidenceInner subscribed = return $ Subscriber
502− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
503− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
504− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
505− case occ of
506− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
507− Nothing -> do
508− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
509− scheduleClear $ coincidenceSubscribedOccurrence subscribed
510− propagate a $ coincidenceSubscribedSubscribers subscribed
511− , subscriberInvalidateHeight = \_ -> do
512− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
513− invalidateCoincidenceHeight subscribed
514− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
515− , subscriberRecalculateHeight = \_ -> do
516− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
517− recalculateCoincidenceHeight subscribed
518− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
519− }
520−
521−invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
522−invalidateSubscriberHeight = flip subscriberInvalidateHeight
523−
524−recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
525−recalculateSubscriberHeight = flip subscriberRecalculateHeight
526−
527−-- | Propagate everything at the current height
528−propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
529−propagate a subscribers = withIncreasedDepth $ do
530− -- 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
531− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
532− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
533−
534−-- | Propagate everything at the current height
535−propagateFast :: a -> FastWeakBag (Subscriber x a) -> EventM x ()
536−propagateFast a subscribers = withIncreasedDepth $ do
537− -- 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
538− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
539− FastWeakBag.traverse subscribers $ \s -> subscriberPropagate s a
540−
541−--------------------------------------------------------------------------------
542−-- EventSubscribed
543−--------------------------------------------------------------------------------
544−
545−toAny :: a -> Any
546−toAny = unsafeCoerce
547−
548−data EventSubscribed x = EventSubscribed
549− { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
550− , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
551−#ifdef DEBUG_CYCLES
552− , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
553− , eventSubscribedHasOwnHeightRef :: !Bool
554− , eventSubscribedWhoCreated :: !(IO [String])
555−#endif
556− }
557−
558−eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
559−eventSubscribedRoot !r = EventSubscribed
560− { eventSubscribedHeightRef = zeroRef
561− , eventSubscribedRetained = toAny r
562−#ifdef DEBUG_CYCLES
563− , eventSubscribedGetParents = return []
564− , eventSubscribedHasOwnHeightRef = False
565− , eventSubscribedWhoCreated = return ["root"]
566−#endif
567− }
568−
569−eventSubscribedNever :: EventSubscribed x
570−eventSubscribedNever = EventSubscribed
571− { eventSubscribedHeightRef = zeroRef
572− , eventSubscribedRetained = toAny ()
573−#ifdef DEBUG_CYCLES
574− , eventSubscribedGetParents = return []
575− , eventSubscribedHasOwnHeightRef = False
576− , eventSubscribedWhoCreated = return ["never"]
577−#endif
578− }
579−
580−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
581−eventSubscribedFan !subscribed = EventSubscribed
582− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
583− , eventSubscribedRetained = toAny subscribed
584−#ifdef DEBUG_CYCLES
585− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
586− , eventSubscribedHasOwnHeightRef = False
587− , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
588−#endif
589− }
590−
591−eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
592−eventSubscribedSwitch !subscribed = EventSubscribed
593− { eventSubscribedHeightRef = switchSubscribedHeight subscribed
594− , eventSubscribedRetained = toAny subscribed
595−#ifdef DEBUG_CYCLES
596− , eventSubscribedGetParents = do
597− s <- readIORef $ switchSubscribedCurrentParent subscribed
598− return [Some.This $ _eventSubscription_subscribed s]
599− , eventSubscribedHasOwnHeightRef = True
600− , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
601−#endif
602− }
603−
604−eventSubscribedCoincidence :: CoincidenceSubscribed x a -> EventSubscribed x
605−eventSubscribedCoincidence !subscribed = EventSubscribed
606− { eventSubscribedHeightRef = coincidenceSubscribedHeight subscribed
607− , eventSubscribedRetained = toAny subscribed
608−#ifdef DEBUG_CYCLES
609− , eventSubscribedGetParents = do
610− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
611− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
612− innerParents = maybeToList $ fmap Some.This innerSubscription
613− return $ outerParent : innerParents
614− , eventSubscribedHasOwnHeightRef = True
615− , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
616−#endif
617− }
618−
619−getEventSubscribedHeight :: EventSubscribed x -> IO Height
620−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
621−
622−#ifdef DEBUG_CYCLES
623−whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
624−whoCreatedEventSubscribed = eventSubscribedWhoCreated
625−
626−walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
627−walkInvalidHeightParents s0 = do
628− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
629− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
630− when (h == invalidHeight) $ do
631− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
632− modify (Some.This s :)
633− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
634− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
635− return subscribers
636−#endif
637−
638−{-# INLINE subscribeHoldEvent #-}
639−subscribeHoldEvent :: Hold x p -> Subscriber x p -> EventM x (EventSubscription x, Maybe p)
640−subscribeHoldEvent = subscribeAndRead . holdEvent
641−
642−--------------------------------------------------------------------------------
643−-- Behavior
644−--------------------------------------------------------------------------------
645−
646−newtype Behavior x a = Behavior { readBehaviorTracked :: BehaviorM x a }
647−
648−behaviorHold :: Hold x p -> Behavior x (PatchTarget p)
649−behaviorHold !h = Behavior $ readHoldTracked h
650−
651−behaviorHoldIdentity :: Hold x (Identity a) -> Behavior x a
652−behaviorHoldIdentity = behaviorHold
653−
654−behaviorConst :: a -> Behavior x a
655−behaviorConst !a = Behavior $ return a
656−
657−behaviorPull :: Pull x a -> Behavior x a
658−behaviorPull !p = Behavior $ do
659− val <- liftIO $ readIORef $ pullValue p
660− case val of
661− Just subscribed -> do
662− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
663− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
664− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
665− return $ pullSubscribedValue subscribed
666− Nothing -> do
667− i <- liftIO $ newInvalidatorPull p
668− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
669− parentsRef <- liftIO $ newIORef []
670− holdInits <- askBehaviorHoldInits
671− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
672− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
673− parents <- liftIO $ readIORef parentsRef
674− let subscribed = PullSubscribed
675− { pullSubscribedValue = a
676− , pullSubscribedInvalidators = invsRef
677− , pullSubscribedOwnInvalidator = i
678− , pullSubscribedParents = parents
679− }
680− liftIO $ writeIORef (pullValue p) $ Just subscribed
681− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
682− return a
683−
684−behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
685−behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
686−
687−{-# INLINE readHoldTracked #-}
688−readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
689−readHoldTracked h = do
690− result <- liftIO $ readIORef $ holdValue h
691− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
692− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
693− liftIO $ touch h -- Otherwise, if this gets inlined enough, the hold's parent reference may get collected
694− return result
695−
696−{-# INLINABLE readBehaviorUntracked #-}
697−readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
698−readBehaviorUntracked b = do
699− holdInits <- getDeferralQueue
700− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
701−
702−--------------------------------------------------------------------------------
703−-- Dynamic
704−--------------------------------------------------------------------------------
705−
706−data Dynamic x p = Dynamic
707− { dynamicCurrent :: !(Behavior x (PatchTarget p))
708− , dynamicUpdated :: !(Event x p)
709− }
710−
711−dynamicHold :: Hold x p -> Dynamic x p
712−dynamicHold !h = Dynamic
713− { dynamicCurrent = behaviorHold h
714− , dynamicUpdated = eventHold h
715− }
716−
717−dynamicHoldIdentity :: Hold x (Identity a) -> Dynamic x (Identity a)
718−dynamicHoldIdentity = dynamicHold
719−
720−dynamicConst :: PatchTarget p -> Dynamic x p
721−dynamicConst !a = Dynamic
722− { dynamicCurrent = behaviorConst a
723− , dynamicUpdated = eventNever
724− }
725−
726−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
727−dynamicDyn !d = Dynamic
728− { dynamicCurrent = behaviorDyn d
729− , dynamicUpdated = eventDyn d
730− }
731−
732−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
733−dynamicDynIdentity = dynamicDyn
734−
735−--------------------------------------------------------------------------------
736−-- Combinators
737−--------------------------------------------------------------------------------
738−
739−--TODO: Figure out why certain things are not 'representational', then make them
740−--representational so we can use coerce
741−
742−--type role Hold representational
743−data Hold x p
744− = Hold { holdValue :: !(IORef (PatchTarget p))
745− , holdInvalidators :: !(IORef [Weak (Invalidator x)])
746− , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
747− , holdParent :: !(IORef (Maybe (EventSubscription x))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
748−#ifdef DEBUG_NODEIDS
749− , holdNodeId :: Int
750−#endif
751− }
752−
753−-- | A statically allocated 'SpiderTimeline'
754−data Global
755−
756−{-# NOINLINE globalSpiderTimelineEnv #-}
757−globalSpiderTimelineEnv :: SpiderTimelineEnv Global
758−globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
759−
760−-- | Stores all global data relevant to a particular Spider timeline; only one
761−-- value should exist for each type @x@
762−data SpiderTimelineEnv x = SpiderTimelineEnv
763− { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
764− , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
765−#ifdef DEBUG
766− , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
767−#endif
768− }
769−type role SpiderTimelineEnv nominal
770−
771−instance Eq (SpiderTimelineEnv x) where
772− _ == _ = True -- Since only one exists of each type
773−
774−instance GEq SpiderTimelineEnv where
775− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
776− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
777− else Nothing
778−
779−data EventEnv x
780− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
781− , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
782− , eventEnvDynInits :: !(IORef [SomeDynInit x])
783− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
784− , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
785− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
786− , eventEnvIntClears :: !(IORef [SomeIntClear])
787− , eventEnvRootClears :: !(IORef [SomeRootClear])
788− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
789− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
790− , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
791− }
792−
793−{-# INLINE runEventM #-}
794−runEventM :: EventM x a -> IO a
795−runEventM = unEventM
796−
797−asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
798−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
799−
800−class MonadIO m => Defer a m where
801− getDeferralQueue :: m (IORef [a])
802−
803−{-# INLINE defer #-}
804−defer :: Defer a m => a -> m ()
805−defer a = do
806− q <- getDeferralQueue
807− liftIO $ modifyIORef' q (a:)
808−
809−instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
810− {-# INLINE getDeferralQueue #-}
811− getDeferralQueue = asksEventEnv eventEnvAssignments
812−
813−instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
814− {-# INLINE getDeferralQueue #-}
815− getDeferralQueue = asksEventEnv eventEnvHoldInits
816−
817−instance HasSpiderTimeline x => Defer (SomeDynInit x) (EventM x) where
818− {-# INLINE getDeferralQueue #-}
819− getDeferralQueue = asksEventEnv eventEnvDynInits
820−
821−instance Defer (SomeHoldInit x) (BehaviorM x) where
822− {-# INLINE getDeferralQueue #-}
823− getDeferralQueue = BehaviorM $ asks snd
824−
825−instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
826− {-# INLINE getDeferralQueue #-}
827− getDeferralQueue = asksEventEnv eventEnvMergeUpdates
828−
829−instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
830− {-# INLINE getDeferralQueue #-}
831− getDeferralQueue = asksEventEnv eventEnvMergeInits
832−
833−class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
834− getCurrentHeight :: m Height
835− scheduleMerge :: Height -> EventM x () -> m ()
836−
837−instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
838− {-# INLINE getCurrentHeight #-}
839− getCurrentHeight = do
840− heightRef <- asksEventEnv eventEnvCurrentHeight
841− liftIO $ readIORef heightRef
842− {-# INLINE scheduleMerge #-}
843− scheduleMerge height subscribed = do
844− delayedRef <- asksEventEnv eventEnvDelayedMerges
845− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
846−
847−class HasSpiderTimeline x where
848− -- | Retrieve the current SpiderTimelineEnv
849− spiderTimeline :: SpiderTimelineEnv x
850−
851−instance HasSpiderTimeline Global where
852− spiderTimeline = globalSpiderTimelineEnv
853−
854−putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
855−putCurrentHeight h = do
856− heightRef <- asksEventEnv eventEnvCurrentHeight
857− liftIO $ writeIORef heightRef $! h
858−
859−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
860− {-# INLINE getDeferralQueue #-}
861− getDeferralQueue = asksEventEnv eventEnvClears
862−
863−{-# INLINE scheduleClear #-}
864−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
865−scheduleClear r = defer $ SomeClear r
866−
867−instance HasSpiderTimeline x => Defer SomeIntClear (EventM x) where
868− {-# INLINE getDeferralQueue #-}
869− getDeferralQueue = asksEventEnv eventEnvIntClears
870−
871−{-# INLINE scheduleIntClear #-}
872−scheduleIntClear :: Defer SomeIntClear m => IORef (IntMap a) -> m ()
873−scheduleIntClear r = defer $ SomeIntClear r
874−
875−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
876− {-# INLINE getDeferralQueue #-}
877− getDeferralQueue = asksEventEnv eventEnvRootClears
878−
879−{-# INLINE scheduleRootClear #-}
880−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
881−scheduleRootClear r = defer $ SomeRootClear r
882−
883−instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
884− {-# INLINE getDeferralQueue #-}
885− getDeferralQueue = asksEventEnv eventEnvResetCoincidences
886−
887−-- Note: hold cannot examine its event until after the phase is over
888−{-# INLINE [1] hold #-}
889−hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
890−hold v0 e = do
891− valRef <- liftIO $ newIORef v0
892− invsRef <- liftIO $ newIORef []
893− parentRef <- liftIO $ newIORef Nothing
894−#ifdef DEBUG_NODEIDS
895− nodeId <- liftIO newNodeId
896−#endif
897− let h = Hold
898− { holdValue = valRef
899− , holdInvalidators = invsRef
900− , holdEvent = e
901− , holdParent = parentRef
902−#ifdef DEBUG_NODEIDS
903− , holdNodeId = nodeId
904−#endif
905− }
906− defer $ SomeHoldInit h
907− return h
908−
909−{-# INLINE getHoldEventSubscription #-}
910−getHoldEventSubscription :: forall p x. (HasSpiderTimeline x, Patch p) => Hold x p -> EventM x (EventSubscription x)
911−getHoldEventSubscription h = do
912− ep <- liftIO $ readIORef $ holdParent h
913− case ep of
914− Just subd -> return subd
915− Nothing -> do
916− let e = holdEvent h
917− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
918− (subscription@(EventSubscription _ _), occ) <- subscribeAndRead e =<< liftIO (newSubscriberHold h)
919− liftIO $ writeIORef subscriptionRef $! subscription
920− case occ of
921− Nothing -> return ()
922− Just o -> do
923− old <- liftIO $ readIORef $ holdValue h
924− case apply o old of
925− Nothing -> return ()
926− Just new -> do
927− -- Need to evaluate these so that we don't retain the Hold itself
928− v <- liftIO $ evaluate $ holdValue h
929− i <- liftIO $ evaluate $ holdInvalidators h
930− defer $ SomeAssignment v i new
931− liftIO $ writeIORef (holdParent h) $ Just subscription
932− return subscription
933−
934−type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
935−
936−--type role BehaviorM representational
937−-- BehaviorM can sample behaviors
938−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
939−
940−instance Monad (BehaviorM x) where
941− {-# INLINE (>>=) #-}
942− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
943− {-# INLINE (>>) #-}
944− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
945− {-# INLINE return #-}
946− return x = BehaviorM $ return x
947− {-# INLINE fail #-}
948− fail s = BehaviorM $ fail s
949−
950−data BehaviorSubscribed x a
951− = forall p. BehaviorSubscribedHold (Hold x p)
952− | BehaviorSubscribedPull (PullSubscribed x a)
953−
954−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
955−
956−--type role PullSubscribed representational
957−data PullSubscribed x a
958− = PullSubscribed { pullSubscribedValue :: !a
959− , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
960− , pullSubscribedOwnInvalidator :: !(Invalidator x)
961− , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
962− }
963−
964−--type role Pull representational
965−data Pull x a
966− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
967− , pullCompute :: !(BehaviorM x a)
968−#ifdef DEBUG_NODEIDS
969− , pullNodeId :: Int
970−#endif
971− }
972−
973−data Invalidator x
974− = forall a. InvalidatorPull (Pull x a)
975− | forall a. InvalidatorSwitch (SwitchSubscribed x a)
976−
977−data RootSubscribed x a = forall k. GCompare k => RootSubscribed
978− { rootSubscribedKey :: !(k a)
979− , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
980− , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
981− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
982− , rootSubscribedUninit :: IO ()
983− , 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?
984−#ifdef DEBUG_NODEIDS
985− , rootSubscribedNodeId :: Int
986−#endif
987− }
988−
989−data Root x (k :: * -> *)
990− = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
991− , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
992− , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
993− }
994−
995−data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
996−
997−data SomeDynInit x = forall p. Patch p => SomeDynInit !(Dyn x p)
998−
999−data SomeMergeUpdate x = SomeMergeUpdate
1000− { _someMergeUpdate_update :: !(EventM x [EventSubscription x])
1001− , _someMergeUpdate_invalidateHeight :: !(IO ())
1002− , _someMergeUpdate_recalculateHeight :: !(IO ())
1003− }
1004−
1005−newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
1006−
1007−-- EventM can do everything BehaviorM can, plus create holds
1008−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
1009−
1010−newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
1011−
1012−data MergeSubscribedParentWithMove x k a = MergeSubscribedParentWithMove
1013− { _mergeSubscribedParentWithMove_subscription :: !(EventSubscription x)
1014− , _mergeSubscribedParentWithMove_key :: !(IORef (k a))
1015− }
1016−
1017−data HeightBag = HeightBag
1018− { _heightBag_size :: {-# UNPACK #-} !Int
1019− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
1020− }
1021− deriving (Show, Read, Eq, Ord)
1022−
1023−heightBagEmpty :: HeightBag
1024−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
1025−
1026−heightBagSize :: HeightBag -> Int
1027−heightBagSize = _heightBag_size
1028−
1029−heightBagFromList :: [Height] -> HeightBag
1030−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
1031−
1032−heightBagAdd :: Height -> HeightBag -> HeightBag
1033−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
1034−
1035−heightBagRemove :: Height -> HeightBag -> HeightBag
1036−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
1037− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
1038− Just old -> HeightBag (pred s) $ case old of
1039− 0 -> IntMap.delete h c
1040− _ -> IntMap.insert h (pred old) c
1041−
1042−heightBagMax :: HeightBag -> Height
1043−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
1044− Just ((h, _), _) -> Height h
1045− Nothing -> zeroHeight
1046−
1047−heightBagVerify :: HeightBag -> HeightBag
1048−#ifdef DEBUG
1049−heightBagVerify b@(HeightBag s c) = if
1050− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
1051− -> error $ "heightBagVerify: size doesn't match: " <> show b
1052− | unHeight invalidHeight `IntMap.member` c
1053− -> error $ "heightBagVerify: contains invalid height: " <> show b
1054− | otherwise -> b
1055−#else
1056−heightBagVerify = id
1057−#endif
1058−
1059−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
1060− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
1061− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
1062− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
1063− }
1064−
1065−data FanSubscribed (x :: *) k
1066− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1067− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
1068− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
1069− , fanSubscribedParent :: !(EventSubscription x)
1070−#ifdef DEBUG_NODEIDS
1071− , fanSubscribedNodeId :: Int
1072−#endif
1073− }
1074−
1075−data Fan x k
1076− = Fan { fanParent :: !(Event x (DMap k Identity))
1077− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
1078− }
1079−
1080−data SwitchSubscribed x a
1081− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1082− , switchSubscribedOccurrence :: !(IORef (Maybe a))
1083− , switchSubscribedHeight :: !(IORef Height)
1084− , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1085− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
1086− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
1087− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
1088− , switchSubscribedParent :: !(Behavior x (Event x a))
1089− , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
1090− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
1091−#ifdef DEBUG_NODEIDS
1092− , switchSubscribedNodeId :: Int
1093−#endif
1094− }
1095−
1096−data Switch x a
1097− = Switch { switchParent :: !(Behavior x (Event x a))
1098− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1099− }
1100−
1101−#ifdef USE_TEMPLATE_HASKELL
1102−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1103−#endif
1104−data CoincidenceSubscribed x a
1105− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1106− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1107− , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1108− , coincidenceSubscribedHeight :: !(IORef Height)
1109− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
1110− , coincidenceSubscribedOuterParent :: !(EventSubscription x)
1111− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
1112− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
1113−#ifdef DEBUG_NODEIDS
1114− , coincidenceSubscribedNodeId :: Int
1115−#endif
1116− }
1117−
1118−data Coincidence x a
1119− = Coincidence { coincidenceParent :: !(Event x (Event x a))
1120− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1121− }
1122−
1123−{-# NOINLINE newInvalidatorSwitch #-}
1124−newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
1125−newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1126−
1127−{-# NOINLINE newInvalidatorPull #-}
1128−newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1129−newInvalidatorPull p = return $! InvalidatorPull p
1130−
1131−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1132− fmapMaybe f = push $ return . f
1133−
1134−instance HasSpiderTimeline x => Align (Event x) where
1135− nil = eventNever
1136− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromDistinctAscList [LeftTag :=> ea, RightTag :=> eb]
1137−
1138−data DynType x p = UnsafeDyn !(BehaviorM x (PatchTarget p), Event x p)
1139− | BuildDyn !(EventM x (PatchTarget p), Event x p)
1140− | HoldDyn !(Hold x p)
1141−
1142−newtype Dyn x p = Dyn { unDyn :: IORef (DynType x p) }
1143−
1144−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1145−newMapDyn f d = dynamicDynIdentity $ unsafeBuildDynamic (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1146−
1147−--TODO: Avoid the duplication between this and R.zipDynWith
1148−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1149−zipDynWith f da db =
1150− let eab = align (dynamicUpdated da) (dynamicUpdated db)
1151− ec = flip push eab $ \o -> do
1152− (a, b) <- case o of
1153− This (Identity a) -> do
1154− b <- readBehaviorUntracked $ dynamicCurrent db
1155− return (a, b)
1156− That (Identity b) -> do
1157− a <- readBehaviorUntracked $ dynamicCurrent da
1158− return (a, b)
1159− These (Identity a) (Identity b) -> return (a, b)
1160− return $ Just $ Identity $ f a b
1161− in dynamicDynIdentity $ unsafeBuildDynamic (f <$> readBehaviorUntracked (dynamicCurrent da) <*> readBehaviorUntracked (dynamicCurrent db)) ec
1162−
1163−buildDynamic :: (Defer (SomeDynInit x) m, Patch p) => EventM x (PatchTarget p) -> Event x p -> m (Dyn x p)
1164−buildDynamic readV0 v' = do
1165− result <- liftIO $ newIORef $ BuildDyn (readV0, v')
1166− let !d = Dyn result
1167− defer $ SomeDynInit d
1168− return d
1169−
1170−unsafeBuildDynamic :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1171−unsafeBuildDynamic readV0 v' = Dyn $ unsafeNewIORef x $ UnsafeDyn x
1172− where x = (readV0, v')
1173−
1174−-- ResultM can read behaviors and events
1175−type ResultM = EventM
1176−
1177−{-# NOINLINE unsafeNewIORef #-}
1178−unsafeNewIORef :: a -> b -> IORef b
1179−unsafeNewIORef a b = unsafePerformIO $ do
1180− touch a
1181− newIORef b
1182−
1183−instance HasSpiderTimeline x => Functor (Event x) where
1184− fmap f = push $ return . Just . f
1185−
1186−instance Functor (Behavior x) where
1187− fmap f = pull . fmap f . readBehaviorTracked
1188−
1189−{-# INLINE push #-}
1190−push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
1191−push f e = cacheEvent (pushCheap f e)
1192−
1193−{-# INLINABLE pull #-}
1194−pull :: BehaviorM x a -> Behavior x a
1195−pull a = behaviorPull $ Pull
1196− { pullCompute = a
1197− , pullValue = unsafeNewIORef a Nothing
1198−#ifdef DEBUG_NODEIDS
1199− , pullNodeId = unsafeNodeId a
1200−#endif
1201− }
1202−
1203−{-# INLINABLE switch #-}
1204−switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1205−switch a = eventSwitch $ Switch
1206− { switchParent = a
1207− , switchSubscribed = unsafeNewIORef a Nothing
1208− }
1209−
1210−coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1211−coincidence a = eventCoincidence $ Coincidence
1212− { coincidenceParent = a
1213− , coincidenceSubscribed = unsafeNewIORef a Nothing
1214− }
1215−
1216−-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1217−run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1218−run roots after = do
1219− tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1220− let t = spiderTimeline :: SpiderTimelineEnv x
1221− result <- SpiderHost $ withMVar (_spiderTimeline_lock t) $ \_ -> unSpiderHost $ runFrame $ do
1222− rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1223− occBefore <- liftIO $ do
1224− occBefore <- readIORef occRef
1225− writeIORef occRef $! DMap.insert k a occBefore
1226− return occBefore
1227− if DMap.null occBefore
1228− then do scheduleRootClear occRef
1229− return $ Just r
1230− else return Nothing
1231− forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1232− propagate a subscribersRef
1233− delayedRef <- asksEventEnv eventEnvDelayedMerges
1234− let go = do
1235− delayed <- liftIO $ readIORef delayedRef
1236− case IntMap.minViewWithKey delayed of
1237− Nothing -> return ()
1238− Just ((currentHeight, cur), future) -> do
1239− tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
1240− putCurrentHeight $ Height currentHeight
1241− liftIO $ writeIORef delayedRef $! future
1242− sequence_ cur
1243− go
1244− go
1245− putCurrentHeight maxBound
1246− after
1247− tracePropagate (Proxy :: Proxy x) "Done running an event frame"
1248− return result
1249−
1250−scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
1251−scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1252− height <- liftIO $ readIORef heightRef
1253− currentHeight <- getCurrentHeight
1254− case height `compare` currentHeight of
1255− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
1256− GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1257− EQ -> a
1258−
1259−data SomeClear = forall a. SomeClear {-# UNPACK #-} !(IORef (Maybe a))
1260−
1261−data SomeIntClear = forall a. SomeIntClear {-# UNPACK #-} !(IORef (IntMap a))
1262−
1263−data SomeRootClear = forall k. SomeRootClear {-# UNPACK #-} !(IORef (DMap k Identity))
1264−
1265−data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1266−
1267−debugFinalize :: Bool
1268−debugFinalize = False
1269−
1270−mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1271−mkWeakPtrWithDebug x debugNote = do
1272− x' <- evaluate x
1273− mkWeakPtr x' $
1274− if debugFinalize
1275− then Just $ putStrLn $ "finalizing: " ++ debugNote
1276− else Nothing
1277−
1278−type WeakList a = [Weak a]
1279−
1280−{-# INLINE withIncreasedDepth #-}
1281−#ifdef DEBUG
1282−withIncreasedDepth :: CanTrace x m => m a -> m a
1283−withIncreasedDepth a = do
1284− spiderTimeline <- askSpiderTimelineEnv
1285− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1286− result <- a
1287− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1288− return result
1289−#else
1290−withIncreasedDepth :: m a -> m a
1291−withIncreasedDepth = id
1292−#endif
1293−
1294−type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
1295−
1296−{-# INLINE tracePropagate #-}
1297−tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1298−tracePropagate p = traceWhen p debugPropagate
1299−
1300−{-# INLINE traceInvalidate #-}
1301−traceInvalidate :: String -> IO ()
1302−traceInvalidate = when debugInvalidate . liftIO . putStrLn
1303−
1304−{-# INLINE traceWhen #-}
1305−traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1306−traceWhen p b message = traceMWhen p b $ return message
1307−
1308−{-# INLINE traceMWhen #-}
1309−traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1310−traceMWhen _ b getMessage = when b $ do
1311− message <- getMessage
1312−#ifdef DEBUG
1313− spiderTimeline <- askSpiderTimelineEnv
1314− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1315−#else
1316− let d = 0
1317−#endif
1318− liftIO $ putStrLn $ replicate d ' ' <> message
1319−
1320−whoCreatedIORef :: IORef a -> IO [String]
1321−whoCreatedIORef (IORef a) = whoCreated $! a
1322−
1323−#ifdef DEBUG_CYCLES
1324−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1325−groupByHead = \case
1326− [] -> []
1327− (x :| xs) : t -> case groupByHead t of
1328− [] -> [(x, xs :| [])]
1329− l@((y, yss) : t')
1330− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
1331− | otherwise -> (x, xs :| []) : l
1332−
1333−listsToForest :: Eq a => [[a]] -> Forest a
1334−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1335−#endif
1336−
1337−{-# INLINE propagateSubscriberHold #-}
1338−propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
1339−propagateSubscriberHold h a = do
1340− {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
1341− invalidators <- liftIO $ readIORef $ holdInvalidators h
1342− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1343− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1344− case {-# SCC "apply" #-} apply a v of
1345− Nothing -> return ()
1346− Just v' -> do
1347− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1348− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1349− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1350− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1351−
1352−data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1353−
1354−runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1355−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1356−
1357−askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1358−askInvalidator = do
1359− (!m, _) <- BehaviorM ask
1360− case m of
1361− Nothing -> return Nothing
1362− Just (!wi, _) -> return $ Just wi
1363−
1364−askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1365−askParentsRef = do
1366− (!m, _) <- BehaviorM ask
1367− case m of
1368− Nothing -> return Nothing
1369− Just (_, !p) -> return $ Just p
1370−
1371−askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1372−askBehaviorHoldInits = do
1373− (_, !his) <- BehaviorM ask
1374− return his
1375−
1376−{-# INLINE getDynHold #-}
1377−getDynHold :: (Defer (SomeHoldInit x) m, Patch p) => Dyn x p -> m (Hold x p)
1378−getDynHold d = do
1379− mh <- liftIO $ readIORef $ unDyn d
1380− case mh of
1381− HoldDyn h -> return h
1382− UnsafeDyn (readV0, v') -> do
1383− holdInits <- getDeferralQueue
1384− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1385− hold' v0 v'
1386− BuildDyn (readV0, v') -> do
1387− v0 <- liftIO $ runEventM readV0
1388− hold' v0 v'
1389− where
1390− hold' v0 v' = do
1391− h <- hold v0 v'
1392− liftIO $ writeIORef (unDyn d) $ HoldDyn h
1393− return h
1394−
1395−
1396−-- Always refers to 0
1397−{-# NOINLINE zeroRef #-}
1398−zeroRef :: IORef Height
1399−zeroRef = unsafePerformIO $ newIORef zeroHeight
1400−
1401−getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
1402−getRootSubscribed k r sub = do
1403− mSubscribed <- readIORef $ rootSubscribed r
1404− let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1405− case DMap.lookup k mSubscribed of
1406− Just subscribed -> {-# SCC "hitRoot" #-} do
1407− sln <- subscribeRootSubscribed subscribed sub
1408− occ <- getOcc
1409− return (sln, subscribed, occ)
1410− Nothing -> {-# SCC "missRoot" #-} do
1411− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1412− let !cached = rootSubscribed r
1413− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1414− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1415− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1416− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1417− writeIORef uninitRef $! uninit
1418− let !subscribed = RootSubscribed
1419− { rootSubscribedKey = k
1420− , rootSubscribedCachedSubscribed = cached
1421− , rootSubscribedOccurrence = getOcc
1422− , rootSubscribedSubscribers = subs
1423− , rootSubscribedUninit = uninit
1424− , rootSubscribedWeakSelf = weakSelf
1425−#ifdef DEBUG_NODEIDS
1426− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1427−#endif
1428− }
1429− -- If we die at the same moment that all our children die, they will
1430− -- try to clean us up but will fail because their Weak reference to us
1431− -- will also be dead. So, if we are dying, check if there are any
1432− -- children; since children don't bother cleaning themselves up if
1433− -- their parents are already dead, I don't think there's a race
1434− -- condition here. However, if there are any children, then we can
1435− -- infer that we need to clean ourselves up, so we do.
1436− finalCleanup = do
1437− cs <- readIORef $ _weakBag_children subs
1438− when (not $ IntMap.null cs) (cleanupRootSubscribed subscribed)
1439− writeIORef weakSelf =<< evaluate =<< mkWeakPtr subscribed (Just finalCleanup)
1440− 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
1441− occ <- getOcc
1442− return (sln, subscribed, occ)
1443−
1444−#ifdef USE_TEMPLATE_HASKELL
1445−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1446−#endif
1447−cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1448−cleanupRootSubscribed self@RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached } = do
1449− rootSubscribedUninit self
1450− modifyIORef' cached $ DMap.delete k
1451−
1452−{-# INLINE subscribeRootSubscribed #-}
1453−subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1454−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1455−
1456−newtype EventSelectorInt x a = EventSelectorInt { selectInt :: Int -> Event x a }
1457−
1458−data FanInt x a = FanInt
1459− { _fanInt_subscribers :: {-# UNPACK #-} !(FastMutableIntMap (FastWeakBag (Subscriber x a))) --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1460− , _fanInt_subscriptionRef :: {-# UNPACK #-} !(IORef (EventSubscription x)) -- This should have a valid subscription iff subscribers is non-empty
1461− , _fanInt_occRef :: {-# UNPACK #-} !(IORef (IntMap a))
1462− }
1463−
1464−newFanInt :: IO (FanInt x a)
1465−newFanInt = do
1466− subscribers <- FastMutableIntMap.newEmpty --TODO: Clean up the keys in here when their child weak bags get empty --TODO: Remove our own subscription when the subscribers list is completely empty
1467− subscriptionRef <- newIORef $ error "fanInt: no subscription"
1468− occRef <- newIORef $ error "fanInt: no occurrence"
1469− return $ FanInt
1470− { _fanInt_subscribers = subscribers
1471− , _fanInt_subscriptionRef = subscriptionRef
1472− , _fanInt_occRef = occRef
1473− }
1474−
1475−{-# NOINLINE unsafeNewFanInt #-}
1476−unsafeNewFanInt :: b -> FanInt x a
1477−unsafeNewFanInt b = unsafePerformIO $ do
1478− touch b
1479− newFanInt
1480−
1481−fanInt :: HasSpiderTimeline x => Event x (IntMap a) -> EventSelectorInt x a
1482−fanInt p =
1483− let self = unsafeNewFanInt p
1484− in EventSelectorInt $ \k -> Event $ \sub -> do
1485− isEmpty <- liftIO $ FastMutableIntMap.isEmpty (_fanInt_subscribers self)
1486− when isEmpty $ do -- This is the first subscriber, so we need to subscribe to our input
1487− (subscription, parentOcc) <- subscribeAndRead p $ Subscriber
1488− { subscriberPropagate = \m -> do
1489− liftIO $ writeIORef (_fanInt_occRef self) m
1490− scheduleIntClear $ _fanInt_occRef self
1491− FastMutableIntMap.forIntersectionWithImmutable_ (_fanInt_subscribers self) m $ \b v -> do --TODO: Do we need to know that no subscribers are being added as we traverse?
1492− FastWeakBag.traverse b $ \s -> do
1493− subscriberPropagate s v
1494− , subscriberInvalidateHeight = \old -> do
1495− FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1496− FastWeakBag.traverse b $ \s -> do
1497− subscriberInvalidateHeight s old
1498− , subscriberRecalculateHeight = \new -> do
1499− FastMutableIntMap.for_ (_fanInt_subscribers self) $ \b -> do
1500− FastWeakBag.traverse b $ \s -> do
1501− subscriberRecalculateHeight s new
1502− }
1503− liftIO $ do
1504− writeIORef (_fanInt_subscriptionRef self) subscription
1505− writeIORef (_fanInt_occRef self) $ fromMaybe IntMap.empty parentOcc
1506− scheduleIntClear $ _fanInt_occRef self
1507− liftIO $ do
1508− b <- FastMutableIntMap.lookup (_fanInt_subscribers self) k >>= \case
1509− Nothing -> do
1510− b <- FastWeakBag.empty
1511− FastMutableIntMap.insert (_fanInt_subscribers self) k b
1512− return b
1513− Just b -> return b
1514− t <- liftIO $ FastWeakBag.insert sub b
1515− currentOcc <- readIORef (_fanInt_occRef self)
1516− (EventSubscription _ (EventSubscribed !heightRef _)) <- readIORef (_fanInt_subscriptionRef self)
1517− return (EventSubscription (FastWeakBag.remove t) $! EventSubscribed heightRef $! toAny (_fanInt_subscriptionRef self, t), IntMap.lookup k currentOcc)
1518−
1519−{-# INLINABLE getFanSubscribed #-}
1520−getFanSubscribed :: (HasSpiderTimeline x, GCompare k) => k a -> Fan x k -> Subscriber x a -> EventM x (WeakBagTicket, FanSubscribed x k, Maybe a)
1521−getFanSubscribed k f sub = do
1522− mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1523− case mSubscribed of
1524− Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1525− sln <- subscribeFanSubscribed k subscribed sub
1526− occ <- readIORef $ fanSubscribedOccurrence subscribed
1527− return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
1528− Nothing -> {-# SCC "missFan" #-} do
1529− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1530− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1531− s <- liftIO $ newSubscriberFan subscribedUnsafe
1532− (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
1533− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1534− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1535− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1536− occRef <- liftIO $ newIORef parentOcc
1537− when (isJust parentOcc) $ scheduleClear occRef
1538− let subscribed = FanSubscribed
1539− { fanSubscribedCachedSubscribed = fanSubscribed f
1540− , fanSubscribedOccurrence = occRef
1541− , fanSubscribedParent = subscription
1542− , fanSubscribedSubscribers = subscribersRef
1543−#ifdef DEBUG_NODEIDS
1544− , fanSubscribedNodeId = unsafeNodeId f
1545−#endif
1546− }
1547− let !self = (k, subscribed)
1548− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1549− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1550− liftIO $ writeIORef subscribedRef $! subscribed
1551− liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1552− return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1553−
1554−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1555−cleanupFanSubscribed (k, subscribed) = do
1556− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1557− let reducedSubscribers = DMap.delete k subscribers
1558− if DMap.null reducedSubscribers
1559− then do
1560− unsubscribe $ fanSubscribedParent subscribed
1561− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1562− writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1563− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1564−
1565−{-# INLINE subscribeFanSubscribed #-}
1566−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1567−subscribeFanSubscribed k subscribed sub = do
1568− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1569− case DMap.lookup k subscribers of
1570− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1571− let !self = (k, subscribed)
1572− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1573− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1574− 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
1575− return sln
1576− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1577−
1578−{-# INLINABLE getSwitchSubscribed #-}
1579−getSwitchSubscribed :: HasSpiderTimeline x => Switch x a -> Subscriber x a -> EventM x (WeakBagTicket, SwitchSubscribed x a, Maybe a)
1580−getSwitchSubscribed s sub = do
1581− mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1582− case mSubscribed of
1583− Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1584− sln <- subscribeSwitchSubscribed subscribed sub
1585− occ <- readIORef $ switchSubscribedOccurrence subscribed
1586− return (sln, subscribed, occ)
1587− Nothing -> {-# SCC "missSwitch" #-} do
1588− subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1589− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1590− i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1591− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1592− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1593− wiRef <- liftIO $ newIORef wi
1594− parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1595− holdInits <- getDeferralQueue
1596− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1597− (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
1598− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1599− subscriptionRef <- liftIO $ newIORef subscription
1600− occRef <- liftIO $ newIORef parentOcc
1601− when (isJust parentOcc) $ scheduleClear occRef
1602− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1603− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1604− let !subscribed = SwitchSubscribed
1605− { switchSubscribedCachedSubscribed = switchSubscribed s
1606− , switchSubscribedOccurrence = occRef
1607− , switchSubscribedHeight = heightRef
1608− , switchSubscribedSubscribers = subs
1609− , switchSubscribedOwnInvalidator = i
1610− , switchSubscribedOwnWeakInvalidator = wiRef
1611− , switchSubscribedBehaviorParents = parentsRef
1612− , switchSubscribedParent = switchParent s
1613− , switchSubscribedCurrentParent = subscriptionRef
1614− , switchSubscribedWeakSelf = weakSelf
1615−#ifdef DEBUG_NODEIDS
1616− , switchSubscribedNodeId = unsafeNodeId s
1617−#endif
1618− }
1619− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1620− liftIO $ writeIORef subscribedRef $! subscribed
1621− liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1622− return (slnForSub, subscribed, parentOcc)
1623−
1624−cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
1625−cleanupSwitchSubscribed subscribed = do
1626− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1627− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1628− writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1629−
1630−{-# INLINE subscribeSwitchSubscribed #-}
1631−subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1632−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1633−
1634−{-# INLINABLE getCoincidenceSubscribed #-}
1635−getCoincidenceSubscribed :: forall x a. HasSpiderTimeline x => Coincidence x a -> Subscriber x a -> EventM x (WeakBagTicket, CoincidenceSubscribed x a, Maybe a)
1636−getCoincidenceSubscribed c sub = do
1637− mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1638− case mSubscribed of
1639− Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1640− sln <- subscribeCoincidenceSubscribed subscribed sub
1641− occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1642− return (sln, subscribed, occ)
1643− Nothing -> {-# SCC "missCoincidence" #-} do
1644− subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1645− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1646− subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1647− (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1648− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1649− (occ, height, mInnerSubd) <- case outerOcc of
1650− Nothing -> return (Nothing, outerHeight, Nothing)
1651− Just o -> do
1652− (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
1653− return (occ, height, Just innerSubd)
1654− occRef <- liftIO $ newIORef occ
1655− when (isJust occ) $ scheduleClear occRef
1656− heightRef <- liftIO $ newIORef height
1657− innerSubdRef <- liftIO $ newIORef mInnerSubd
1658− scheduleClear innerSubdRef
1659− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1660− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1661− let subscribed = CoincidenceSubscribed
1662− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1663− , coincidenceSubscribedOccurrence = occRef
1664− , coincidenceSubscribedHeight = heightRef
1665− , coincidenceSubscribedSubscribers = subs
1666− , coincidenceSubscribedOuter = subOuter
1667− , coincidenceSubscribedOuterParent = outerSubscription
1668− , coincidenceSubscribedInnerParent = innerSubdRef
1669− , coincidenceSubscribedWeakSelf = weakSelf
1670−#ifdef DEBUG_NODEIDS
1671− , coincidenceSubscribedNodeId = unsafeNodeId c
1672−#endif
1673− }
1674− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1675− liftIO $ writeIORef subscribedRef $! subscribed
1676− liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1677− return (slnForSub, subscribed, occ)
1678−
1679−cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
1680−cleanupCoincidenceSubscribed subscribed = do
1681− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1682− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1683−
1684−{-# INLINE subscribeCoincidenceSubscribed #-}
1685−subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1686−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1687−
1688−{-# INLINE merge #-}
1689−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1690−merge d = cacheEvent (mergeCheap d)
1691−
1692−{-# INLINE mergeWithMove #-}
1693−mergeWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1694−mergeWithMove d = cacheEvent (mergeCheapWithMove d)
1695−
1696−{-# INLINE [1] mergeCheap #-}
1697−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1698−mergeCheap = mergeCheap' getInitialSubscribers updateMe destroy
1699− where
1700− updateMe :: MergeUpdateFunc k x (PatchDMap k (Event x)) (MergeSubscribedParent x)
1701− updateMe subscriber heightBagRef oldParents (PatchDMap p) = do
1702− let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1703− (mOldSubd, newPs) <- case me of
1704− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1705− Just e -> do
1706− let s = subscriber $ return k
1707− subscription@(EventSubscription _ subd) <- subscribe e s
1708− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1709− let newParent = MergeSubscribedParent subscription
1710− liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1711− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1712− forM_ mOldSubd $ \oldSubd -> do
1713− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ unMergeSubscribedParent oldSubd
1714− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1715− return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1716− foldM f ([], oldParents) $ DMap.toList p
1717− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParent x)
1718− getInitialSubscribers initialParents subscriber = do
1719− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1720− let s = subscriber $ return k
1721− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1722− height <- liftIO $ getEventSubscribedHeight parentSubd
1723− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1724− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1725− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1726− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1727− )
1728− destroy :: MergeDestroyFunc k (MergeSubscribedParent x)
1729− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParent sub) -> unsubscribe sub
1730−
1731−{-# INLINE [1] mergeCheapWithMove #-}
1732−mergeCheapWithMove :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMapWithMove k (Event x)) -> Event x (DMap k Identity)
1733−mergeCheapWithMove = mergeCheap' getInitialSubscribers updateMe destroy
1734− where
1735− updateMe :: MergeUpdateFunc k x (PatchDMapWithMove k (Event x)) (MergeSubscribedParentWithMove x k)
1736− updateMe subscriber heightBagRef oldParents p = do
1737− -- Prepare new parents for insertion
1738− let subscribeParent :: forall a. k a -> Event x a -> EventM x (MergeSubscribedParentWithMove x k a)
1739− subscribeParent k e = do
1740− keyRef <- liftIO $ newIORef k
1741− let s = subscriber $ liftIO $ readIORef keyRef
1742− subscription@(EventSubscription _ subd) <- subscribe e s
1743− liftIO $ do
1744− newParentHeight <- getEventSubscribedHeight subd
1745− modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1746− return $ MergeSubscribedParentWithMove subscription keyRef
1747− p' <- PatchDMapWithMove.traversePatchDMapWithMoveWithKey subscribeParent p
1748− -- Collect old parents for deletion and update the keys of moved parents
1749− let moveOrDelete :: forall a. k a -> PatchDMapWithMove.NodeInfo k (Event x) a -> MergeSubscribedParentWithMove x k a -> Constant (EventM x (Maybe (EventSubscription x))) a
1750− moveOrDelete _ ni parent = Constant $ case getComposeMaybe $ PatchDMapWithMove._nodeInfo_to ni of
1751− Nothing -> do
1752− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ _mergeSubscribedParentWithMove_subscription parent
1753− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1754− return $ Just $ _mergeSubscribedParentWithMove_subscription parent
1755− Just toKey -> do
1756− liftIO $ writeIORef (_mergeSubscribedParentWithMove_key parent) $! toKey
1757− return Nothing
1758− toDelete <- fmap catMaybes $ mapM (\(_ :=> v) -> getConstant v) $ DMap.toList $ DMap.intersectionWithKey moveOrDelete (unPatchDMapWithMove p) oldParents
1759− return (toDelete, applyAlways p' oldParents)
1760− getInitialSubscribers :: MergeInitFunc k x (MergeSubscribedParentWithMove x k)
1761− getInitialSubscribers initialParents subscriber = do
1762− subscribers <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1763− keyRef <- liftIO $ newIORef k
1764− let s = subscriber $ liftIO $ readIORef keyRef
1765− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1766− height <- liftIO $ getEventSubscribedHeight parentSubd
1767− return (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParentWithMove subscription keyRef)
1768− return ( DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1769− , fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1770− , DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1771− )
1772− destroy :: MergeDestroyFunc k (MergeSubscribedParentWithMove x k)
1773− destroy s = forM_ (DMap.toList s) $ \(_ :=> MergeSubscribedParentWithMove sub _) -> unsubscribe sub
1774−
1775−type MergeUpdateFunc k x p s
1776− = (forall a. EventM x (k a) -> Subscriber x a)
1777− -> IORef HeightBag
1778− -> DMap k s
1779− -> p
1780− -> EventM x ([EventSubscription x], DMap k s)
1781−
1782−type MergeInitFunc k x s
1783− = DMap k (Event x)
1784− -> (forall a. EventM x (k a) -> Subscriber x a)
1785− -> EventM x (DMap k Identity, [Height], DMap k s)
1786−
1787−type MergeDestroyFunc k s
1788− = DMap k s
1789− -> IO ()
1790−
1791−data Merge x k s = Merge
1792− { _merge_parentsRef :: {-# UNPACK #-} !(IORef (DMap k s))
1793− , _merge_heightBagRef :: {-# UNPACK #-} !(IORef HeightBag)
1794− , _merge_heightRef :: {-# UNPACK #-} !(IORef Height)
1795− , _merge_sub :: {-# UNPACK #-} !(Subscriber x (DMap k Identity))
1796− , _merge_accumRef :: {-# UNPACK #-} !(IORef (DMap k Identity))
1797− }
1798−
1799−invalidateMergeHeight :: Merge x k s -> IO ()
1800−invalidateMergeHeight m = invalidateMergeHeight' (_merge_heightRef m) (_merge_sub m)
1801−
1802−invalidateMergeHeight' :: IORef Height -> Subscriber x a -> IO ()
1803−invalidateMergeHeight' heightRef sub = do
1804− oldHeight <- readIORef heightRef
1805− 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
1806− writeIORef heightRef $! invalidHeight
1807− subscriberInvalidateHeight sub oldHeight
1808−
1809−
1810−revalidateMergeHeight :: Merge x k s -> IO ()
1811−revalidateMergeHeight m = do
1812− currentHeight <- readIORef $ _merge_heightRef m
1813− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1814− heights <- readIORef $ _merge_heightBagRef m
1815− parents <- readIORef $ _merge_parentsRef m
1816− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1817− case heightBagSize heights `compare` DMap.size parents of
1818− LT -> return ()
1819− EQ -> do
1820− let height = succHeight $ heightBagMax heights
1821− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1822− writeIORef (_merge_heightRef m) $! height
1823− subscriberRecalculateHeight (_merge_sub m) height
1824− GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show (DMap.size parents) <> ") for Merge"
1825−
1826−scheduleMergeSelf :: HasSpiderTimeline x => Merge x k s -> Height -> EventM x ()
1827−scheduleMergeSelf m height = scheduleMerge' height (_merge_heightRef m) $ do
1828− vals <- liftIO $ readIORef $ _merge_accumRef m
1829− liftIO $ writeIORef (_merge_accumRef m) $! DMap.empty -- Once we're done with this, we can clear it immediately, because if there's a cacheEvent in front of us, it'll handle subsequent subscribers, and if not, we won't get subsequent subscribers
1830− --TODO: Assert that m is not empty
1831− subscriberPropagate (_merge_sub m) vals
1832−
1833−mergeSubscriber :: forall x k s a. (HasSpiderTimeline x, GCompare k) => Merge x k s -> EventM x (k a) -> Subscriber x a
1834−mergeSubscriber m getKey = Subscriber
1835− { subscriberPropagate = \a -> do
1836− oldM <- liftIO $ readIORef $ _merge_accumRef m
1837− k <- getKey
1838− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1839− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1840− liftIO $ writeIORef (_merge_accumRef m) $! newM
1841− when (DMap.null oldM) $ do -- Only schedule the firing once
1842− height <- liftIO $ readIORef $ _merge_heightRef m
1843− --TODO: assertions about height
1844− currentHeight <- getCurrentHeight
1845− when (height <= currentHeight) $ do
1846− if height /= invalidHeight
1847− then do
1848− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1849− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1850− else liftIO $ do
1851−#ifdef DEBUG_CYCLES
1852− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1853− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1854− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1855−#else
1856− let cycleInfo = ""
1857−#endif
1858− error $ "Causality loop found" <> cycleInfo
1859− scheduleMergeSelf m height
1860− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1861− modifyIORef' (_merge_heightBagRef m) $ heightBagRemove old
1862− invalidateMergeHeight m
1863− , subscriberRecalculateHeight = \new -> do
1864− modifyIORef' (_merge_heightBagRef m) $ heightBagAdd new
1865− revalidateMergeHeight m
1866− }
1867−
1868−--TODO: Be able to run as much of this as possible promptly
1869−updateMerge :: (HasSpiderTimeline x, GCompare k) => Merge x k s -> MergeUpdateFunc k x p s -> p -> SomeMergeUpdate x
1870−updateMerge m updateFunc p = SomeMergeUpdate updateMe (invalidateMergeHeight m) (revalidateMergeHeight m)
1871− where updateMe = do
1872− oldParents <- liftIO $ readIORef $ _merge_parentsRef m
1873− (subscriptionsToKill, newParents) <- updateFunc (mergeSubscriber m) (_merge_heightBagRef m) oldParents p
1874− liftIO $ writeIORef (_merge_parentsRef m) $! newParents
1875− return subscriptionsToKill
1876−
1877−{-# INLINE mergeCheap' #-}
1878−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 -> Dynamic x p -> Event x (DMap k Identity)
1879−mergeCheap' getInitialSubscribers updateFunc destroy d = Event $ \sub -> do
1880− initialParents <- readBehaviorUntracked $ dynamicCurrent d
1881− accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1882− heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1883− heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
1884− parentsRef :: IORef (DMap k s) <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1885− let m = Merge
1886− { _merge_parentsRef = parentsRef
1887− , _merge_heightBagRef = heightBagRef
1888− , _merge_heightRef = heightRef
1889− , _merge_sub = sub
1890− , _merge_accumRef = accumRef
1891− }
1892− (dm, heights, initialParentState) <- getInitialSubscribers initialParents $ mergeSubscriber m
1893− let myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1894− myHeight = if invalidHeight `elem` heights
1895− then invalidHeight
1896− else succHeight $ heightBagMax myHeightBag
1897− currentHeight <- getCurrentHeight
1898− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1899− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1900− else (Nothing, dm)
1901− unless (DMap.null accum) $ do
1902− scheduleMergeSelf m myHeight
1903− liftIO $ writeIORef accumRef $! accum
1904− liftIO $ writeIORef heightRef $! myHeight
1905− liftIO $ writeIORef heightBagRef $! myHeightBag
1906− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1907− liftIO $ writeIORef parentsRef $! initialParentState
1908− defer $ SomeMergeInit $ do
1909− let s = Subscriber
1910− { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1911− tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1912− defer $ updateMerge m updateFunc a
1913− , subscriberInvalidateHeight = \_ -> return ()
1914− , subscriberRecalculateHeight = \_ -> return ()
1915− }
1916− (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
1917− forM_ change $ \c -> defer $ updateMerge m updateFunc c
1918− -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
1919− -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
1920− -- its subscription.
1921− liftIO $ writeIORef changeSubdRef (s, changeSubscription)
1922− let unsubscribeAll = destroy =<< readIORef parentsRef
1923− return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parentsRef, changeSubdRef)
1924− , occ
1925− )
1926−
1927−mergeInt :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1928−mergeInt = cacheEvent . mergeIntCheap
1929−
1930−{-# INLINABLE mergeIntCheap #-}
1931−mergeIntCheap :: forall x a. (HasSpiderTimeline x) => Dynamic x (PatchIntMap (Event x a)) -> Event x (IntMap a)
1932−mergeIntCheap d = Event $ \sub -> do
1933− initialParents <- readBehaviorUntracked $ dynamicCurrent d
1934− accum <- liftIO $ FastMutableIntMap.newEmpty
1935− heightRef <- liftIO $ newIORef zeroHeight
1936− heightBagRef <- liftIO $ newIORef heightBagEmpty
1937− parents <- liftIO $ FastMutableIntMap.newEmpty
1938− let scheduleSelf = do
1939− height <- liftIO $ readIORef $ heightRef
1940− scheduleMerge' height heightRef $ do
1941− vals <- liftIO $ FastMutableIntMap.getFrozenAndClear accum
1942− subscriberPropagate sub vals
1943− invalidateMyHeight = do
1944− invalidateMergeHeight' heightRef sub
1945− recalculateMyHeight = do
1946− currentHeight <- readIORef heightRef
1947− when (currentHeight == invalidHeight) $ do --TODO: This will almost always be true; can we get rid of this check and just proceed to the next one always?
1948− heights <- readIORef heightBagRef
1949− numParents <- FastMutableIntMap.size parents
1950− case heightBagSize heights `compare` numParents of
1951− LT -> return ()
1952− EQ -> do
1953− let height = succHeight $ heightBagMax heights
1954− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1955− writeIORef heightRef $! height
1956− subscriberRecalculateHeight sub height
1957− GT -> error $ "revalidateMergeHeight: more heights (" <> show (heightBagSize heights) <> ") than parents (" <> show numParents <> ") for Merge"
1958− mySubscriber k = Subscriber
1959− { subscriberPropagate = \a -> do
1960− wasEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1961− liftIO $ FastMutableIntMap.insert accum k a
1962− when wasEmpty scheduleSelf
1963− , subscriberInvalidateHeight = \old -> do
1964− modifyIORef' heightBagRef $ heightBagRemove old
1965− invalidateMyHeight
1966− , subscriberRecalculateHeight = \new -> do
1967− modifyIORef' heightBagRef $ heightBagAdd new
1968− recalculateMyHeight
1969− }
1970− forM_ (IntMap.toList initialParents) $ \(k, p) -> do
1971− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead p $ mySubscriber k
1972− liftIO $ do
1973− forM_ parentOcc $ FastMutableIntMap.insert accum k
1974− FastMutableIntMap.insert parents k subscription
1975− height <- getEventSubscribedHeight parentSubd
1976− if height == invalidHeight
1977− then writeIORef heightRef invalidHeight
1978− else do
1979− modifyIORef' heightBagRef $ heightBagAdd height
1980− modifyIORef' heightRef $ \oldHeight ->
1981− if oldHeight == invalidHeight
1982− then invalidHeight
1983− else max (succHeight height) oldHeight
1984− myHeight <- liftIO $ readIORef heightRef
1985− currentHeight <- getCurrentHeight
1986− isEmpty <- liftIO $ FastMutableIntMap.isEmpty accum
1987− occ <- if currentHeight >= myHeight -- If we should have fired by now
1988− then if isEmpty
1989− then return Nothing
1990− else liftIO $ Just <$> FastMutableIntMap.getFrozenAndClear accum
1991− else do when (not isEmpty) scheduleSelf -- We have things accumulated, but we shouldn't have fired them yet
1992− return Nothing
1993− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1994− defer $ SomeMergeInit $ do
1995− let updateMe a = SomeMergeUpdate u invalidateMyHeight recalculateMyHeight
1996− where
1997− u = do
1998− let f k newParent = do
1999− subscription@(EventSubscription _ subd) <- subscribe newParent $ mySubscriber k
2000− newParentHeight <- liftIO $ getEventSubscribedHeight subd
2001− liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
2002− return subscription
2003− newSubscriptions <- FastMutableIntMap.traverseIntMapPatchWithKey f a
2004− oldParents <- liftIO $ FastMutableIntMap.applyPatch parents newSubscriptions
2005− liftIO $ for_ oldParents $ \oldParent -> do
2006− oldParentHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed oldParent
2007− modifyIORef' heightBagRef $ heightBagRemove oldParentHeight
2008− return $ IntMap.elems oldParents
2009− let s = Subscriber
2010− { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
2011− tracePropagate (Proxy :: Proxy x) $ "SubscriberMergeInt/Change"
2012− defer $ updateMe a
2013− , subscriberInvalidateHeight = \_ -> return ()
2014− , subscriberRecalculateHeight = \_ -> return ()
2015− }
2016− (changeSubscription, change) <- subscribeAndRead (dynamicUpdated d) s
2017− forM_ change $ \c -> defer $ updateMe c
2018− -- We explicitly hold on to the unsubscribe function from subscribing to the update event.
2019− -- If we don't do this, there are certain cases where mergeCheap will fail to properly retain
2020− -- its subscription.
2021− liftIO $ writeIORef changeSubdRef (s, changeSubscription)
2022− let unsubscribeAll = traverse_ unsubscribe =<< FastMutableIntMap.getFrozenAndClear parents
2023− return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parents, changeSubdRef)
2024− , occ
2025− )
2026−
2027−newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
2028−
2029−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
2030−fan e =
2031− let f = Fan
2032− { fanParent = e
2033− , fanSubscribed = unsafeNewIORef e Nothing
2034− }
2035− in EventSelector $ \k -> eventFan k f
2036−
2037−runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeDynInit x] -> IORef [SomeMergeInit x] -> EventM x ()
2038−runHoldInits holdInitRef dynInitRef mergeInitRef = do
2039− holdInits <- liftIO $ readIORef holdInitRef
2040− dynInits <- liftIO $ readIORef dynInitRef
2041− mergeInits <- liftIO $ readIORef mergeInitRef
2042− unless (null holdInits && null dynInits && null mergeInits) $ do
2043− liftIO $ writeIORef holdInitRef []
2044− liftIO $ writeIORef dynInitRef []
2045− liftIO $ writeIORef mergeInitRef []
2046− mapM_ initHold holdInits
2047− mapM_ initDyn dynInits
2048− mapM_ unSomeMergeInit mergeInits
2049− runHoldInits holdInitRef dynInitRef mergeInitRef
2050−
2051−initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
2052−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
2053−
2054−initDyn :: HasSpiderTimeline x => SomeDynInit x -> EventM x ()
2055−initDyn (SomeDynInit d) = void $ getDynHold d
2056−
2057−newEventEnv :: IO (EventEnv x)
2058−newEventEnv = do
2059− toAssignRef <- newIORef [] -- This should only actually get used when events are firing
2060− holdInitRef <- newIORef []
2061− dynInitRef <- newIORef []
2062− mergeUpdateRef <- newIORef []
2063− mergeInitRef <- newIORef []
2064− heightRef <- newIORef zeroHeight
2065− toClearRef <- newIORef []
2066− toClearIntRef <- newIORef []
2067− toClearRootRef <- newIORef []
2068− coincidenceInfosRef <- newIORef []
2069− delayedRef <- newIORef IntMap.empty
2070− return $ EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef
2071−
2072−clearEventEnv :: EventEnv x -> IO ()
2073−clearEventEnv (EventEnv toAssignRef holdInitRef dynInitRef mergeUpdateRef mergeInitRef toClearRef toClearIntRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
2074− writeIORef toAssignRef []
2075− writeIORef holdInitRef []
2076− writeIORef dynInitRef []
2077− writeIORef mergeUpdateRef []
2078− writeIORef mergeInitRef []
2079− writeIORef heightRef zeroHeight
2080− writeIORef toClearRef []
2081− writeIORef toClearIntRef []
2082− writeIORef toClearRootRef []
2083− writeIORef coincidenceInfosRef []
2084− writeIORef delayedRef IntMap.empty
2085−
2086−-- | Run an event action outside of a frame
2087−runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
2088−runFrame a = SpiderHost $ do
2089− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
2090− let go = do
2091− result <- a
2092− runHoldInits (eventEnvHoldInits env) (eventEnvDynInits 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
2093− return result
2094− result <- runEventM go
2095− toClear <- readIORef $ eventEnvClears env
2096− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
2097− toClearInt <- readIORef $ eventEnvIntClears env
2098− forM_ toClearInt $ \(SomeIntClear ref) -> {-# SCC "intClear" #-} writeIORef ref $! IntMap.empty
2099− toClearRoot <- readIORef $ eventEnvRootClears env
2100− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
2101− toAssign <- readIORef $ eventEnvAssignments env
2102− toReconnectRef <- newIORef []
2103− coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
2104− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
2105− writeIORef vRef v
2106− when debugInvalidate $ putStrLn $ "Invalidating Hold"
2107− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
2108− mergeUpdates <- readIORef $ eventEnvMergeUpdates env
2109− writeIORef (eventEnvMergeUpdates env) []
2110− when debugPropagate $ putStrLn "Updating merges"
2111− mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
2112− when debugPropagate $ putStrLn "Updating merges done"
2113− toReconnect <- readIORef toReconnectRef
2114− clearEventEnv env
2115− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
2116− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
2117− wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
2118− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
2119− finalize wi
2120− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
2121− wi' <- mkWeakPtrWithDebug i "wi'"
2122− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
2123− writeIORef (switchSubscribedBehaviorParents subscribed) []
2124− writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
2125− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
2126− runEventM $ runHoldInits (eventEnvHoldInits env) (eventEnvDynInits 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
2127− --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
2128− sub <- newSubscriberSwitch subscribed
2129− subscription <- unSpiderHost $ runFrame $ {-# SCC "subscribeSwitch" #-} subscribe e sub --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
2130− {-
2131− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
2132− liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
2133− EventRoot _ -> "EventRoot"
2134− EventNever -> "EventNever"
2135− _ -> "something else"
2136− -}
2137− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
2138− return oldSubscription
2139− liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
2140− liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
2141− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
2142− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
2143− parentHeight <- getEventSubscribedHeight subd'
2144− myHeight <- readIORef $ switchSubscribedHeight subscribed
2145− when (parentHeight /= myHeight) $ do
2146− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
2147− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
2148− 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
2149− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
2150− unsubscribe subscription
2151− mapM_ invalidateCoincidenceHeight mcs
2152− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
2153− mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
2154− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
2155− height <- calculateSwitchHeight subscribed
2156− updateSwitchHeight height subscribed
2157− return result
2158−
2159−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
2160−
2161−{-# INLINE zeroHeight #-}
2162−zeroHeight :: Height
2163−zeroHeight = Height 0
2164−
2165−{-# INLINE invalidHeight #-}
2166−invalidHeight :: Height
2167−invalidHeight = Height (-1000)
2168−
2169−#ifdef DEBUG_CYCLES
2170−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
2171−{-# INLINE invalidHeightBeingTraversed #-}
2172−invalidHeightBeingTraversed :: Height
2173−invalidHeightBeingTraversed = Height (-1001)
2174−#endif
2175−
2176−{-# INLINE succHeight #-}
2177−succHeight :: Height -> Height
2178−succHeight h@(Height a) =
2179− if h == invalidHeight
2180− then invalidHeight
2181− else Height $ succ a
2182−
2183−invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2184−invalidateCoincidenceHeight subscribed = do
2185− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2186− when (oldHeight /= invalidHeight) $ do
2187− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
2188− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
2189−
2190−updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
2191−updateSwitchHeight new subscribed = do
2192− oldHeight <- readIORef $ switchSubscribedHeight subscribed
2193− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2194− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2195− writeIORef (switchSubscribedHeight subscribed) $! new
2196− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
2197−
2198−recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
2199−recalculateCoincidenceHeight subscribed = do
2200− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
2201− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
2202− height <- calculateCoincidenceHeight subscribed
2203− when (height /= invalidHeight) $ do
2204− writeIORef (coincidenceSubscribedHeight subscribed) $! height
2205− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
2206−
2207−calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
2208−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
2209−
2210−calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
2211−calculateCoincidenceHeight subscribed = do
2212− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
2213− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
2214− return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
2215−
2216−data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
2217−
2218−invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
2219−invalidate toReconnectRef wis = do
2220− forM_ wis $ \wi -> do
2221− mi <- deRefWeak wi
2222− case mi of
2223− Nothing -> do
2224− traceInvalidate "invalidate Dead"
2225− return () --TODO: Should we clean this up here?
2226− Just i -> do
2227− finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
2228− case i of
2229− InvalidatorPull p -> do
2230− traceInvalidate $ "invalidate: Pull" <> showNodeId p
2231− mVal <- readIORef $ pullValue p
2232− forM_ mVal $ \val -> do
2233− writeIORef (pullValue p) Nothing
2234− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
2235− InvalidatorSwitch subscribed -> do
2236− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
2237− modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
2238− 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
2239−
2240−--------------------------------------------------------------------------------
2241−-- Reflex integration
2242−--------------------------------------------------------------------------------
2243−
2244−-- | Designates the default, global Spider timeline
2245−data SpiderTimeline x
2246−type role SpiderTimeline nominal
2247−
2248−-- | The default, global Spider environment
2249−type Spider = SpiderTimeline Global
2250−
2251−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (EventM x) where
2252− {-# INLINABLE sample #-}
2253− sample (SpiderBehavior b) = readBehaviorUntracked b
2254−
2255−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (EventM x) where
2256− {-# INLINABLE hold #-}
2257− hold = holdSpiderEventM
2258− {-# INLINABLE holdDyn #-}
2259− holdDyn = holdDynSpiderEventM
2260− {-# INLINABLE holdIncremental #-}
2261− holdIncremental = holdIncrementalSpiderEventM
2262− {-# INLINABLE buildDynamic #-}
2263− buildDynamic = buildDynamicSpiderEventM
2264− {-# INLINABLE headE #-}
2265− headE = R.slowHeadE
2266−-- headE (SpiderEvent e) = SpiderEvent <$> Reflex.Spider.Internal.headE e
2267−
2268−instance Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
2269− {-# INLINABLE sample #-}
2270− sample = coerce . readBehaviorTracked . unSpiderBehavior
2271−
2272−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
2273− {-# INLINABLE sample #-}
2274− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
2275−
2276−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderPushM x) where
2277− {-# INLINABLE hold #-}
2278− hold v0 e = Reflex.Class.current <$> Reflex.Class.holdDyn v0 e
2279− {-# INLINABLE holdDyn #-}
2280− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce e
2281− {-# INLINABLE holdIncremental #-}
2282− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . dynamicHold <$> Reflex.Spider.Internal.hold v0 e
2283− {-# INLINABLE buildDynamic #-}
2284− buildDynamic getV0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicDynIdentity) $ Reflex.Spider.Internal.buildDynamic (coerce getV0) $ coerce e
2285− {-# INLINABLE headE #-}
2286− headE = R.slowHeadE
2287−-- headE (SpiderEvent e) = SpiderPushM $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2288−
2289−instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
2290− {-# INLINE return #-}
2291− return = pure
2292− {-# INLINE (>>=) #-}
2293− x >>= f = SpiderDynamic $ dynamicDynIdentity $ newJoinDyn $ newMapDyn (unSpiderDynamic . f) $ unSpiderDynamic x
2294− {-# INLINE (>>) #-}
2295− (>>) = (*>)
2296− {-# INLINE fail #-}
2297− fail _ = error "Dynamic does not support 'fail'"
2298−
2299−{-# INLINABLE newJoinDyn #-}
2300−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
2301−newJoinDyn d =
2302− let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
2303− eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
2304− eInner = Reflex.Spider.Internal.switch $ dynamicUpdated <$> dynamicCurrent d
2305− eBoth = Reflex.Spider.Internal.coincidence $ dynamicUpdated . runIdentity <$> dynamicUpdated d
2306− v' = unSpiderEvent $ Reflex.Class.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
2307− in Reflex.Spider.Internal.unsafeBuildDynamic readV0 v'
2308−
2309−instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
2310− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
2311− x <$ d = R.unsafeBuildDynamic (return x) $ x <$ R.updated d
2312−
2313−instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
2314− pure = SpiderDynamic . dynamicConst
2315−#if MIN_VERSION_base(4,10,0)
2316− liftA2 f a b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith f (unSpiderDynamic a) (unSpiderDynamic b)
2317−#endif
2318− SpiderDynamic a <*> SpiderDynamic b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith ($) a b
2319− a *> b = R.unsafeBuildDynamic (R.sample $ R.current b) $ R.leftmost [R.updated b, R.tag (R.current b) $ R.updated a]
2320− (<*) = flip (*>) -- There are no effects, so order doesn't matter
2321−
2322−holdSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Behavior (SpiderTimeline x) a)
2323−holdSpiderEventM v0 e = fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
2324−
2325−holdDynSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
2326−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
2327−
2328−holdIncrementalSpiderEventM :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Reflex.Class.Event (SpiderTimeline x) p -> EventM x (Reflex.Class.Incremental (SpiderTimeline x) p)
2329−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
2330−
2331−buildDynamicSpiderEventM :: HasSpiderTimeline x => SpiderPushM x a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
2332−buildDynamicSpiderEventM getV0 e = fmap (SpiderDynamic . dynamicDynIdentity) $ Reflex.Spider.Internal.buildDynamic (coerce getV0) $ coerce $ unSpiderEvent e
2333−
2334−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHost x) where
2335− {-# INLINABLE hold #-}
2336− hold v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.hold v0 e
2337− {-# INLINABLE holdDyn #-}
2338− holdDyn v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdDyn v0 e
2339− {-# INLINABLE holdIncremental #-}
2340− holdIncremental v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdIncremental v0 e
2341− {-# INLINABLE buildDynamic #-}
2342− buildDynamic getV0 e = runFrame . runSpiderHostFrame $ Reflex.Class.buildDynamic getV0 e
2343− {-# INLINABLE headE #-}
2344− headE e = runFrame . runSpiderHostFrame $ Reflex.Class.headE e
2345−
2346−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
2347− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
2348−
2349−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
2350− {-# INLINABLE hold #-}
2351− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
2352− {-# INLINABLE holdDyn #-}
2353− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
2354− {-# INLINABLE holdIncremental #-}
2355− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
2356− {-# INLINABLE buildDynamic #-}
2357− buildDynamic getV0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicDynIdentity) $ Reflex.Spider.Internal.buildDynamic (coerce getV0) $ coerce $ unSpiderEvent e
2358− {-# INLINABLE headE #-}
2359− headE = R.slowHeadE
2360−-- headE (SpiderEvent e) = SpiderHostFrame $ SpiderEvent <$> Reflex.Spider.Internal.headE e
2361−
2362−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
2363− {-# INLINABLE sample #-}
2364− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
2365−
2366−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
2367− {-# INLINABLE sample #-}
2368− sample = Reflex.Spider.Internal.ReadPhase . Reflex.Class.sample
2369−
2370−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
2371− {-# INLINABLE hold #-}
2372− hold v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.hold v0 e
2373− {-# INLINABLE holdDyn #-}
2374− holdDyn v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdDyn v0 e
2375− {-# INLINABLE holdIncremental #-}
2376− holdIncremental v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdIncremental v0 e
2377− {-# INLINABLE buildDynamic #-}
2378− buildDynamic getV0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.buildDynamic getV0 e
2379− {-# INLINABLE headE #-}
2380− headE e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.headE e
2381−
2382−--------------------------------------------------------------------------------
2383−-- Deprecated items
2384−--------------------------------------------------------------------------------
2385−
2386−-- | 'SpiderEnv' is the old name for 'SpiderTimeline'
2387−{-# DEPRECATED SpiderEnv "Use 'SpiderTimelineEnv' instead" #-}
2388−type SpiderEnv = SpiderTimeline
2389−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2390− {-# INLINABLE subscribeEvent #-}
2391− subscribeEvent e = SpiderHostFrame $ do
2392− --TODO: Unsubscribe eventually (manually and/or with weak ref)
2393− val <- liftIO $ newIORef Nothing
2394− subscription <- subscribe (unSpiderEvent e) $ Subscriber
2395− { subscriberPropagate = \a -> do
2396− liftIO $ writeIORef val $ Just a
2397− scheduleClear val
2398− , subscriberInvalidateHeight = \_ -> return ()
2399− , subscriberRecalculateHeight = \_ -> return ()
2400− }
2401− return $ SpiderEventHandle
2402− { spiderEventHandleSubscription = subscription
2403− , spiderEventHandleValue = val
2404− }
2405−
2406−instance HasSpiderTimeline x => Reflex.Host.Class.ReflexHost (SpiderTimeline x) where
2407− type EventTrigger (SpiderTimeline x) = RootTrigger x
2408− type EventHandle (SpiderTimeline x) = SpiderEventHandle x
2409− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
2410−
2411−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReadEvent (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
2412− {-# NOINLINE readEvent #-}
2413− readEvent h = Reflex.Spider.Internal.ReadPhase $ fmap (fmap return) $ liftIO $ do
2414− result <- readIORef $ spiderEventHandleValue h
2415− touch h
2416− return result
2417−
2418−instance Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
2419− newEventWithTrigger = SpiderHost . fmap SpiderEvent . newEventWithTriggerIO
2420− newFanEventWithTrigger f = SpiderHost $ do
2421− es <- newFanEventWithTriggerIO f
2422− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
2423−
2424−instance Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
2425− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
2426− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
2427− es <- newFanEventWithTriggerIO f
2428− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
2429−
2430−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
2431− {-# INLINABLE subscribeEvent #-}
2432− subscribeEvent = runFrame . runSpiderHostFrame . Reflex.Host.Class.subscribeEvent
2433−
2434−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
2435− type ReadPhase (SpiderHost x) = Reflex.Spider.Internal.ReadPhase x
2436− fireEventsAndRead es (Reflex.Spider.Internal.ReadPhase a) = run es a
2437− runHostFrame = runFrame . runSpiderHostFrame
2438−
2439−unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
2440−unsafeNewSpiderTimelineEnv = do
2441− lock <- newMVar ()
2442− env <- newEventEnv
2443−#ifdef DEBUG
2444− depthRef <- newIORef 0
2445−#endif
2446− return $ SpiderTimelineEnv
2447− { _spiderTimeline_lock = lock
2448− , _spiderTimeline_eventEnv = env
2449−#ifdef DEBUG
2450− , _spiderTimeline_depth = depthRef
2451−#endif
2452− }
2453−
2454−-- | Create a new SpiderTimelineEnv
2455−newSpiderTimeline :: IO (Some SpiderTimelineEnv)
2456−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
2457−
2458−data LocalSpiderTimeline x s
2459−
2460−instance Reifies s (SpiderTimelineEnv x) =>
2461− HasSpiderTimeline (LocalSpiderTimeline x s) where
2462− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
2463−
2464−localSpiderTimeline
2465− :: Proxy s
2466− -> SpiderTimelineEnv x
2467− -> SpiderTimelineEnv (LocalSpiderTimeline x s)
2468−localSpiderTimeline _ = unsafeCoerce
2469−
2470−-- | Pass a new timeline to the given function.
2471−withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
2472−withSpiderTimeline k = do
2473− env <- unsafeNewSpiderTimelineEnv
2474− reify env $ \s -> k $ localSpiderTimeline s env
2475−
2476−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2477−
2478−type ComputeM = EventM
2479−
2480−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
2481−
2482−instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
2483− {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
2484− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
2485− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
2486− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
2487− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
2488− type PullM (SpiderTimeline x) = SpiderPullM x
2489− type PushM (SpiderTimeline x) = SpiderPushM x
2490− {-# INLINABLE never #-}
2491− never = SpiderEvent eventNever
2492− {-# INLINABLE constant #-}
2493− constant = SpiderBehavior . behaviorConst
2494− {-# INLINE push #-}
2495− push f = SpiderEvent . push (coerce f) . unSpiderEvent
2496− {-# INLINE pushCheap #-}
2497− pushCheap f = SpiderEvent . pushCheap (coerce f) . unSpiderEvent
2498− {-# INLINABLE pull #-}
2499− pull = SpiderBehavior . pull . coerce
2500− {-# INLINABLE merge #-}
2501− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
2502− {-# INLINABLE fan #-}
2503− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
2504− {-# INLINABLE switch #-}
2505− switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
2506− {-# INLINABLE coincidence #-}
2507− coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
2508− {-# INLINABLE current #-}
2509− current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
2510− {-# INLINABLE updated #-}
2511− updated = coerce $ SpiderEvent . dynamicUpdated . unSpiderDynamic
2512− {-# INLINABLE unsafeBuildDynamic #-}
2513− unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (coerce readV0) $ coerce $ unSpiderEvent v'
2514− {-# INLINABLE unsafeBuildIncremental #-}
2515− unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeBuildDynamic (coerce readV0) $ unSpiderEvent dv
2516− {-# INLINABLE mergeIncremental #-}
2517− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
2518− {-# INLINABLE mergeIncrementalWithMove #-}
2519− mergeIncrementalWithMove = SpiderEvent . mergeWithMove . (unsafeCoerce :: Dynamic x (PatchDMapWithMove k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMapWithMove k (Event x))) . unSpiderIncremental
2520− {-# INLINABLE currentIncremental #-}
2521− currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
2522− {-# INLINABLE updatedIncremental #-}
2523− updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
2524− {-# INLINABLE incrementalToDynamic #-}
2525− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeBuildDynamic (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
2526− c <- readBehaviorUntracked $ dynamicCurrent i
2527− return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
2528− eventCoercion Coercion = Coercion
2529− behaviorCoercion Coercion = Coercion
2530− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
2531− {-# INLINABLE mergeIntIncremental #-}
2532− mergeIntIncremental = SpiderEvent . mergeInt . (unsafeCoerce :: Dynamic x (PatchIntMap (R.Event (SpiderTimeline x) a)) -> Dynamic x (PatchIntMap (Event x a))) . unSpiderIncremental
2533− {-# INLINABLE fanInt #-}
2534− fanInt e = R.EventSelectorInt $ SpiderEvent . selectInt (fanInt (unSpiderEvent e))
2535−
2536−data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
2537−
2538−data SpiderEventHandle x a = SpiderEventHandle
2539− { spiderEventHandleSubscription :: EventSubscription x
2540− , spiderEventHandleValue :: IORef (Maybe a)
2541− }
2542−
2543−instance MonadRef (EventM x) where
2544− type Ref (EventM x) = Ref IO
2545− {-# INLINABLE newRef #-}
2546− {-# INLINABLE readRef #-}
2547− {-# INLINABLE writeRef #-}
2548− newRef = liftIO . newRef
2549− readRef = liftIO . readRef
2550− writeRef r a = liftIO $ writeRef r a
2551−
2552−instance MonadAtomicRef (EventM x) where
2553− {-# INLINABLE atomicModifyRef #-}
2554− atomicModifyRef r f = liftIO $ atomicModifyRef r f
2555−
2556−-- | The monad for actions that manipulate a Spider timeline identified by @x@
2557−newtype SpiderHost x a = SpiderHost { unSpiderHost :: IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2558−
2559−instance Monad (SpiderHost x) where
2560− {-# INLINABLE (>>=) #-}
2561− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
2562− {-# INLINABLE (>>) #-}
2563− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
2564− {-# INLINABLE return #-}
2565− return x = SpiderHost $ return x
2566− {-# INLINABLE fail #-}
2567− fail s = SpiderHost $ fail s
2568−
2569−-- | Run an action affecting the global Spider timeline; this will be guarded by
2570−-- a mutex for that timeline
2571−runSpiderHost :: SpiderHost Global a -> IO a
2572−runSpiderHost (SpiderHost a) = a
2573−
2574−-- | Run an action affecting a given Spider timeline; this will be guarded by a
2575−-- mutex for that timeline
2576−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
2577−runSpiderHostForTimeline (SpiderHost a) _ = a
2578−
2579−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a }
2580− deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
2581−
2582−instance Monad (SpiderHostFrame x) where
2583− {-# INLINABLE (>>=) #-}
2584− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2585− {-# INLINABLE (>>) #-}
2586− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2587− {-# INLINABLE return #-}
2588− return x = SpiderHostFrame $ return x
2589− {-# INLINABLE fail #-}
2590− fail s = SpiderHostFrame $ fail s
2591−
2592−instance NotReady (SpiderTimeline x) (SpiderHostFrame x) where
2593− notReadyUntil _ = pure ()
2594− notReady = pure ()
2595−
2596−newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
2597−newEventWithTriggerIO f = do
2598− es <- newFanEventWithTriggerIO $ \Refl -> f
2599− return $ select es Refl
2600−
2601−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger x a -> IO (IO ())) -> IO (EventSelector x k)
2602−newFanEventWithTriggerIO f = do
2603− occRef <- newIORef DMap.empty
2604− subscribedRef <- newIORef DMap.empty
2605− let !r = Root
2606− { rootOccurrence = occRef
2607− , rootSubscribed = subscribedRef
2608− , rootInit = f
2609− }
2610− return $ EventSelector $ \k -> eventRoot k r
2611−
2612−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2613−
2614−instance MonadRef (SpiderHost x) where
2615− type Ref (SpiderHost x) = Ref IO
2616− newRef = SpiderHost . newRef
2617− readRef = SpiderHost . readRef
2618− writeRef r = SpiderHost . writeRef r
2619−
2620−instance MonadAtomicRef (SpiderHost x) where
2621− atomicModifyRef r = SpiderHost . atomicModifyRef r
2622−
2623−instance MonadRef (SpiderHostFrame x) where
2624− type Ref (SpiderHostFrame x) = Ref IO
2625− newRef = SpiderHostFrame . newRef
2626− readRef = SpiderHostFrame . readRef
2627− writeRef r = SpiderHostFrame . writeRef r
2628−
2629−instance MonadAtomicRef (SpiderHostFrame x) where
2630− atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2631−
2632−instance PrimMonad (SpiderHostFrame x) where
2633− type PrimState (SpiderHostFrame x) = PrimState IO
2634− primitive = SpiderHostFrame . EventM . primitive
src/Reflex/Time.hsdeleted−284
@@ -1,284 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveDataTypeable #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RecursiveDo #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TupleSections #-}
8−{-# LANGUAGE TypeFamilies #-}
9−#ifdef USE_TEMPLATE_HASKELL
10−{-# LANGUAGE TemplateHaskell #-}
11−#endif
12−module Reflex.Time where
13−
14−import Reflex.Class
15−import Reflex.Dynamic
16−import Reflex.PerformEvent.Class
17−import Reflex.PostBuild.Class
18−import Reflex.TriggerEvent.Class
19−
20−import Control.Concurrent
21−import qualified Control.Concurrent.Thread.Delay as Concurrent
22−import Control.Lens hiding ((|>))
23−import Control.Monad
24−import Control.Monad.Fix
25−import Control.Monad.IO.Class
26−import Data.Align
27−import Data.Fixed
28−import Data.Sequence (Seq, (|>))
29−import qualified Data.Sequence as Seq
30−import Data.These
31−import Data.Time.Clock
32−import Data.Typeable
33−import System.Random
34−
35−data TickInfo
36− = TickInfo { _tickInfo_lastUTC :: UTCTime
37− -- ^ UTC time immediately after the last tick.
38− , _tickInfo_n :: Integer
39− -- ^ Number of time periods since t0
40− , _tickInfo_alreadyElapsed :: NominalDiffTime
41− -- ^ Amount of time already elapsed in the current tick period.
42− }
43− deriving (Eq, Ord, Show, Typeable)
44−
45−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
46−-- tick thread.
47−tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
48−tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
49−
50−-- | Special case of 'tickLossyFrom' that uses the post-build event to start the
51−-- tick thread and the time of the post-build as the tick basis time.
52−tickLossyFromPostBuildTime :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> m (Event t TickInfo)
53−tickLossyFromPostBuildTime dt = do
54− postBuild <- getPostBuild
55− postBuildTime <- performEvent $ liftIO getCurrentTime <$ postBuild
56− tickLossyFrom' $ (dt,) <$> postBuildTime
57−
58−-- | Send events over time with the given basis time and interval
59−-- If the system starts running behind, occurrences will be dropped rather than buffered
60−-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time
61−tickLossyFrom
62− :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
63− => NominalDiffTime
64− -- ^ The length of a tick interval
65− -> UTCTime
66− -- ^ The basis time from which intervals count
67− -> Event t a
68− -- ^ Event that starts a tick generation thread. Usually you want this to
69− -- be something like the result of getPostBuild that only fires once. But
70− -- there could be uses for starting multiple timer threads.
71− -> m (Event t TickInfo)
72−tickLossyFrom dt t0 e = tickLossyFrom' $ (dt, t0) <$ e
73−
74−-- | Generalization of tickLossyFrom that takes dt and t0 in the event.
75−tickLossyFrom'
76− :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
77− => Event t (NominalDiffTime, UTCTime)
78− -- ^ Event that starts a tick generation thread. Usually you want this to
79− -- be something like the result of getPostBuild that only fires once. But
80− -- there could be uses for starting multiple timer threads.
81− -> m (Event t TickInfo)
82−tickLossyFrom' e = do
83− rec result <- performEventAsync $ callAtNextInterval <$> leftmost [e, snd <$> result]
84− return $ fst <$> result
85− where callAtNextInterval pair cb = void $ liftIO $ forkIO $ do
86− tick <- uncurry getCurrentTick pair
87− Concurrent.delay $ ceiling $ (fst pair - _tickInfo_alreadyElapsed tick) * 1000000
88− cb (tick, pair)
89−
90−clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m, MonadFix m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
91−clockLossy dt t0 = do
92− initial <- liftIO $ getCurrentTick dt t0
93− e <- tickLossy dt t0
94− holdDyn initial e
95−
96−getCurrentTick :: NominalDiffTime -> UTCTime -> IO TickInfo
97−getCurrentTick dt t0 = do
98− t <- getCurrentTime
99− let offset = t `diffUTCTime` t0
100− (n, alreadyElapsed) = offset `divMod'` dt
101− return $ TickInfo t n alreadyElapsed
102−
103−-- | Delay an Event's occurrences by a given amount in seconds.
104−delay :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
105−delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do
106− Concurrent.delay $ ceiling $ dt * 1000000
107− cb a
108−
109−-- | Send events with Poisson timing with the given basis and rate
110−-- Each occurrence of the resulting event will contain the index of
111−-- the current interval, with 0 representing the basis time
112−poissonLossyFrom
113− :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
114− => g
115− -> Double
116− -- ^ Poisson event rate (Hz)
117− -> UTCTime
118− -- ^ Baseline time for events
119− -> Event t a
120− -- ^ Event that starts a tick generation thread. Usually you want this to
121− -- be something like the result of getPostBuild that only fires once. But
122− -- there could be uses for starting multiple timer threads.
123− -- Start sending events in response to the event parameter.
124− -> m (Event t TickInfo)
125−poissonLossyFrom rnd rate = inhomogeneousPoissonFrom rnd (constant rate) rate
126−
127−
128−-- | Send events with Poisson timing with the given basis and rate
129−-- Each occurrence of the resulting event will contain the index of
130−-- the current interval, with 0 representing the basis time.
131−-- Automatically begin sending events when the DOM is built
132−poissonLossy
133− :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
134− => g
135− -> Double
136− -- ^ Poisson event rate (Hz)
137− -> UTCTime
138− -- ^ Baseline time for events
139− -> m (Event t TickInfo)
140−poissonLossy rnd rate t0 = poissonLossyFrom rnd rate t0 =<< getPostBuild
141−
142−-- | Send events with inhomogeneous Poisson timing with the given basis
143−-- and variable rate. Provide a maxRate that you expect to support.
144−inhomogeneousPoissonFrom
145− :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
146− => g
147− -> Behavior t Double
148− -> Double
149− -> UTCTime
150− -> Event t a
151− -> m (Event t TickInfo)
152−inhomogeneousPoissonFrom rnd rate maxRate t0 e = do
153−
154− -- Create a thread for producing homogeneous poisson events
155− -- along with random Doubles (usage of Double's explained below)
156− ticksWithRateRand <- performEventAsync $
157− fmap callAtNextInterval e
158−
159− -- Filter homogeneous events according to associated
160− -- random values and the current rate parameter
161− return $ attachWithMaybe filterFun rate ticksWithRateRand
162−
163− where
164−
165− -- Inhomogeneous poisson processes are built from faster
166− -- homogeneous ones by randomly dropping events from the
167− -- fast process. For each fast homogeneous event, choose
168− -- a uniform random sample from (0, rMax). If the
169− -- inhomogeneous rate at this moment is greater than the
170− -- random sample, then keep this event, otherwise drop it
171− filterFun :: Double -> (TickInfo, Double) -> Maybe TickInfo
172− filterFun r (tInfo, p)
173− | r >= p = Just tInfo
174− | otherwise = Nothing
175−
176− callAtNextInterval _ cb = void $ liftIO $ forkIO $ go t0 rnd cb 0
177−
178− go tTargetLast lastGen cb lastN = do
179− t <- getCurrentTime
180−
181− -- Generate random numbers for this poisson interval (u)
182− -- and sample-retention likelihood (p)
183− let (u, nextGen) = randomR (0,1) lastGen
184− (p :: Double, nextGen') = randomR (0,maxRate) nextGen
185−
186− -- Inter-event interval is drawn from exponential
187− -- distribution accourding to u
188− let dt = realToFrac $ (-1) * log u / maxRate :: NominalDiffTime
189− nEvents = lastN + 1
190− alreadyElapsed = diffUTCTime t tTargetLast
191− tTarget = addUTCTime dt tTargetLast
192− thisDelay = realToFrac $ diffUTCTime tTarget t :: Double
193− Concurrent.delay $ ceiling $ thisDelay * 1000000
194− _ <- cb (TickInfo t nEvents alreadyElapsed, p)
195− go tTarget nextGen' cb nEvents
196−
197−-- | Send events with inhomogeneous Poisson timing with the given basis
198−-- and variable rate. Provide a maxRate that you expect to support
199−inhomogeneousPoisson
200− :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
201− => g
202− -> Behavior t Double
203− -> Double
204− -> UTCTime
205− -> m (Event t TickInfo)
206−inhomogeneousPoisson rnd rate maxRate t0 =
207− inhomogeneousPoissonFrom rnd rate maxRate t0 =<< getPostBuild
208−
209−-- | Block occurrences of an Event until the given number of seconds elapses without
210−-- the Event firing, at which point the last occurrence of the Event will fire.
211−debounce :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
212−debounce dt e = do
213− n :: Dynamic t Integer <- count e
214− let tagged = attachPromptlyDynWith (,) n e
215− delayed <- delay dt tagged
216− return $ attachWithMaybe (\n' (t, v) -> if n' == t then Just v else Nothing) (current n) delayed
217−
218−-- | When the given 'Event' occurs, wait the given amount of time and collect
219−-- all occurrences during that time. Then, fire the output 'Event' with the
220−-- collected output.
221−batchOccurrences :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t (Seq a))
222−batchOccurrences t newValues = do
223− let f s x = (Just newState, out)
224− where newState = case x of
225− This a -> s |> a
226− That _ -> mempty
227− These a _ -> Seq.singleton a
228− out = case x of
229− This _ -> if Seq.null s then Just () else Nothing
230− That _ -> Nothing
231− These _ _ -> Just ()
232− rec (buffer, toDelay) <- mapAccumMaybe f mempty $ align newValues delayed
233− delayed <- delay t toDelay
234− return $ tag buffer delayed
235−
236−-- | Throttle an input event, ensuring that at least a given amount of time passes between occurrences of the output event. If the input event occurs too
237−-- frequently, the output event occurs with the most recently seen input value after the given delay passes since the last occurrence of the output.
238−-- If the output event has not occurred recently, occurrences of the input event will cause the output event to fire immediately.
239−throttle :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
240−throttle t e = do
241− let f (immediate, buffer) x = case x of -- (Just newState, out)
242− This a -- If only the input event fires
243− | immediate -> -- and we're in immediate mode
244− -- Immediate mode turns off, and the buffer is empty.
245− -- We fire the output event with the input event value immediately.
246− (Just (False, Nothing), Just a)
247− | otherwise -> -- and we're not in immediate mode
248− -- Immediate mode remains off, and we replace the contents of the buffer (if any) with the input value.
249− -- We don't fire the output event.
250− (Just (False, Just a), Nothing)
251− That _ -> -- If only the delayed output event fires,
252− case buffer of
253− Nothing -> -- and the buffer is empty:
254− -- Immediate mode turns back on, and the buffer remains empty.
255− -- We don't fire.
256− (Just (True, Nothing), Nothing)
257− Just b -> -- and the buffer is full:
258− -- Immediate mode remains off, and the buffer is cleared.
259− -- We fire with the buffered value.
260− (Just (False, Nothing), Just b)
261− These a _ -> -- If both the input and delayed output event fire simultaneously:
262− -- Immediate mode turns off, and the buffer is empty.
263− -- We fire with the input event's value, as it is the most recent we have seen at this moment.
264− (Just (False, Nothing), Just a)
265− rec (_, outE) <- mapAccumMaybeDyn f (True, Nothing) $ align e delayed -- We start in immediate mode with an empty buffer.
266− delayed <- delay t outE
267− return outE
268−
269−#ifdef USE_TEMPLATE_HASKELL
270−makeLensesWith (lensRules & simpleLenses .~ True) ''TickInfo
271−#else
272−tickInfo_lastUTC :: Lens' TickInfo UTCTime
273−tickInfo_lastUTC f (TickInfo x1 x2 x3) = (\y -> TickInfo y x2 x3) <$> f x1
274−{-# INLINE tickInfo_lastUTC #-}
275−
276−tickInfo_n :: Lens' TickInfo Integer
277−tickInfo_n f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 y x3) <$> f x2
278−{-# INLINE tickInfo_n #-}
279−
280−tickInfo_alreadyElapsed :: Lens' TickInfo NominalDiffTime
281−tickInfo_alreadyElapsed f (TickInfo x1 x2 x3) = (\y -> TickInfo x1 x2 y) <$> f x3
282−{-# INLINE tickInfo_alreadyElapsed #-}
283−#endif
284−
src/Reflex/TriggerEvent/Base.hsdeleted−148
@@ -1,148 +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.Applicative (liftA2)
17−import Control.Concurrent
18−import Control.Monad.Exception
19−import Control.Monad.Primitive
20−import Control.Monad.Reader
21−import Control.Monad.Ref
22−import Data.Coerce
23−import Data.Dependent.Sum
24−import Data.IORef
25−import Data.Monoid ((<>))
26−import qualified Data.Semigroup as S
27−import Reflex.Class
28−import Reflex.Adjustable.Class
29−import Reflex.Host.Class
30−import Reflex.PerformEvent.Class
31−import Reflex.PostBuild.Class
32−import Reflex.TriggerEvent.Class
33−
34−-- | A value with which to fire an 'Event', as well as a callback to invoke
35−-- after its propagation has completed.
36−data TriggerInvocation a = TriggerInvocation a (IO ())
37−
38−-- | A reference to an 'EventTrigger' suitable for firing with 'TriggerEventT'.
39−newtype EventTriggerRef t a = EventTriggerRef { unEventTriggerRef :: IORef (Maybe (EventTrigger t a)) }
40−
41−-- | A basic implementation of 'TriggerEvent'.
42−newtype TriggerEventT t m a = TriggerEventT { unTriggerEventT :: ReaderT (Chan [DSum (EventTriggerRef t) TriggerInvocation]) m a }
43− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
44−
45−-- | Run a 'TriggerEventT' action. The argument should be a 'Chan' into which
46−-- 'TriggerInvocation's can be passed; it is expected that some other thread
47−-- will be responsible for popping values out of the 'Chan' and firing their
48−-- 'EventTrigger's.
49−runTriggerEventT :: TriggerEventT t m a -> Chan [DSum (EventTriggerRef t) TriggerInvocation] -> m a
50−runTriggerEventT = runReaderT . unTriggerEventT
51−
52−instance MonadTrans (TriggerEventT t) where
53− {-# INLINABLE lift #-}
54− lift = TriggerEventT . lift
55−
56−instance PrimMonad m => PrimMonad (TriggerEventT t m) where
57− type PrimState (TriggerEventT t m) = PrimState m
58− {-# INLINABLE primitive #-}
59− primitive = lift . primitive
60−
61−instance PerformEvent t m => PerformEvent t (TriggerEventT t m) where
62− type Performable (TriggerEventT t m) = Performable m
63− {-# INLINABLE performEvent_ #-}
64− performEvent_ e = lift $ performEvent_ e
65− {-# INLINABLE performEvent #-}
66− performEvent e = lift $ performEvent e
67−
68−instance PostBuild t m => PostBuild t (TriggerEventT t m) where
69− {-# INLINABLE getPostBuild #-}
70− getPostBuild = lift getPostBuild
71−
72−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (TriggerEventT t m) where
73− {-# INLINABLE newEventWithTrigger #-}
74− newEventWithTrigger = lift . newEventWithTrigger
75− {-# INLINABLE newFanEventWithTrigger #-}
76− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
77−
78−instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (TriggerEventT t m) where
79− {-# INLINABLE newTriggerEvent #-}
80− newTriggerEvent = do
81− (e, t) <- newTriggerEventWithOnComplete
82− return (e, \a -> t a $ return ())
83− {-# INLINABLE newTriggerEventWithOnComplete #-}
84− newTriggerEventWithOnComplete = do
85− events <- askEvents
86− (eResult, reResultTrigger) <- lift newEventWithTriggerRef
87− return . (,) eResult $ \a cb ->
88− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
89− {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
90− newEventWithLazyTriggerWithOnComplete f = do
91− events <- askEvents
92− lift . newEventWithTrigger $ \t ->
93− f $ \a cb -> do
94− reResultTrigger <- newRef $ Just t
95− writeChan events [EventTriggerRef reResultTrigger :=> TriggerInvocation a cb]
96−
97−instance MonadRef m => MonadRef (TriggerEventT t m) where
98− type Ref (TriggerEventT t m) = Ref m
99− {-# INLINABLE newRef #-}
100− newRef = lift . newRef
101− {-# INLINABLE readRef #-}
102− readRef = lift . readRef
103− {-# INLINABLE writeRef #-}
104− writeRef r = lift . writeRef r
105−
106−instance MonadAtomicRef m => MonadAtomicRef (TriggerEventT t m) where
107− {-# INLINABLE atomicModifyRef #-}
108− atomicModifyRef r = lift . atomicModifyRef r
109−
110−instance MonadSample t m => MonadSample t (TriggerEventT t m) where
111− {-# INLINABLE sample #-}
112− sample = lift . sample
113−
114−instance MonadHold t m => MonadHold t (TriggerEventT t m) where
115− {-# INLINABLE hold #-}
116− hold v0 v' = lift $ hold v0 v'
117− {-# INLINABLE holdDyn #-}
118− holdDyn v0 v' = lift $ holdDyn v0 v'
119− {-# INLINABLE holdIncremental #-}
120− holdIncremental v0 v' = lift $ holdIncremental v0 v'
121− {-# INLINABLE buildDynamic #-}
122− buildDynamic a0 = lift . buildDynamic a0
123− {-# INLINABLE headE #-}
124− headE = lift . headE
125−
126−instance Adjustable t m => Adjustable t (TriggerEventT t m) where
127− {-# INLINABLE runWithReplace #-}
128− runWithReplace (TriggerEventT a0) a' = TriggerEventT $ runWithReplace a0 (coerceEvent a')
129− {-# INLINABLE traverseIntMapWithKeyWithAdjust #-}
130− traverseIntMapWithKeyWithAdjust f dm0 dm' = TriggerEventT $ traverseIntMapWithKeyWithAdjust (coerce . f) dm0 dm'
131− {-# INLINABLE traverseDMapWithKeyWithAdjust #-}
132− traverseDMapWithKeyWithAdjust f dm0 dm' = TriggerEventT $ traverseDMapWithKeyWithAdjust (coerce . f) dm0 dm'
133− {-# INLINABLE traverseDMapWithKeyWithAdjustWithMove #-}
134− traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = TriggerEventT $ traverseDMapWithKeyWithAdjustWithMove (coerce . f) dm0 dm'
135−
136−-- TODO: Monoid and Semigroup can likely be derived once ReaderT has them.
137−instance (Monoid a, Applicative m) => Monoid (TriggerEventT t m a) where
138− mempty = pure mempty
139− mappend = (<>)
140−
141−instance (S.Semigroup a, Applicative m) => S.Semigroup (TriggerEventT t m a) where
142− (<>) = liftA2 (S.<>)
143−
144−
145−-- | Retrieve the current 'Chan'; event trigger invocations pushed into it will
146−-- be fired.
147−askEvents :: Monad m => TriggerEventT t m (Chan [DSum (EventTriggerRef t) TriggerInvocation])
148−askEvents = TriggerEventT ask
src/Reflex/TriggerEvent/Class.hsdeleted−50
@@ -1,50 +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−import Control.Monad.State
15−import qualified Control.Monad.State.Strict as Strict
16−
17−--TODO: Shouldn't have IO hard-coded
18−-- | 'TriggerEvent' represents actions that can create 'Event's that can be
19−-- triggered by 'IO' actions.
20−class Monad m => TriggerEvent t m | m -> t where
21− -- | Create a triggerable 'Event'. Whenever the resulting function is called,
22− -- the resulting 'Event' will fire at some point in the future. Note that
23− -- this may not be synchronous.
24− newTriggerEvent :: m (Event t a, a -> IO ())
25− -- | Like 'newTriggerEvent', but the callback itself takes another callback,
26− -- to be invoked once the requested 'Event' occurrence has finished firing.
27− -- This allows synchronous operation.
28− newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
29− -- | Like 'newTriggerEventWithOnComplete', but with setup and teardown. This
30− -- relatively complex type signature allows any external listeners to be
31− -- subscribed lazily and then removed whenever the returned 'Event' is no
32− -- longer being listened to. Note that the setup/teardown may happen multiple
33− -- times, and there is no guarantee that the teardown will be executed
34− -- promptly, or even at all, in the case of program termination.
35− newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
36−
37−instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
38− newTriggerEvent = lift newTriggerEvent
39− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
40− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
41−
42−instance TriggerEvent t m => TriggerEvent t (StateT s m) where
43− newTriggerEvent = lift newTriggerEvent
44− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
45− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
46−
47−instance TriggerEvent t m => TriggerEvent t (Strict.StateT s m) where
48− newTriggerEvent = lift newTriggerEvent
49− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
50− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
src/Reflex/Widget/Basic.hsdeleted−44
@@ -1,44 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ExplicitForAll #-}
3−{-# LANGUAGE MultiParamTypeClasses #-}
4−{-# LANGUAGE RecursiveDo #-}
5−#ifdef USE_REFLEX_OPTIMIZER
6−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7−#endif
8−
9−module Reflex.Widget.Basic where
10−
11−import Control.Monad.Fix (MonadFix)
12−import Data.Map (Map)
13−
14−import Reflex.Class
15−import Reflex.Adjustable.Class
16−import Reflex.Patch.MapWithMove
17−
18−
19−-- | Build sortable content in such a way that re-sorting it can cause minimal
20−-- disruption to an existing context.
21−--
22−-- Naively re-sorting a list of images would destroy every image and add them back
23−-- in the new order. This framework is able to avoid that by preserving the
24−-- identity of each image and simply moving it to the new location.
25−--
26−-- Example:
27−--
28−-- > let sortByFst = buttonA $> comparing fst
29−-- > sortBySnd = buttonB $> comparing snd
30−-- > sortEvent = leftmost [sortByFst, sortBySnd]
31−-- > sortableList
32−-- > (\k v -> text $ "\n" ++ show k ++ " " ++ v) -- show each element on a new line
33−-- > (Map.fromList $ zip [0..] [(3, "a"), (2, "b"), (1, "c")])
34−-- > sortEvent
35−sortableList :: forall t m k v a. (MonadHold t m, MonadFix m, Adjustable t m, Ord k)
36− => (k -> v -> m a) -- ^ Function to render the content for each key/value pair
37− -> Map k v -- ^ The sortable list with an initial ordering determined by the @Map@ keys in ascending order
38− -> Event t (v -> v -> Ordering) -- ^ An event carrying a sort function for the list
39− -> m (Map k a)
40−sortableList f m0 reSortFunc = do
41− rec let reSortPatch = attachWith (flip patchThatSortsMapWith) (currentIncremental m) reSortFunc
42− m <- holdIncremental m0 reSortPatch
43− (results, _) <- mapMapWithAdjustWithMove f m0 reSortPatch
44− pure results
src/Reflex/Workflow.hsdeleted−38
@@ -1,38 +0,0 @@
1−{-# LANGUAGE RecursiveDo #-}
2−{-# LANGUAGE ScopedTypeVariables #-}
3−module Reflex.Workflow (
4− -- * Workflows
5− Workflow (..)
6− , workflow
7− , workflowView
8− , mapWorkflow
9− , mapWorkflowCheap
10− ) where
11−
12−import Control.Arrow ((***))
13−import Control.Monad.Fix (MonadFix)
14−
15−import Reflex.Class
16−import Reflex.Adjustable.Class
17−import Reflex.Network
18−import Reflex.NotReady.Class
19−import Reflex.PostBuild.Class
20−
21−newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
22−
23−workflow :: forall t m a. (Reflex t, Adjustable t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
24−workflow w0 = do
25− rec eResult <- networkHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
26− return $ fmap fst eResult
27−
28−workflowView :: forall t m a. (Reflex t, NotReady t m, Adjustable t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
29−workflowView w0 = do
30− rec eResult <- networkView . fmap unWorkflow =<< holdDyn w0 eReplace
31− eReplace <- fmap switch $ hold never $ fmap snd eResult
32− return $ fmap fst eResult
33−
34−mapWorkflow :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
35−mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
36−
37−mapWorkflowCheap :: (Reflex t, Functor m) => (a -> b) -> Workflow t m a -> Workflow t m b
38−mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
test/EventWriterT.hsdeleted−114
@@ -1,114 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE RecursiveDo #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−module Main where
6−
7−import Control.Lens
8−import Control.Monad
9−import Control.Monad.Fix
10−import qualified Data.Dependent.Map as DMap
11−import Data.Functor.Misc
12−import qualified Data.Map as M
13−import Data.These
14−
15−import Reflex
16−import Reflex.EventWriter.Base
17−import Test.Run
18−
19−main :: IO ()
20−main = do
21− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
22− [ Just ()
23− ]
24− print os1
25− os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
26− <- runApp' (unwrapApp testSimultaneous) $ map Just $
27− [ This ()
28− , That ()
29− , This ()
30− , These () ()
31− ]
32− print os2
33− os3@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEvent) [Just ()]
34− print os3
35− os4@[[Nothing, Just [2]]] <- runApp' (unwrapApp testMoribundTellEventDMap) [Just ()]
36− print os4
37− os5@[[Nothing, Just [1, 2]]] <- runApp' (unwrapApp testLiveTellEventDMap) [Just ()]
38− print os5
39− return ()
40−
41−unwrapApp :: (Reflex t, Monad m) => (a -> EventWriterT t [Int] m ()) -> a -> m (Event t [Int])
42−unwrapApp x appIn = do
43− ((), e) <- runEventWriterT $ x appIn
44− return e
45−
46−testOrdering :: (Reflex t, Monad m) => Event t () -> EventWriterT t [Int] m ()
47−testOrdering pulse = do
48− forM_ [10,9..1] $ \i -> tellEvent ([i] <$ pulse)
49− return ()
50−
51−testSimultaneous :: (Reflex t, Adjustable t m, MonadHold t m) => Event t (These () ()) -> EventWriterT t [Int] m ()
52−testSimultaneous pulse = do
53− let e0 = fmapMaybe (^? here) pulse
54− e1 = fmapMaybe (^? there) pulse
55− forM_ [1,3..9] $ \i -> runWithReplace (tellEvent ([i] <$ e0)) $ ffor e1 $ \_ -> tellEvent ([i+1] <$ e0)
56− return ()
57−
58−-- | Test that a widget telling and event which fires at the same time it has been replaced
59−-- doesn't count along with the new widget.
60−testMoribundTellEvent
61− :: forall t m
62− . ( Reflex t
63− , Adjustable t m
64− , MonadHold t m
65− , MonadFix m
66− )
67− => Event t ()
68− -> EventWriterT t [Int] m ()
69−testMoribundTellEvent pulse = do
70− rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
71− tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
72− (_, rwrFinished) <- runWithReplace (tellIntOnReplace 1) $ tellIntOnReplace 2 <$ pulse
73− return ()
74−
75−-- | The equivalent of 'testMoribundTellEvent' for 'traverseDMapWithKeyWithAdjust'.
76−testMoribundTellEventDMap
77− :: forall t m
78− . ( Reflex t
79− , Adjustable t m
80− , MonadHold t m
81− , MonadFix m
82− )
83− => Event t ()
84− -> EventWriterT t [Int] m ()
85−testMoribundTellEventDMap pulse = do
86− rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
87− tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
88− (_, rwrFinished :: Event t (PatchDMap (Const2 () Int) Identity)) <-
89− traverseDMapWithKeyWithAdjust
90− (\(Const2 ()) (Identity v) -> Identity . const v <$> tellIntOnReplace v)
91− (mapToDMap $ M.singleton () 1)
92− ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton () 2) <$ pulse)
93− return ()
94−
95−-- | Ensures that elements which are _not_ removed can still fire 'tellEvent's
96−-- during the same frame as other elements are updated.
97−testLiveTellEventDMap
98− :: forall t m
99− . ( Reflex t
100− , Adjustable t m
101− , MonadHold t m
102− , MonadFix m
103− )
104− => Event t ()
105− -> EventWriterT t [Int] m ()
106−testLiveTellEventDMap pulse = do
107− rec let tellIntOnReplace :: Int -> EventWriterT t [Int] m ()
108− tellIntOnReplace x = tellEvent $ [x] <$ rwrFinished
109− (_, rwrFinished :: Event t (PatchDMap (Const2 Int ()) Identity)) <-
110− traverseDMapWithKeyWithAdjust
111− (\(Const2 k) (Identity ()) -> Identity <$> tellIntOnReplace k)
112− (mapToDMap $ M.singleton 1 ())
113− ((PatchDMap $ DMap.map (ComposeMaybe . Just) $ mapToDMap $ M.singleton 2 ()) <$ pulse)
114− return ()
test/GC.hsdeleted−100
@@ -1,100 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−{-# LANGUAGE RankNTypes #-}
5−{-# LANGUAGE ScopedTypeVariables #-}
6−{-# LANGUAGE TypeFamilies #-}
7−module Main where
8−
9−import Control.Monad
10−import Control.Monad.IO.Class
11−import Control.Monad.Ref
12−import Data.Align
13−import Data.Dependent.Map (DMap)
14−import qualified Data.Dependent.Map as DMap
15−import Data.Dependent.Sum
16−import Data.Functor.Identity
17−import Data.GADT.Compare
18−import Data.IORef
19−import Data.Semigroup
20−import Data.These
21−
22−import Data.Functor.Misc
23−import Reflex.Patch
24−
25−import qualified Reflex.Host.Class as Host
26−import qualified Reflex.Spider.Internal as S
27−
28−import System.Exit
29−import System.Mem
30−
31−main :: IO ()
32−main = do
33− ref <- newIORef Nothing
34− hostPerf ref
35− readIORef ref >>= maybe exitFailure (putStrLn . ("Result " <>) . show)
36−
37−hostPerf :: IORef (Maybe Int) -> IO ()
38−hostPerf ref = S.runSpiderHost $ do
39− ---- body
40− liftIO $ putStrLn "#creating triggers"
41− (response', responseTrigger) <- Host.newEventWithTriggerRef
42− (eadd', addTriggerRef) <- Host.newEventWithTriggerRef
43− let response = S.unSpiderEvent response'
44− eadd = S.unSpiderEvent eadd'
45− liftIO $ putStrLn "#creating event graph"
46− eventToPerform <- Host.runHostFrame $ do
47− (reqMap :: S.Event S.Global (DMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) Identity))
48− <- S.SpiderHostFrame
49− $ fmap ( S.merge
50− . S.dynamicHold)
51− $ S.hold DMap.empty
52− -- Construct a new heap object for the subscriber, invalidating any weak references to the subscriber if they are not retained
53− $ (\e -> S.Event $ \sub -> do
54− (s, o) <- S.subscribeAndRead e $ sub
55− { S.subscriberPropagate = S.subscriberPropagate sub
56− }
57− return (s, o))
58− $ runIdentity <$> S.select
59− (S.fan $ S.pushCheap (return . Just . mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e)) response)
60− (WrapArg Request)
61− return $ alignWith (mergeThese (<>))
62− (flip S.pushCheap eadd $ \_ -> return $ Just $ DMap.singleton Request $ do
63− liftIO $ putStrLn "#eadd fired"
64− return $ PatchDMap $ DMap.singleton (Const2 (1 :: Int)) $ ComposeMaybe $ Just
65− $ S.pushCheap (return . Just . DMap.singleton Action . (\_ -> liftIO (writeIORef ref (Just 1)))) $ eadd)
66− (flip S.pushCheap reqMap $ \m -> return $ Just $ mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m)
67− ---- epilogue
68− eventToPerformHandle <- Host.subscribeEvent (S.SpiderEvent eventToPerform)
69− liftIO $ putStrLn "#performing GC" >> performMajorGC
70− liftIO $ putStrLn "#attempting to fire eadd"
71− mAddTrigger <- readRef addTriggerRef
72− forM_ mAddTrigger $ \t -> replicateM_ 2 $ do
73− liftIO $ putStrLn "#firing eadd"
74− mToPerform <- Host.fireEventsAndRead [t :=> Identity ()] $ sequence =<< Host.readEvent eventToPerformHandle
75− case mToPerform of
76− Nothing -> return ()
77− Just toPerform -> do
78− responses <- Host.runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
79− mrt <- readRef responseTrigger
80− let followupEventTriggers = case mrt of
81− Just rt -> [rt :=> Identity responses]
82− Nothing -> []
83− Host.fireEventsAndRead followupEventTriggers $ return ()
84− return ()
85− return ()
86−
87−data Tell a where
88− Action :: Tell ()
89− Request :: Tell (PatchDMap (Const2 Int (DMap Tell (S.SpiderHostFrame S.Global))) (S.Event S.Global))
90−
91−instance GEq Tell where
92− geq Action Action = Just Refl
93− geq Request Request = Just Refl
94− geq _ _ = Nothing
95−
96−instance GCompare Tell where
97− gcompare Action Action = GEQ
98− gcompare Request Request = GEQ
99− gcompare Action Request = GLT
100− gcompare Request Action = GGT
test/QueryT.hsdeleted−133
@@ -1,133 +0,0 @@
1−{-# LANGUAGE DeriveFunctor #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4−{-# LANGUAGE LambdaCase #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE TypeFamilies #-}
7−{-# LANGUAGE UndecidableInstances #-}
8−
9−import Control.Lens
10−import Control.Monad.Fix
11−import Data.Align
12−import Data.AppendMap () -- for the Align instance
13−import qualified Data.AppendMap as AMap
14−import Data.Functor.Misc
15−import Data.Map (Map)
16−import qualified Data.Map as Map
17−import Data.Map.Monoidal (MonoidalMap)
18−import Data.Semigroup
19−import Data.These
20−
21−import Reflex
22−import Reflex.Patch.MapWithMove
23−import Test.Run
24−
25−newtype MyQuery = MyQuery SelectedCount
26− deriving (Show, Read, Eq, Ord, Monoid, Semigroup, Additive, Group)
27−
28−instance Query MyQuery where
29− type QueryResult MyQuery = ()
30− crop _ _ = ()
31−
32−instance (Ord k, Query a, Eq (QueryResult a), Align (MonoidalMap k)) => Query (Selector k a) where
33− type QueryResult (Selector k a) = Selector k (QueryResult a)
34− crop q r = undefined
35−
36−newtype Selector k a = Selector { unSelector :: MonoidalMap k a }
37− deriving (Show, Read, Eq, Ord, Functor)
38−
39−instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Semigroup (Selector k a) where
40− (Selector a) <> (Selector b) = Selector $ fmapMaybe id $ f a b
41− where
42− f = alignWith $ \case
43− This x -> Just x
44− That y -> Just y
45− These x y ->
46− let z = x `mappend` y
47− in if z == mempty then Nothing else Just z
48−
49−instance (Ord k, Eq a, Monoid a, Align (MonoidalMap k)) => Monoid (Selector k a) where
50− mempty = Selector AMap.empty
51− mappend = (<>)
52−
53−instance (Eq a, Ord k, Group a, Align (MonoidalMap k)) => Group (Selector k a) where
54− negateG = fmap negateG
55−
56−instance (Eq a, Ord k, Group a, Align (MonoidalMap k)) => Additive (Selector k a)
57−
58−main :: IO ()
59−main = do
60− [0, 1, 1, 0] <- fmap (map fst . concat) $
61− runApp (testQueryT testRunWithReplace) () $ map (Just . That) $
62− [ That (), This (), That () ]
63− [0, 1, 1, 0] <- fmap (map fst . concat) $
64− runApp (testQueryT testSequenceDMapWithAdjust) () $ map (Just . That) $
65− [ That (), This (), That () ]
66− [0, 1, 1, 0] <- fmap (map fst . concat) $
67− runApp (testQueryT testSequenceDMapWithAdjustWithMove) () $ map (Just . That) $
68− [ That (), This (), That () ]
69− return ()
70−
71−testQueryT :: (Reflex t, MonadFix m)
72− => (Event t () -> Event t () -> QueryT t (Selector Int MyQuery) m ())
73− -> AppIn t () (These () ())
74− -> m (AppOut t Int Int)
75−testQueryT w (AppIn _ pulse) = do
76− let replace = fmapMaybe (^? here) pulse
77− increment = fmapMaybe (^? there) pulse
78− (_, q) <- runQueryT (w replace increment) $ pure mempty
79− let qDyn = head . AMap.keys . unSelector <$> incrementalToDynamic q
80− return $ AppOut
81− { _appOut_behavior = current qDyn
82− , _appOut_event = updated qDyn
83− }
84−
85−testRunWithReplace :: ( Reflex t
86− , Adjustable t m
87− , MonadHold t m
88− , MonadFix m
89− , MonadQuery t (Selector Int MyQuery) m)
90− => Event t ()
91− -> Event t ()
92− -> m ()
93−testRunWithReplace replace increment = do
94− let w = do
95− n <- count increment
96− queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
97− _ <- runWithReplace w $ w <$ replace
98− return ()
99−
100−testSequenceDMapWithAdjust :: ( Reflex t
101− , Adjustable t m
102− , MonadHold t m
103− , MonadFix m
104− , MonadQuery t (Selector Int MyQuery) m)
105− => Event t ()
106− -> Event t ()
107− -> m ()
108−testSequenceDMapWithAdjust replace increment = do
109− _ <- listHoldWithKey (Map.singleton () ()) (Map.singleton () (Just ()) <$ replace) $ \_ _ -> do
110− n <- count increment
111− queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
112− return ()
113−
114−testSequenceDMapWithAdjustWithMove :: ( Reflex t
115− , Adjustable t m
116− , MonadHold t m
117− , MonadFix m
118− , MonadQuery t (Selector Int MyQuery) m)
119− => Event t ()
120− -> Event t ()
121− -> m ()
122−testSequenceDMapWithAdjustWithMove replace increment = do
123− _ <- listHoldWithKeyWithMove (Map.singleton () ()) (Map.singleton () (Just ()) <$ replace) $ \_ _ -> do
124− n <- count increment
125− queryDyn $ zipDynWith (\x y -> Selector (AMap.singleton (x :: Int) y)) n $ pure $ MyQuery $ SelectedCount 1
126− return ()
127−
128−-- scam it out to test traverseDMapWithAdjustWithMove
129−listHoldWithKeyWithMove :: forall t m k v a. (Ord k, MonadHold t m, Adjustable t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
130−listHoldWithKeyWithMove m0 m' f = do
131− (n0, n') <- mapMapWithAdjustWithMove f m0 $ ffor m' $ PatchMapWithMove . Map.map (\v -> NodeInfo (maybe From_Delete From_Insert v) Nothing)
132− incrementalToDynamic <$> holdIncremental n0 n'
133−-- -}
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.foldrWithKey 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−72
@@ -1,72 +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− buildDynamic getInitial = liftPlan . buildDynamic getInitial
43− headE = liftPlan . headE
44−
45−instance MonadSample (Pure Int) PurePlan where
46− sample = liftPlan . sample
47−
48−
49−instance TestPlan (Pure Int) PurePlan where
50− plan occs = do
51− PurePlan . modify $ IntSet.union (IntMap.keysSet m)
52− return $ mapToPureEvent m
53− where m = IntMap.fromList (first fromIntegral <$> occs)
54−
55−runPure :: PurePlan a -> (a, IntSet)
56−runPure (PurePlan p) = runStateT p mempty $ 0
57−
58−relavantTimes :: IntSet -> IntSet
59−relavantTimes occs = IntSet.fromList [0..l + 1]
60− where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
61−
62−testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
63−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
64−
65−testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
66−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
67−
68−
69−
70−
71−
72−
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 occurrences = 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 <$> occurrences)
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−257
@@ -1,257 +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 RecursiveDo #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
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.Pure as P
23−import qualified Reflex.Spider.Internal as S
24−
25−import Control.Arrow (second, (&&&))
26−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
27−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28−import Data.Dependent.Map (DSum (..))
29−import Data.Foldable
30−import Data.Map.Strict (Map)
31−import qualified Data.Map.Strict as Map
32−import qualified Data.Set as Set
33−import Data.Monoid
34−import Data.Traversable
35−import System.Exit
36−import System.Mem
37−
38−import System.IO.Unsafe
39−
40−mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
41−mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
42− Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
43− Just (_, v) -> v
44−
45−mapToPureEvent :: Map Int a -> Event PureReflexDomain a
46−mapToPureEvent m = P.Event $ flip Map.lookup m
47−
48−relevantTestingTimes :: (Map Int a, Map Int b) -> [Int]
49−relevantTestingTimes (b, e) = case Set.minView &&& Set.maxView $ Map.keysSet b `Set.union` Map.keysSet e of
50− (Just (t0, _), Just (t1, _)) -> [t0..t1+1] -- We need to go to b+1 to see the result of the final event
51− _ -> [] -- Doesn't actually make much sense
52−
53−type PureReflexDomain = P.Pure Int
54−type TimeM = (->) Int
55−
56−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)
57−testPure builder (b, e) =
58− let (P.Behavior b', P.Event e') = ($ 0) $ builder (mapToPureBehavior b, mapToPureEvent e)
59− relevantTimes = relevantTestingTimes (b, e)
60− e'' = Map.mapMaybe id $ Map.fromList $ map (id &&& e') relevantTimes
61− b'' = Map.fromList $ map (id &&& b') relevantTimes
62− in (b'', e'')
63−
64−class MapMSignals a a' t t' | a -> t, a' -> t', a t' -> a', a' t -> a where
65− mapMSignals :: Monad m => (forall b. Behavior t b -> m (Behavior t' b)) -> (forall b. Event t b -> m (Event t' b)) -> a -> m a'
66−
67−instance MapMSignals (Behavior t a) (Behavior t' a) t t' where
68− mapMSignals fb _ = fb
69−
70−instance MapMSignals (Event t a) (Event t' a) t t' where
71− mapMSignals _ fe = fe
72−
73−instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
74− mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
75−
76−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)
77−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
78− (re, reTrigger) <- newEventWithTriggerRef
79− (rb, rbTrigger) <- newEventWithTriggerRef
80− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
81− (b', e') <- runHostFrame $ builder (b, re)
82− e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
83− let times = relevantTestingTimes (bMap, eMap)
84− liftIO performGC
85− outputs <- forM times $ \t -> do
86− forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
87− bOutput <- sample b'
88− eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
89− mret <- readRef reTrigger
90− let firing = case mret of
91− Just ret -> [ret :=> Identity val]
92− Nothing -> []
93− fireEventsAndRead firing $ sequence =<< readEvent e'Handle
94− liftIO performGC
95− return (t, (bOutput, eOutput))
96− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
97−
98−tracePerf :: Show a => a -> b -> b
99−tracePerf = flip const
100−
101−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
102−testAgreement builder inputs = do
103− let identityResult = testPure builder inputs
104− tracePerf "---------" $ return ()
105− let spiderResult = testSpider builder inputs
106− tracePerf "---------" $ return ()
107− let resultsAgree = identityResult == spiderResult
108− if resultsAgree
109− then do --putStrLn "Success:"
110− --print identityResult
111− return ()
112− else do putStrLn "Failure:"
113− putStrLn $ "Pure result: " <> show identityResult
114− putStrLn $ "Spider result: " <> show spiderResult
115− return resultsAgree
116−
117−type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
118−
119−data TestCase = forall a b c d. (Eq c, Eq d, Show c, Show d) => TestCase (Map Int a, Map Int b) (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d))
120−
121−testCases :: [(String, TestCase)]
122−testCases =
123− [ (,) "hold" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
124− b' <- hold "123" e
125− return (b', e)
126− , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
127− e' <- updated <$> count e
128− b' <- hold (0 :: Int) e'
129− return (b', e')
130− , (,) "headE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
131− e' <- headE $ leftmost [e, e]
132− return (b, e')
133− , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134− let e' = fmap (const e) e
135− b' <- hold never e'
136− let e'' = switch b'
137− return (b, e'')
138− , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
139− let e' = flip pushAlways e $ const $ do
140− let eab = splitRecombineEvent e
141− switch <$> hold eab never
142− e'' = coincidence e'
143− return (b, e'')
144− , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145− let e' = flip pushAlways e $ const $ do
146− let eab = splitRecombineEvent e
147− switch <$> hold eab (fmap (const e) e)
148− e'' = coincidence e'
149− return (b, e'')
150− , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
151− let e' = leftmost [e, e]
152− e'' <- switch <$> hold e' (fmap (const e) e)
153− return (b, e'')
154− , (,) "switchHoldPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
155− let e' = fmap (const e) e
156− e'' <- switchHoldPromptly never e'
157− return (b, e'')
158− , (,) "switchHoldPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
159− let e' = fmap (const e) e
160− e'' <- switchHoldPromptly never $ leftmost [e', e']
161− return (b, e'')
162− , (,) "switchHoldPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
163− let e' = leftmost [e, e]
164− e'' <- switchHoldPromptly never (fmap (const e) e')
165− return (b, e'')
166− , (,) "switchHoldPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
167− let e' = leftmost [e, e]
168− e'' <- switchHoldPromptly never (fmap (const e') e)
169− return (b, e'')
170− , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171− let e' = leftmost [e, e]
172− e'' <- switch <$> hold never (fmap (const e') e)
173− return (b, e'')
174− , (,) "switchHoldPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175− let e' = flip push e $ \_ -> do
176− Just <$> headE e
177− e'' <- switchHoldPromptly never e'
178− return (b, e'')
179− , (,) "switchHoldPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
180− let e' = flip pushAlways e $ \_ -> do
181− switchHoldPromptly e never
182− e'' <- switchHoldPromptly never e'
183− return (b, e'')
184− , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
185− let e' = flip pushAlways e $ \_ -> return e
186− e'' = coincidence e'
187− return (b, e'')
188− , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
189− let e' = flip pushAlways e $ \_ -> return $ leftmost [e, e]
190− e'' = coincidence e'
191− return (b, e'')
192− , (,) "coincidence-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
193− let e' = flip pushAlways e $ \_ -> return $ coincidence $ fmap (const e) e
194− e'' = coincidence e'
195− return (b, e'')
196− , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
197− let e' = flip pushAlways e $ \_ -> headE e
198− e'' = coincidence e'
199− return (b, e'')
200− , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
201− let eChild = flip pushAlways e $ const $ do
202− let eNewValues = leftmost [e, e]
203− return $ coincidence $ fmap (const eNewValues) eNewValues
204− e' = coincidence eChild
205− return (b, e')
206− , (,) "coincidence-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
207− let eChild = flip pushAlways e $ const $ do
208− let e' = coincidence $ fmap (const e) e
209− return $ leftmost [e', e']
210− e'' = coincidence eChild
211− return (b, e'')
212− , (,) "coincidence-7" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
213− let e' = leftmost [e, e]
214− eCoincidences = coincidence $ fmap (const e') e
215− return (b, eCoincidences)
216− , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
217− eo <- headE e
218− bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219− let b' = pull $ sample =<< sample bb
220− return (b', e)
221− , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222− bb <- hold "b" e
223− bd <- hold never . fmap (const e) =<< headE e
224− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
225− let eInner = switch bd
226− e' = leftmost [eOuter, eInner]
227− return (b, e')
228− , (,) "holdUniqDyn-laziness" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList (zip [1 :: Int ..] [1 :: Int, 2, 2, 3, 3, 3, 4, 4, 4, 4])) $ \(_, e :: Event t Int) -> do
229− rec result <- holdUniqDyn d
230− d <- holdDyn (0 :: Int) e
231− return (current result, updated result)
232−
233−
234−
235− {-
236− , (,) "mergeIncrementalWithMove" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, PatchDMapWithMove.moveDMapKey LeftTag RightTag), (2, mempty)]) $ \(b, e :: Event t (PatchDMapWithMove (EitherTag () ()) (Const2 () ()))) -> do
237− x <- holdIncremental (DMap.singleton LeftTag $ void e) $ PatchDMapWithMove.mapPatchDMapWithMove (\(Const2 _) -> void e) <$> e
238− let e' = mergeIncrementalWithMove x :: Event t (DMap (EitherTag () ()) Identity)
239− return (b, e')
240− -}
241− ]
242−
243−splitRecombineEvent :: Reflex t => Event t a -> Event t String
244−splitRecombineEvent e =
245− let ea = "a" <$ e
246− eb = "b" <$ e
247− in leftmost [ea, eb]
248−
249−
250−main :: IO ()
251−main = do
252− results <- forM testCases $ \(name, TestCase inputs builder) -> do
253− putStrLn $ "Test: " <> name
254− testAgreement builder inputs
255− exitWith $ if and results
256− then ExitSuccess
257− else ExitFailure 1
test/Reflex/Test/Micro.hsdeleted−346
@@ -1,346 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
9−{-# LANGUAGE RecursiveDo #-}
10−
11−module Reflex.Test.Micro (testCases) where
12−
13−import Reflex
14−import Reflex.TestPlan
15−
16−import Control.Applicative
17−import Control.Monad
18−import Control.Monad.Fix
19−import Data.Char
20−import Data.Foldable
21−import Data.Functor.Misc
22−import qualified Data.Map as Map
23−import Data.Monoid
24−
25−import Prelude
26−
27−pushDyn :: (Reflex t, MonadHold t m) => (a -> PushM t b) -> Dynamic t a -> m (Dynamic t b)
28−pushDyn f d = buildDynamic (sample (current d) >>= f) (pushAlways f (updated d))
29−
30−foldedDyn :: (Reflex t, MonadHold t m) => (a -> a -> a) -> Dynamic t a -> m (Dynamic t a)
31−foldedDyn f d = fmap join $ flip buildDynamic never $ do
32− a <- sample (current d)
33− foldDyn f a (updated d)
34−
35−scannedDyn :: (Reflex t, MonadHold t m) => Dynamic t a -> m (Dynamic t [a])
36−scannedDyn = fmap (fmap reverse) . foldedDyn (<>) . fmap pure
37−
38−scanInnerDyns :: (Reflex t, MonadHold t m) => Dynamic t (Dynamic t a) -> m (Dynamic t [a])
39−scanInnerDyns d = do
40− scans <- scannedDyn d
41− return (join (fmap distributeListOverDynPure scans))
42−
43−
44−
45−{-# ANN testCases "HLint: ignore Functor law" #-}
46−testCases :: [(String, TestCase)]
47−testCases =
48− [ testB "hold" $ hold "0" =<< events1
49−
50− , testB "count" $ do
51− b <- current <$> (count =<< events2)
52− return $ (+ (0::Int)) <$> b
53−
54− , testB "pull-1" $ do
55− b <- hold "0" =<< events1
56− return (pull $ sample $ pull $ sample b)
57−
58− , testB "pull-2" $ do
59− b1 <- behavior1
60− return (pull $ liftA2 (<>) (sample b1) (sample b1))
61−
62− , testB "pull-3" $ do
63− b1 <- behavior1
64− b2 <- behavior2
65− return (pull $ liftA2 (<>) (sample b1) (sample b2))
66−
67− , testB "pull-4" $ do
68− es <- planList ["a", "b", "c"]
69− e <- plan [(0, ())]
70− b <- hold (constant "") $
71− pushAlways (const $ hold "z" es) e
72− return (join b)
73−
74− , testE "id" $ do
75− events2
76−
77− , testE "fmap-id" $ do
78− e <- events2
79− return $ fmap id e
80−
81− , testE "tag-1" $ do
82− b1 <- behavior1
83− e <- events2
84− return (tag b1 e)
85−
86− , testE "tag-2" $ do
87− b1 <- behavior1
88− e <- events2
89− return (tag (map toUpper <$> b1) e)
90−
91− , testE "attach-1" $ do
92− b1 <- behavior1
93− e <- events2
94− return (attachWith (++) (map toUpper <$> b1) e)
95−
96− , testE "leftmost" $ liftA2 leftmost2 events1 events2
97−
98− , testE "appendEvents-1" $ liftA2 mappend events1 events2
99−
100− , testE "appendEvents-2" $ liftA2 mappend events3 events2
101−
102− , testE "merge-1" $ do
103− e <- events1
104− return $ leftmost ["x" <$ e, "y" <$ e]
105−
106− , testE "merge-2" $ do
107− e <- events1
108− let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
109− let ee = flip pushAlways e $ const $ return m
110− return $ coincidence ee
111−
112− , testE "headE-1" $ do
113− e <- events1
114− headE $ leftmost [e, e]
115−
116− , testE "headE-2" $ do
117− e <- events1
118− b <- hold never (e <$ e)
119− headE $ switch b
120−
121− , testE "switch-1" $ do
122− e <- events1
123− b <- hold never (e <$ e)
124− return $ switch b
125−
126− , testE "switch-2" $ do
127− e <- events1
128− return $ coincidence $ flip pushAlways e $ const $ do
129− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e)
130−
131− , testE "switch-3" $ do
132− e <- events1
133− return $ coincidence $ flip pushAlways e $ const $ do
134− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never
135−
136− , testE "switch-4" $ do
137− e <- events1
138− switch <$> hold (deep e) (e <$ e)
139−
140− , testE "switch-5" $ do
141− e <- events1
142− return $ coincidence $ flip pushAlways e $ const $
143− return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e]
144−
145− , testE "switch-6" $ do
146− e <- events1
147− return $ coincidence $ flip pushAlways e $ const $ do
148− switch <$> hold ("x" <$ e) (e <$ e)
149−
150− , testE "switchHoldPromptly-1" $ do
151− e <- events1
152− let e' = e <$ e
153− switchHoldPromptly never $ e <$ e'
154−
155− , testE "switchHoldPromptly-2" $ do
156− e <- events1
157− switchHoldPromptly never $ deep (e <$ e)
158−
159− , testE "switchHoldPromptly-3" $ do
160− e <- events1
161− switchHoldPromptly never $ (e <$ deep e)
162−
163− , testE "switchHoldPromptly-4" $ do
164− e <- events1
165− switchHoldPromptly never $ (deep e <$ e)
166−
167− , testE "switch-5" $ do
168− e <- events1
169− switch <$> hold never (deep e <$ e)
170−
171− , testE "switchHoldPromptly-5" $ do
172− e <- events1
173− switchHoldPromptly never $ flip push e $
174− const (Just <$> headE e)
175−
176− , testE "switchHoldPromptly-6" $ do
177− e <- events1
178− switchHoldPromptly never $ flip pushAlways e $
179− const (switchHoldPromptly e never)
180−
181− , testE "coincidence-1" $ do
182− e <- events1
183− return $ coincidence $ flip pushAlways e $
184− const $ return e
185−
186− , testE "coincidence-2" $ do
187− e <- events1
188− return $ coincidence $ flip pushAlways e $
189− const $ return (deep e)
190−
191− , testE "coincidence-3" $ do
192− e <- events1
193− return $ coincidence $ flip pushAlways e $
194− const $ return (coincidence (e <$ e))
195−
196− , testE "coincidence-4" $ do
197− e <- events1
198− return $ coincidence $ flip pushAlways e $
199− const (headE e)
200−
201− , testE "coincidence-5" $ do
202− e <- events1
203− return $ coincidence $ flip pushAlways e $ const $ do
204− let e' = deep e
205− return (coincidence (e' <$ e'))
206−
207− , testE "coincidence-6" $ do
208− e <- events1
209− return $ coincidence $ flip pushAlways e $ const $ do
210− let e' = coincidence (e <$ e)
211− return $ deep e'
212−
213− , testE "coincidence-7" $ do
214− e <- events1
215− return $ coincidence (deep e <$ e)
216−
217− , testB "holdWhileFiring" $ do
218− e <- events1
219− eo <- headE e
220− bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
221− return $ pull $ sample =<< sample bb
222−
223−
224− , testB "foldDynWhileFiring" $ do
225− e <- events1
226− d <- foldDyn (:) [] $
227− pushAlways (\a -> foldDyn (:) [a] e) e
228−
229− return $ current (join (fmap distributeListOverDynPure d))
230−
231− , testE "joinDyn" $ do
232− e <- events1
233− bb <- hold "b" e
234− bd <- hold never . fmap (const e) =<< headE e
235−
236− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
237− let eInner = switch bd
238− return $ leftmost [eOuter, eInner]
239−
240− , testB "foldDyn" $ do
241− d <- foldDyn (++) "0" =<< events1
242− return (current d)
243−
244− , testB "mapDyn" $ do
245− d <- foldDyn (++) "0" =<< events1
246− return $ current $ fmap (map toUpper) d
247−
248− , testB "combineDyn" $ do
249− d1 <- foldDyn (++) "0" =<< events1
250− d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
251−
252− return $ current $ zipDynWith (<>) d1 d2
253−
254− , testB "buildDynamicStrictness" $ do
255− rec
256− d'' <- pushDyn return d'
257− d' <- pushDyn return d
258− d <- holdDyn "0" =<< events1
259−
260− _ <- sample (current d'')
261− return (current d'')
262−
263− , testB "factorDyn" $ do
264− d <- holdDyn (Left "a") =<< eithers
265−
266− eithers' <- eitherDyn d
267− let unFactor = either id id
268− return $ current (join (fmap unFactor eithers'))
269−
270− , testB "pushDynDeep" $ do
271− _ <- events1
272− _ <- events2
273−
274− d1 <- holdDyn "d1" =<< events1
275− d2 <- holdDyn "d2" =<< events2
276−
277− d <- flip pushDyn d1 $ \a ->
278− flip pushDyn d2 $ \b ->
279− flip pushDyn d1 $ \c ->
280− return (a <> b <> c)
281−
282− d' <- pushDyn scanInnerDyns d >>= scanInnerDyns
283− return $ current d'
284−
285− , testE "fan-1" $ do
286− e <- fmap toMap <$> events1
287− let es = select (fanMap e) . Const2 <$> values
288−
289− return (mergeList es)
290−
291− , testE "fan-2" $ do
292− e <- fmap toMap <$> events3
293− let es = select (fanMap e) . Const2 <$> values
294−
295− return (mergeList es)
296−
297− , testE "fan-3" $ do
298− f <- fanMap . fmap toMap <$> events3
299− return $ select f (Const2 'c')
300−
301− , testE "fan-4" $ do
302− e <- fmap toMap <$> events1
303− return $ toUpper <$> select (fanMap e) (Const2 'a')
304−
305− , testE "fan-5" $ do
306− e <- fmap toMap <$> events2
307− return $ toUpper <$> select (fanMap e) (Const2 'c')
308−
309− , testE "fan-6" $ do
310− f <- fanMap . fmap toMap <$> events1
311− return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
312−
313− , testE "difference" $ do
314− e1 <- events1
315− e2 <- events2
316− return $ e1 `difference ` e2
317−
318− , testE "lazy-hold" $ do
319− let lazyHold :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => m (Event t ())
320− lazyHold = do
321− rec !b <- hold never e
322− let e = never <$ switch b
323− return $ void e
324− lazyHold
325−
326−
327− ] where
328−
329− events1, events2, events3 :: TestPlan t m => m (Event t String)
330− events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
331− events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
332− events3 = liftA2 mappend events1 events2
333−
334− eithers :: TestPlan t m => m (Event t (Either String String))
335− eithers = plan [(1, Left "e"), (3, Left "d"), (4, Right "c"), (6, Right "b"), (7, Left "a")]
336−
337−
338− values = "abcde"
339− toMap str = Map.fromList $ map (\c -> (c, c)) str
340−
341− behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
342− behavior1 = hold "1" =<< events1
343− behavior2 = hold "2" =<< events2
344−
345− deep e = leftmost [e, e]
346− leftmost2 e1 e2 = leftmost [e1, e2]
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−67
@@ -1,67 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE GADTs #-}
3−{-# LANGUAGE RankNTypes #-}
4−{-# LANGUAGE ScopedTypeVariables #-}
5−module Main where
6−
7−import Control.Lens
8−import Control.Monad
9−import qualified Data.Dependent.Map as DMap
10−import Data.Dependent.Sum
11−import Data.These
12−
13−import Reflex
14−import Reflex.Requester.Base
15−import Reflex.Requester.Class
16−import Test.Run
17−
18−data RequestInt a where
19− RequestInt :: Int -> RequestInt Int
20−
21−main :: IO ()
22−main = do
23− os1@[[Just [10,9,8,7,6,5,4,3,2,1]]] <- runApp' (unwrapApp testOrdering) $
24− [ Just ()
25− ]
26− print os1
27− os2@[[Just [1,3,5,7,9]],[Nothing,Nothing],[Just [2,4,6,8,10]],[Just [2,4,6,8,10],Nothing]]
28− <- runApp' (unwrapApp testSimultaneous) $ map Just $
29− [ This ()
30− , That ()
31− , This ()
32− , These () ()
33− ]
34− print os2
35− return ()
36−
37−unwrapRequest :: DSum tag RequestInt -> Int
38−unwrapRequest (_ :=> RequestInt i) = i
39−
40−unwrapApp :: ( Reflex t, Monad m )
41− => (a -> RequesterT t RequestInt Identity m ())
42− -> a
43− -> m (Event t [Int])
44−unwrapApp x appIn = do
45− ((), e) <- runRequesterT (x appIn) never
46− return $ fmap (map unwrapRequest . DMap.toList) e
47−
48−testOrdering :: ( Response m ~ Identity
49− , Request m ~ RequestInt
50− , Requester t m
51− , Adjustable t m)
52− => Event t ()
53− -> m ()
54−testOrdering pulse = forM_ [10,9..1] $ \i ->
55− requestingIdentity (RequestInt i <$ pulse)
56−
57−testSimultaneous :: ( Response m ~ Identity
58− , Request m ~ RequestInt
59− , Requester t m
60− , Adjustable t m)
61− => Event t (These () ())
62− -> m ()
63−testSimultaneous pulse = do
64− let tellE = fmapMaybe (^? here) pulse
65− switchE = fmapMaybe (^? there) pulse
66− forM_ [1,3..9] $ \i -> runWithReplace (requestingIdentity (RequestInt i <$ tellE)) $ ffor switchE $ \_ ->
67− requestingIdentity (RequestInt (i+1) <$ tellE)
test/Test/Run.hsdeleted−74
@@ -1,74 +0,0 @@
1−{-# LANGUAGE GADTs #-}
2−{-# LANGUAGE LambdaCase #-}
3−module Test.Run where
4−
5−import Control.Monad
6−import Control.Monad.Ref
7−import Data.Dependent.Sum
8−import Data.Functor.Identity
9−import Data.These
10−
11−import Reflex
12−import Reflex.Host.Class
13−
14−data AppIn t b e = AppIn
15− { _appIn_behavior :: Behavior t b
16− , _appIn_event :: Event t e
17− }
18−
19−data AppOut t b e = AppOut
20− { _appOut_behavior :: Behavior t b
21− , _appOut_event :: Event t e
22− }
23−
24−runApp :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
25− => (AppIn t bIn eIn -> PerformEventT t m (AppOut t bOut eOut))
26− -> bIn
27− -> [Maybe (These bIn eIn)]
28− -> IO [[(bOut, Maybe eOut)]]
29−runApp app b0 input = runSpiderHost $ do
30− (appInHoldE, pulseHoldTriggerRef) <- newEventWithTriggerRef
31− (appInE, pulseEventTriggerRef) <- newEventWithTriggerRef
32− appInB <- hold b0 appInHoldE
33− (out, FireCommand fire) <- hostPerformEventT $ app $ AppIn
34− { _appIn_event = appInE
35− , _appIn_behavior = appInB
36− }
37− hnd <- subscribeEvent (_appOut_event out)
38− mpulseB <- readRef pulseHoldTriggerRef
39− mpulseE <- readRef pulseEventTriggerRef
40− let readPhase = do
41− b <- sample (_appOut_behavior out)
42− frames <- sequence =<< readEvent hnd
43− return (b, frames)
44− forM input $ \case
45− Nothing ->
46− fire [] $ readPhase
47− Just i -> case i of
48− This b' -> case mpulseB of
49− Nothing -> error "tried to fire in-behavior but ref was empty"
50− Just pulseB -> fire [ pulseB :=> Identity b' ] $ readPhase
51− That e' -> case mpulseE of
52− Nothing -> error "tried to fire in-event but ref was empty"
53− Just pulseE -> fire [ pulseE :=> Identity e' ] $ readPhase
54− These b' e' -> case mpulseB of
55− Nothing -> error "tried to fire in-behavior but ref was empty"
56− Just pulseB -> case mpulseE of
57− Nothing -> error "tried to fire in-event but ref was empty"
58− Just pulseE -> fire [ pulseB :=> Identity b', pulseE :=> Identity e' ] $ readPhase
59−
60−runApp' :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
61− => (Event t eIn -> PerformEventT t m (Event t eOut))
62− -> [Maybe eIn]
63− -> IO [[Maybe eOut]]
64−runApp' app input = do
65− let app' = fmap (AppOut (pure ())) . app
66− map (map snd) <$> runApp (app' . _appIn_event) () (map (fmap That) input)
67−
68−runAppB :: (t ~ SpiderTimeline Global, m ~ SpiderHost Global)
69− => (Event t eIn -> PerformEventT t m (Behavior t bOut))
70− -> [Maybe eIn]
71− -> IO [[bOut]]
72−runAppB app input = do
73− let app' = fmap (flip AppOut never) . app
74− map (map fst) <$> runApp (app' . _appIn_event) () (map (fmap That) input)
test/hlint.hs+3−8
@@ -1,7 +1,7 @@
11module Main where
22
33import Control.Monad
4−import Language.Haskell.HLint3 (hlint)
4+import Language.Haskell.HLint (hlint)
55import System.Directory
66import System.Exit (exitFailure, exitSuccess)
77import System.FilePath
@@ -22,6 +22,8 @@ main = do
2222 , "--ignore=Use unless"
2323 , "--ignore=Reduce duplication"
2424 , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
26+ , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
2527 ]
2628 recurseInto = and <$> sequence
2729 [ fileType ==? Directory
@@ -29,16 +31,9 @@ main = do
2931 ]
3032 matchFile = and <$> sequence
3133 [ extension ==? ".hs"
32− , let notElem' = liftOp notElem
33− in filePath `notElem'` filePathExceptions pwd
3434 ]
3535 files <- find recurseInto matchFile (pwd </> "src") --TODO: Someday fix all hints in tests, etc.
3636 ideas <- fmap concat $ forM files $ \f -> do
3737 putStr $ "linting file " ++ drop (length pwd + 1) f ++ "... "
3838 runHlint f
3939 if null ideas then exitSuccess else exitFailure
40−
41−filePathExceptions :: FilePath -> [FilePath]
42−filePathExceptions pwd = map (pwd </>) $
43− [ "src/Data/AppendMap.hs" -- parse error when hlint runs
44− ]
test/rootCleanup.hsdeleted−25
@@ -1,25 +0,0 @@
1−import Control.Concurrent
2−import Control.Monad
3−import Data.IORef
4−import Reflex
5−import Reflex.Host.Class
6−import System.Exit
7−import System.Mem
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−47
@@ -1,47 +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
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