on develop help sign in
.github/workflows/haskell.ymlnew file+47
since splitsnapshots
unifiedsplit
@@ -0,0 +1,47 @@
1+name: github-action
2+
3+on: [push, pull_request]
4+
5+jobs:
6+ build:
7+ strategy:
8+ matrix:
9+ ghc: ['8.0.2', '8.2.2', '8.4.4', '8.6.5', '8.8.4', '8.10.2']
10+ os: ['ubuntu-latest', 'macos-latest']
11+ exclude:
12+ # There are some linker warnings in 802 on darwin that
13+ # cause compilation to fail
14+ # See https://github.com/NixOS/nixpkgs/issues/25139
15+ - ghc: '8.0.2'
16+ os: 'macos-latest'
17+ runs-on: ${{ matrix.os }}
18+
19+ name: GHC ${{ matrix.ghc }} on ${{ matrix.os }}
20+ steps:
21+ - uses: actions/checkout@v2
22+ - uses: actions/setup-haskell@v1
23+ with:
24+ ghc-version: ${{ matrix.ghc }}
25+ - name: Cache
26+ uses: actions/cache@v1
27+ env:
28+ cache-name: cache-cabal
29+ with:
30+ path: ~/.cabal
31+ key: ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
32+ restore-keys: |
33+ ${{ runner.os }}-${{ matrix.ghc }}-build-${{ env.cache-name }}-
34+ ${{ runner.os }}-${{ matrix.ghc }}-build-
35+ ${{ runner.os }}-${{ matrix.ghc }}-
36+ ${{ runner.os }}
37+
38+ - name: Install dependencies
39+ run: |
40+ cabal update
41+ cabal build --only-dependencies --enable-tests --enable-benchmarks
42+ - name: Build
43+ run: cabal build --enable-tests --enable-benchmarks all
44+ - name: Run tests
45+ run: cabal test --enable-tests all
46+ - name: Build Docs
47+ run: cabal haddock
.stylish-haskell.yaml+2−3
@@ -149,6 +149,5 @@ columns: 110
149# needs to be aware of these, so it can parse the file correctly.
149# needs to be aware of these, so it can parse the file correctly.
150#
150#
151# No language extensions are enabled by default.
151# No language extensions are enabled by default.
152−# language_extensions:
152+language_extensions:
153− # - TemplateHaskell
153+ - TemplateHaskell
154− # - QuasiQuotes
.travis.ymldeleted−26
@@ -1,26 +0,0 @@
1−sudo: false
2−before_install:
3− - travis_retry cabal-$CABALVER update
4− - export PATH=/opt/ghc/$GHCVER/bin:/opt/happy/1.19.5/bin:/opt/alex/3.1.4/bin:$PATH
5−install:
6− - travis_retry cabal-$CABALVER install --only-dependencies -j --enable-tests
7−script:
8− - cabal-$CABALVER configure -v2 --enable-tests
9− - travis_wait cabal-$CABALVER build
10− - cabal-$CABALVER test
11− - cabal-$CABALVER sdist # tests that a source-distribution can be generated
12−# Check that the resulting source distribution can be built & installed.
13−# If there are no other `.tar.gz` files in `dist`, this can be even simpler:
14−# `cabal install --force-reinstalls dist/*-*.tar.gz`
15− - SRC_TGZ=$(cabal-$CABALVER info . | awk '{print $2;exit}').tar.gz &&
16− (cd dist && cabal-$CABALVER install --force-reinstalls "$SRC_TGZ")
17−
18−matrix:
19− include:
20− - env: CABALVER=1.18 GHCVER=7.8.4
21− addons: {apt: {packages: [cabal-install-1.18, ghc-7.8.4, alex-3.1.4, happy-1.19.5], sources: [hvr-ghc]}}
22− - env: CABALVER=1.22 GHCVER=7.10.1
23− addons: {apt: {packages: [cabal-install-1.22, ghc-7.10.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
24− - env: CABALVER=1.24 GHCVER=8.0.1
25− addons: {apt: {packages: [cabal-install-1.24, ghc-8.0.1, alex-3.1.4, happy-1.19.5],sources: [hvr-ghc]}}
26− fast_finish: true
CONTRIBUTING.mdnew file+66
@@ -0,0 +1,66 @@
1+# Contribution Guide
2+
3+Contributions and issue reports are encouraged and appreciated!
4+
5+- [Opening Issues](#opening-issues)
6+- [Submitting Changes](#submitting-changes)
7+ - [Guidelines for Commit Messages](#guidelines-for-commit-messages)
8+ - [Guidelines for Pull Requests](#guidelines-for-pull-requests)
9+ - [Code Quality](#code-quality)
10+ - [Documentation](#documentation)
11+
12+## Opening Issues
13+
14+Before opening an issue, please check whether your issue has already been reported. Assuming it has not:
15+
16+* Describe the issue you're encountering or the suggestion you're making
17+* Include any relevant steps to reproduce or code samples you can. It's always easier for us to debug if we have something that demonstrates the error.
18+* Let us know what version of patch you were using. If you're using a github checkout, provide the git hash.
19+* Describe how you're building patch (i.e., via reflex-platform, cabal install, stack, obelisk, etc.). If you're using reflex-platform or obelisk, provide the git hash of your checkout.
20+
21+## Submitting Changes
22+
23+### Guidelines for Commit Messages
24+
25+#### Summary Line
26+The summary line of your commit message should summarize the changes being made. Commit messages should be written in the imperative mood and should describe what happens when the commit is applied.
27+
28+One way to think about it is that your commit message should be able to complete the sentence:
29+"When applied, this commit will..."
30+
31+#### Body
32+For breaking changes, new features, refactors, or other major changes, the body of the commit message should describe the motivation behind the change in greater detail and may include references to the issue tracker. The body shouldn't repeat code/comments from the diff.
33+
34+### Guidelines for Pull Requests
35+
36+Wherever possible, pull requests should add a single feature or fix a single bug. Pull requests should not bundle several unrelated changes.
37+
38+### Code Quality
39+
40+#### Warnings
41+
42+Your pull request should add no new warnings to the project. It should also generally not disable any warnings.
43+
44+#### Build and Test
45+
46+Make sure the project builds and that the tests pass! This will generally also be checked by CI before merge, but trying it yourself first means you'll catch problems earlier and your contribution can be merged that much sooner!
47+
48+#### Dependencies
49+
50+Include version bounds whenever adding a dependency to the library stanza of the cabal file. Note that libraries added to patch.cabal also need to be added to default.nix.
51+
52+### Documentation
53+
54+#### In the code
55+We're always striving to improve documentation. Please include [haddock](https://haskell-haddock.readthedocs.io/en/latest/index.html) documentation for any added code, and update the documentation for any code you modify.
56+
57+#### In the [Changelog](ChangeLog.md)
58+Add an entry to the changelog when your PR:
59+* Adds a feature
60+* Deprecates something
61+* Includes a breaking change
62+* Makes any other change that will impact users
63+
64+#### In the [Readme](README.md)
65+The readme is the first place a lot of people look for information about the repository. Update any parts of the readme that are affected by your PR.
66+
ChangeLog.mdnew file+61
@@ -0,0 +1,61 @@
1+# Revision history for patch
2+
3+## Unreleased
4+
5+* New dep of `data-orphans` for old GHC to get instances honestly instead of
6+ via `monoidal-containers`.
7+
8+## 0.0.5.0 - 2021-12-17
9+
10+* `Additive` now lives in `Data.Semigroup.Additive`, but is still reexported
11+ from `Data.Patch` for compatability.
12+
13+* Rewrite `PatchMapWithMove` in terms of `PatchMapWithPatchingMove`.
14+ Care is taken to make this not a breaking change.
15+ In particular, `PatchMapWithMove` is a newtype of `PatchMapWithPatchingMove`, as is the `NodeInfo` and `From` of `PatchMapWithPatchingMove`'s versions of those.
16+ There are complete constructor and field patterns too, and everything is
17+ exported under the newtype as real constructors and fields would be.
18+
19+## 0.0.4.0 - 2021-04-20
20+
21+* Enable PolyKinds
22+
23+## 0.0.3.2 - 2020-11-06
24+
25+* Update version bounds
26+
27+## 0.0.3.1 - 2020-02-05
28+
29+* Replace `fromJust` with something easier to debug.
30+
31+## 0.0.3.0 - 2020-02-05
32+
33+* Create `PatchMapWithPatchingMove` variant which supports moves with a patch.
34+
35+* Create `DecidablyEmpty` subclass of `Monoid`.
36+
37+## 0.0.2.0 - 2020-01-17
38+
39+* Consistently provide:
40+
41+ - `Wrapped` instances
42+
43+ - `*WithIndex` instances
44+
45+ - `un*` newtype unwrappers
46+
47+ for `PatchMap`, `PatchIntMap`, and `PatchMapWithMove`.
48+
49+## 0.0.1.0 - 2020-01-09
50+
51+* Support older GHCs with `split-these` flag.
52+
53+* Additional instances for the `Group` class for basic types.
54+
55+## 0.0.0.1 - 2020-01-08
56+
57+* Remove unneeded dependencies
58+
59+## 0.0.0.0 - 2020-01-08
60+
61+* Extract patching functionality from Reflex.
Quickref.mddeleted−183
@@ -1,183 +0,0 @@
1−# Reflex Quick(ish) Reference
2−
3−## Typeclasses
4−
5−Many Reflex functions operate in monadic context `m a`, where the monad 'm' supports various additional typeclasses such as MonadWidget, MonadHold, or MonadSample in addition to Monad itself. The actual 'm' in use will be determined by the top-level entry point of the FRP host (such as Reflex-Dom -- see the bottom of the Reflex-Dom quick reference for details).
6−
7−The function signatures here have been simplified by removing many typeclass constraints and adding simple annotations to each function. Also the ubiquitous 't' type parameter has been removed.
8−
9−Some of these functions are *pure*: They operate on Events, Behaviors, or Dynamics uniformly without regard to the "current time". Other functions must operate in some monadic context, because they produce a result "as of now" (e.g., any function that takes an "initial" value, or returns a "current" value). Annotations are used to distinguish these cases:
10−
11−```haskell
12−[ ] -- Pure function
13−[S] -- Function runs in any monad supporting MonadSample
14−[H] -- Function runs in any monad supporting MonadHold
15−```
16−
17−Since MonadHold depends on MonadSample, any [S] function also runs in [H] context.
18−
19−## Functions producing Event
20−
21−```haskell
22−-- Trivial Event
23−[ ] never :: Event a
24−
25−-- Extract Event from Dynamic
26−[ ] updated :: Dynamic a -> Event a
27−
28−-- Transform Event to Event using function
29−[ ] fmap :: (a -> b) -> Event a -> Event b
30−[ ] fmapMaybe :: (a -> Maybe b) -> Event a -> Event b
31−[ ] ffilter :: (a -> Bool) -> Event a -> Event a
32−[ ] ffor :: Event a -> (a -> b) -> Event b
33−[ ] fforMaybe :: Event a -> (a -> Maybe b) -> Event b
34−
35−-- Event to identical Event with debug trace. (Only prints if Event is ultimately used.)
36−[ ] traceEvent :: Show a => String -> Event a -> Event a
37−[ ] traceEventWith :: (a -> String) -> Event a -> Event a
38−
39−-- Transform Event to Event by sampling Behavior or Dynamic
40−[ ] gate :: Behavior Bool -> Event a -> Event a
41−[ ] tag :: Behavior a -> Event b -> Event a
42−[ ] tagPromptlyDyn :: Dynamic a -> Event b -> Event a
43−[ ] attach :: Behavior a -> Event b -> Event (a, b)
44−[ ] attachPromptlyDyn :: Dynamic a -> Event b -> Event (a, b)
45−[ ] attachWith :: (a -> b -> c) -> Behavior a -> Event b -> Event c
46−[ ] attachPromptlyDynWith :: (a -> b -> c) -> Dynamic a -> Event b -> Event c
47−[ ] attachWithMaybe :: (a -> b -> Maybe c) -> Behavior a -> Event b -> Event c
48−[ ] attachPromptlyDynWithMaybe :: (a -> b -> Maybe c) -> Dynamic a -> Event b -> Event c
49−
50−-- Combine multiple Events
51−[ ] <> :: Semigroup a => Event a -> Event a -> Event a
52−[ ] difference :: Event a -> Event b -> Event a
53−[ ] align :: Event a -> Event b -> Event (These a b)
54−[ ] alignWith :: (These a b -> c) -> Event a -> Event b -> Event c
55−[ ] mergeWith :: (a -> a -> a) -> [Event a] -> Event a
56−[ ] leftmost :: [Event a] -> Event a
57−[ ] mergeList :: [Event a] -> Event (NonEmpty a)
58−[ ] merge :: GCompare k => DMap (WrapArg Event k) -> Event (DMap k)
59−[ ] mergeMap :: Ord k => Map k (Event a) -> Event (Map k a)
60−
61−-- Efficient one-to-many fanout
62−[ ] fanMap :: Ord k => Event (Map k a) -> EventSelector (Const2 k a)
63−[ ] fan :: GCompare k => Event (DMap k) -> EventSelector k
64−[ ] select :: EventSelector k -> k a -> Event a
65−[ ] fanEither :: Event (Either a b) -> (Event a, Event b)
66−[ ] fanThese :: Event (These a b) -> (Event a, Event b)
67−
68−-- Event to Event via function that can sample current values
69−[ ] push :: (a -> m (Maybe b)) -> Event a -> Event b
70−[ ] pushAlways :: (a -> m b ) -> Event a -> Event b
71− -- Note supplied function operates in [H] context
72−
73−-- Split Event into next occurrence and all other future occurrences, as of now.
74−[H] headE :: Event a -> m (Event a)
75−[H] tailE :: Event a -> m (Event a)
76−[H] headTailE :: Event a -> m (Event a, Event a)
77−```
78−
79−## Functions producing Behavior
80−
81−```haskell
82−-- Trivial Behavior
83−[ ] constant :: a -> Behavior a
84−
85−-- Extract Behavior from Dynamic
86−[ ] current :: Dynamic a -> Behavior a
87−
88−-- Create Behavior with given initial value, updated when the Event fires.
89−[H] hold :: a -> Event a -> m (Behavior a)
90−
91−-- Transform Behavior to Behavior using function
92−[ ] fmap :: (a -> b) -> Behavior a -> Behavior b
93−[ ] ffor :: Behavior a -> (a -> b) -> Behavior b
94−[ ] <*> :: Behavior (a -> b) -> Behavior a -> Behavior b
95−[ ] >>= :: Behavior a -> (a -> Behavior b) -> Behavior b
96−[ ] <> :: Monoid a => Behavior a -> Behavior a -> Behavior a
97−-- ... plus many more due to typeclass membership
98−
99−-- Behavior to Behavior by sampling current values
100−[S] sample :: Behavior a -> m a
101−[ ] pull :: m a -> Behavior a
102− -- Note supplied value is in [S] context
103−```
104−
105−## Functions producing Dynamic
106−
107−```haskell
108−-- Trivial Dynamic
109−[ ] constDyn :: a -> Dynamic a
110−
111−-- Create Dynamic with given initial value, updated (various ways) when the Event fires.
112−[H] holdDyn :: a -> Event a -> m (Dynamic a)
113−[H] foldDyn :: (a -> b -> b ) -> b -> Event a -> m (Dynamic b)
114−[H] foldDynMaybe :: (a -> b -> Maybe b ) -> b -> Event a -> m (Dynamic b)
115−[H] foldDynM :: (a -> b -> m' b ) -> b -> Event a -> m (Dynamic b)
116−[H] foldDynMaybeM :: (a -> b -> m' (Maybe b)) -> b -> Event a -> m (Dynamic b)
117− -- Note m' supplies [H] context
118−
119−-- Initial value is 0; counts Event firings from now.
120−[H] count :: Num b => Event a -> m (Dynamic b)
121−-- Initial Bool value is supplied; toggles at each Event firing.
122−[H] toggle :: Bool -> Event a -> m (Dynamic Bool)
123−
124−-- Transform Dynamic to Dynamic using function
125−[ ] ffor :: Dynamic a -> (a -> b) -> Dynamic b
126−[ ] fmap :: (a -> b) -> Dynamic a -> Dynamic b
127−[ ] splitDynPure :: Dynamic (a, b) -> (Dynamic a, Dynamic b)
128−
129−-- Combine multiple Dynamics
130−[ ] mconcat :: Monoid a => [Dynamic a] -> Dynamic a
131−[ ] distributeDMapOverDynPure :: GCompare k => DMap (WrapArg Dynamic k) -> Dynamic (DMap k)
132−[ ] <*> :: Dynamic (a -> b) -> Dynamic a -> Dynamic b
133−[ ] >>= :: Dynamic a -> (a -> Dynamic b) -> Dynamic b
134−[ ] zipDynWith :: (a -> b -> c) -> Dynamic a -> Dynamic b -> Dynamic c
135−
136−-- Efficient one-to-many fanout
137−[ ] demux :: Ord k => Dynamic k -> Demux k
138−[ ] demuxed :: Eq k => Demux k -> k -> Dynamic Bool
139−
140−-- Dynamic to Dynamic, removing updates w/o value change
141−[ ] uniqDyn :: Eq a => Dynamic a -> Dynamic a
142−
143−-- Dynamic to identical Dynamic with debug trace. (Only prints if Dynamic is ultimately used.)
144−[ ] traceDyn :: Show a => String -> Dynamic a -> Dynamic a
145−[ ] traceDynWith :: (a -> String) -> Dynamic a -> Dynamic a
146−```
147−
148−## Flattening functions
149−
150−These functions flatten nested types such as Event-of-Event, Behavior-of-Event, Event-of-Behavior etc, removing the outer wrapper.
151−
152−For Events, the returned Event fires whenever the latest Event supplied by the wrapper fires. There are differences in how the functions handle *coincidences* -- situations where the old or new Event fires at the same instant that we switch from old to new. In some cases, the output Event tracks the old Event at the instant of switchover, while in other cases it tracks the new Event.
153−
154−```haskell
155−-- Flatten Behavior-of-Event to Event. Old Event is used during switchover.
156−[ ] switch :: Behavior (Event a) -> Event a
157−
158−-- Flatten Dyanmic-of-Event to Event. New Event is used immediately.
159−[ ] switchPromptlyDyn :: Dynamic (Event a) -> Event a
160−
161−-- Flatten Event-of-Event to Event that fires when both wrapper AND new Event fire.
162−[ ] coincidence :: Event (Event a) -> Event a
163−
164−-- Flatten Behavior-of-Behavior to Behavior. Behavior is an instance of Monad,
165−-- so the 'join' function from 'Control.Monad' suffices. The output of 'join' is
166−-- a Behavior whose value is equal to the current value of the current value of
167−-- the input.
168−[ ] join :: Behavior (Behavior a) -> Behavior a
169−
170−
171−-- Flatten Dynamic-of-Dynamic to Dynamic. New Dynamic is used immediately.
172−-- Output updated whenever inner OR outer Dynamic updates.
173−[ ] join :: Dynamic (Dynamic a) -> Dynamic a
174−[ ] joinDynThroughMap :: Ord k => Dynamic (Map k (Dynamic a)) -> Dynamic (Map k a)
175−
176−-- Analogous to 'hold': Create a Behavior that is initially identical to the
177−-- supplied Behavior. Updated to track a new Behavior whenever the Event fires.
178−[H] switcher :: Behavior a -> Event (Behavior a) -> m (Behavior a)
179−
180−-- Similar to above, for Events. Created Event initially tracks the first argument.
181−-- At switchover, the output Event immediately tracks the new Event.
182−[H] switchPromptly :: Event a -> Event (Event a) -> m (Event a)
183−```
README.md+44−11
@@ -1,19 +1,52 @@
1−## Reflex
1+# patch
2−### Practical Functional Reactive Programming
3
2
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)
5
4
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.
7
6
8−Comprehensive documentation is still a work in progress, but a tutorial for Reflex and Reflex-DOM is available at https://github.com/reflex-frp/reflex-platform and an introductory talk given at the New York Haskell Meetup is available here: [Part 1](https://www.youtube.com/watch?v=mYvkcskJbc4) / [Part 2](https://www.youtube.com/watch?v=3qfc9XFVo2c).
7+A `Patch` type represents a kind of change made to a data structure.
9
8
10−A summary of Reflex functions is available in the [quick reference](Quickref.md).
9+```haskell
10+class Patch p where
11+ type PatchTarget p :: *
12+ -- | Apply the patch p a to the value a. If no change is needed, return
13+ -- 'Nothing'.
14+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
15+```
11
16
12−### Additional resources
17+## Patching Maps
13−[Get started with Reflex](https://github.com/reflex-frp/reflex-platform)
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).
14
19
15−[/r/reflexfrp](https://www.reddit.com/r/reflexfrp)
20+`PatchMap` is defined as:
16
21
17−[hackage](https://hackage.haskell.org/package/reflex)
22+```haskell
23+newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
24+```
18
25
19−[irc.freenode.net #reflex-frp](http://webchat.freenode.net?channels=%23reflex-frp&uio=d4)
26+The `Maybe` around the value is used to represent insertion/updates or deletion of the element at a given key.
27+
28+Its `Patch` instance begins with:
29+
30+```haskell
31+instance Ord k => Patch (PatchMap k v) where
32+ type PatchTarget (PatchMap k v) = Map k v
33+ ...
34+```
35+
36+When a `PatchMap` is applied to its `PatchTarget`, the following changes can occur:
37+
38+- If the key is present in the `Patch` and the `PatchTarget`...
39+
40+ - And the `Patch` value at that key is `Nothing`: delete that key from the `PatchTarget`.
41+
42+ - And the `Patch` value at that key is `Just x`: update the value at that key in the `PatchTarget` to `x`.
43+
44+- If the key is present in the `Patch` and not present in the `PatchTarget`...
45+
46+ - And the `Patch` value at that key is `Nothing`: do nothing because we're trying to delete a key that doesn't exist in the target in the first place.
47+
48+ - And the `Patch` value at that key is `Just x`: insert the key and the value `x` into the `PatchTarget`
49+
50+- If the key is *not* present in the `Patch` but present in the `PatchTarget`: do nothing.
51+
52+There are, of course, more complicated ways of patching maps involving, for example, moving values from one key to another. You can find the code for that in `Data.Patch.PatchMapWithMove`. Note that the `PatchTarget` type associated with the `PatchMapWithMove` patch instance is still `Map k v`!
bench-cbits/checkCapability.c+11−12
@@ -42,7 +42,7 @@ typedef struct Task_ {
42 // or just continue immediately. It's a workaround for the fact
42 // or just continue immediately. It's a workaround for the fact
43 // that signalling a condition variable doesn't do anything if the
43 // that signalling a condition variable doesn't do anything if the
44 // thread is already running, but we want it to be sticky.
44 // thread is already running, but we want it to be sticky.
45− rtsBool wakeup;
45+ HsBool wakeup;
46#endif
46#endif
47
47
48 // This points to the Capability that the Task "belongs" to. If
48 // This points to the Capability that the Task "belongs" to. If
@@ -62,14 +62,14 @@ typedef struct Task_ {
62 // The current top-of-stack InCall
62 // The current top-of-stack InCall
63 struct InCall_ *incall;
63 struct InCall_ *incall;
64
64
65− nat n_spare_incalls;
65+ uint32_t n_spare_incalls;
66 struct InCall_ *spare_incalls;
66 struct InCall_ *spare_incalls;
67
67
68− rtsBool worker; // == rtsTrue if this is a worker Task
68+ HsBool worker; // == rtsTrue if this is a worker Task
69− rtsBool stopped; // this task has stopped or exited Haskell
69+ HsBool stopped; // this task has stopped or exited Haskell
70
70
71 // So that we can detect when a finalizer illegally calls back into Haskell
71 // So that we can detect when a finalizer illegally calls back into Haskell
72− rtsBool running_finalizers;
72+ HsBool running_finalizers;
73
73
74 // Links tasks on the returning_tasks queue of a Capability, and
74 // Links tasks on the returning_tasks queue of a Capability, and
75 // on spare_workers.
75 // on spare_workers.
@@ -88,7 +88,7 @@ struct Capability_ {
88 StgFunTable f;
88 StgFunTable f;
89 StgRegTable r;
89 StgRegTable r;
90
90
91− nat no; // capability number.
91+ uint32_t no; // capability number.
92
92
93 // The Task currently holding this Capability. This task has
93 // The Task currently holding this Capability. This task has
94 // exclusive access to the contents of this Capability (apart from
94 // exclusive access to the contents of this Capability (apart from
@@ -98,12 +98,12 @@ struct Capability_ {
98
98
99 // true if this Capability is running Haskell code, used for
99 // true if this Capability is running Haskell code, used for
100 // catching unsafe call-ins.
100 // catching unsafe call-ins.
101− rtsBool in_haskell;
101+ HsBool in_haskell;
102
102
103 // Has there been any activity on this Capability since the last GC?
103 // Has there been any activity on this Capability since the last GC?
104− nat idle;
104+ uint32_t idle;
105
105
106− rtsBool disabled;
106+ HsBool disabled;
107
107
108 // The run queue. The Task owning this Capability has exclusive
108 // The run queue. The Task owning this Capability has exclusive
109 // access to its run queue, so can wake up threads without
109 // access to its run queue, so can wake up threads without
@@ -159,7 +159,7 @@ struct Capability_ {
159#if defined(THREADED_RTS)
159#if defined(THREADED_RTS)
160 // Worker Tasks waiting in the wings. Singly-linked.
160 // Worker Tasks waiting in the wings. Singly-linked.
161 Task *spare_workers;
161 Task *spare_workers;
162− nat n_spare_workers; // count of above
162+ uint32_t n_spare_workers; // count of above
163
163
164 // This lock protects:
164 // This lock protects:
165 // running_task
165 // running_task
@@ -192,10 +192,9 @@ struct Capability_ {
192
192
193 // Per-capability STM-related data
193 // Per-capability STM-related data
194 StgTVarWatchQueue *free_tvar_watch_queues;
194 StgTVarWatchQueue *free_tvar_watch_queues;
195− StgInvariantCheckQueue *free_invariant_check_queues;
196 StgTRecChunk *free_trec_chunks;
195 StgTRecChunk *free_trec_chunks;
197 StgTRecHeader *free_trec_headers;
196 StgTRecHeader *free_trec_headers;
198− nat transaction_tokens;
197+ uint32_t transaction_tokens;
199} // typedef Capability is defined in RtsAPI.h
198} // typedef Capability is defined in RtsAPI.h
200 // We never want a Capability to overlap a cache line with anything
199 // We never want a Capability to overlap a cache line with anything
201 // else, so round it up to a cache line size:
200 // else, so round it up to a cache line size:
bench/Main.hsdeleted−109
@@ -1,109 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ScopedTypeVariables #-}
4−{-# LANGUAGE TupleSections #-}
5−{-# LANGUAGE TypeFamilies #-}
6−
7−-- The instance for NFData (TVar a) is an orphan, but necessary here
8−{-# OPTIONS_GHC -fno-warn-orphans #-}
9−
10−module Main where
11−
12−import Control.Concurrent.STM
13−import Control.DeepSeq
14−import Control.Exception (evaluate)
15−import Control.Monad.Identity
16−import Control.Monad.IO.Class
17−import Criterion.Main
18−import Data.Dependent.Map (DMap)
19−import qualified Data.Dependent.Map as DMap
20−import Data.Dependent.Sum
21−import Data.Functor.Misc
22−import Data.IORef
23−import Reflex
24−import Reflex.Host.Class
25−
26−main :: IO ()
27−main = defaultMain
28− [ bgroup "micro" micros ]
29−
30−#if !(MIN_VERSION_deepseq(1,4,2))
31−instance NFData (IORef a) where
32− rnf x = seq x ()
33−#endif
34−
35−instance NFData (TVar a) where
36− rnf x = seq x ()
37−
38−newtype WHNF a = WHNF a
39−instance NFData (WHNF a) where
40− rnf (WHNF a) = seq a ()
41−
42−withSetup :: NFData b => String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
43−withSetup name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
44− bench name . nfIO $ runSpiderHost (action a)
45−
46−withSetupWHNF :: String -> SpiderHost Global a -> (a -> SpiderHost Global b) -> Benchmark
47−withSetupWHNF name setup action = env (WHNF <$> runSpiderHost setup) $ \ ~(WHNF a) ->
48− bench name . whnfIO $ runSpiderHost (action a)
49−
50−micros :: [Benchmark]
51−micros =
52− [ bench "newIORef" $ whnfIO $ void $ newIORef ()
53− , env (newIORef (42 :: Int)) (bench "readIORef" . whnfIO . readIORef)
54− , bench "newTVar" $ whnfIO $ void $ newTVarIO ()
55− , env (newTVarIO (42 :: Int)) (bench "readTVar" . whnfIO . readTVarIO)
56− , bench "newEventWithTrigger" $ whnfIO . void $ runSpiderHost $ newEventWithTrigger $
57− \trigger -> return () <$ evaluate trigger
58− , bench "newEventWithTriggerRef" $ whnfIO . void $ runSpiderHost newEventWithTriggerRef
59− , withSetupWHNF "subscribeEvent" newEventWithTriggerRef $ subscribeEvent . fst
60− , withSetupWHNF "subscribeSwitch"
61− (join $ hold <$> fmap fst newEventWithTriggerRef <*> fmap fst newEventWithTriggerRef)
62− (subscribeEvent . switch)
63− , withSetupWHNF "subscribeMerge(1)" (setupMerge 1) $ \(ev,_) -> subscribeEvent ev
64− , withSetupWHNF "subscribeMerge(100)" (setupMerge 100) (subscribeEvent . fst)
65− , withSetupWHNF "subscribeMerge(10000)" (setupMerge 10000) (subscribeEvent . fst)
66− , bench "runHostFrame" $ whnfIO $ runSpiderHost $ runHostFrame $ return ()
67− , withSetupWHNF "fireEventsAndRead(single/single)"
68− (newEventWithTriggerRef >>= subscribePair)
69− (\(subd, trigger) -> fireAndRead trigger (42 :: Int) subd)
70− , withSetupWHNF "fireEventsOnly"
71− (newEventWithTriggerRef >>= subscribePair)
72− (\(_, trigger) -> do
73− Just key <- liftIO $ readIORef trigger
74− fireEvents [key :=> Identity (42 :: Int)])
75− , withSetupWHNF "fireEventsAndRead(head/merge1)"
76− (setupMerge 1 >>= subscribePair)
77− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
78− , withSetupWHNF "fireEventsAndRead(head/merge100)"
79− (setupMerge 100 >>= subscribePair)
80− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
81− , withSetupWHNF "fireEventsAndRead(head/merge10000)"
82− (setupMerge 10000 >>= subscribePair)
83− (\(subd, t:_) -> fireAndRead t (42 :: Int) subd)
84− , withSetupWHNF "fireEventsOnly(head/merge100)"
85− (setupMerge 100 >>= subscribePair)
86− (\(_, t:_) -> do
87− Just key <- liftIO $ readIORef t
88− fireEvents [key :=> Identity (42 :: Int)])
89− , withSetupWHNF "hold" newEventWithTriggerRef $ \(ev, _) -> hold (42 :: Int) ev
90− , withSetupWHNF "sample" (newEventWithTriggerRef >>= hold (42 :: Int) . fst) sample
91− ]
92−
93−setupMerge :: Int
94− -> SpiderHost Global ( Event (SpiderEnv Global) (DMap (Const2 Int a) Identity)
95− , [IORef (Maybe (EventTrigger Spider a))]
96− )
97−setupMerge num = do
98− (evs, triggers) <- unzip <$> replicateM num newEventWithTriggerRef
99− let !m = DMap.fromList [Const2 i :=> v | (i,v) <- zip [0..] evs]
100− pure (merge m, triggers)
101−
102−subscribePair :: (Event (SpiderEnv Global) a, b) -> SpiderHost Global (EventHandle (SpiderEnv Global) a, b)
103−subscribePair (ev, b) = (,b) <$> subscribeEvent ev
104−
105−fireAndRead :: IORef (Maybe (EventTrigger (SpiderEnv Global) a)) -> a -> EventHandle (SpiderEnv Global) b
106− -> SpiderHost Global (Maybe b)
107−fireAndRead trigger val subd = do
108− Just key <- liftIO $ readIORef trigger
109− fireEventsAndRead [key :=> Identity val] $ readEvent subd >>= sequence
bench/RunAll.hsdeleted−157
@@ -1,157 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE ForeignFunctionInterface #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE PatternSynonyms #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TemplateHaskell #-}
13−{-# LANGUAGE TupleSections #-}
14−{-# LANGUAGE TypeSynonymInstances #-}
15−{-# LANGUAGE ViewPatterns #-}
16−{-# OPTIONS_GHC -fno-warn-orphans #-}
17−
18−module Main where
19−
20−import Criterion.Main
21−import Criterion.Types
22−
23−import Reflex
24−import Reflex.Host.Class
25−
26−import Reflex.Plan.Reflex
27−import Reflex.TestPlan
28−
29−import qualified Reflex.Bench.Focused as Focused
30−import Reflex.Spider.Internal (SpiderEventHandle)
31−
32−import Control.Applicative
33−import Control.DeepSeq (NFData (..))
34−
35−import Prelude
36−import System.IO
37−import System.Mem
38−
39−import Control.Arrow
40−import Control.Concurrent
41−import Control.Concurrent.STM
42−import Control.Exception
43−import Control.Monad
44−import Control.Monad.Trans
45−import Data.Bool
46−import Data.Function
47−import Data.Int
48−import Data.IORef
49−import Data.Monoid
50−import Data.Time.Clock
51−import Debug.Trace.LocationTH
52−import GHC.Stats
53−import System.Environment
54−import System.Mem.Weak
55−import System.Process
56−import Text.Read
57−
58−import Unsafe.Coerce
59−
60−import Data.Map (Map)
61−import qualified Data.Map as Map
62−
63−type MonadReflexHost' t m = (MonadReflexHost t m, MonadIORef m, MonadIORef (HostFrame t))
64−
65−
66−setupFiring :: (MonadReflexHost t m, MonadIORef m) => Plan t (Event t a) -> m (EventHandle t a, Schedule t)
67−setupFiring p = do
68− (e, s) <- runPlan p
69− h <- subscribeEvent e
70− return (h, s)
71−
72−-- Hack to avoid the NFData constraint for EventHandle which is a synonym
73−newtype Ignore a = Ignore a
74−instance NFData (Ignore a) where
75− rnf !_ = ()
76−
77−instance NFData (SpiderEventHandle x a) where
78− rnf !_ = ()
79−
80−instance NFData (Behavior t a) where
81− rnf !_ = ()
82−
83−instance NFData (Firing t) where
84− rnf !_ = ()
85−
86−-- Measure the running time
87−benchFiring :: forall t m. (MonadReflexHost' t m, MonadSample t m) => (forall a. m a -> IO a) -> TestCase -> Int -> IO ()
88−benchFiring runHost tc n = runHost $ do
89− let runIterations :: m a -> m ()
90− runIterations test = replicateM_ (10*n) $ do
91− result <- test
92− liftIO $ evaluate result
93− case tc of
94− TestE p -> do
95− (h, s) <- setupFiring p
96− runIterations $ readSchedule_ s $ readEvent' h
97− TestB p -> do
98− (b, s) <- runPlan p
99− runIterations $ readSchedule_ (makeDense s) $ sample b
100−
101−waitForFinalizers :: IO ()
102−waitForFinalizers = do
103− performGC
104− x <- getCurrentTime
105− isFinalized <- newIORef False
106− mkWeakPtr x $ Just $ writeIORef isFinalized True
107− performGC
108− fix $ \loop -> do
109− f <- readIORef isFinalized
110− unless f $ do
111− threadDelay 1
112− loop
113−
114−benchmarks :: [(String, Int -> IO ())]
115−benchmarks = implGroup "spider" runSpiderHost cases
116− where
117− implGroup :: (MonadReflexHost' t m, MonadSample t m) => String -> (forall a. m a -> IO a) -> [(String, TestCase)] -> [(String, Int -> IO ())]
118− implGroup name runHost = group name . fmap (second (benchFiring runHost))
119− group name = fmap $ first ((name <> "/") <>)
120− sub n frames = group ("subscribing " ++ show (n, frames)) $ Focused.subscribing n frames
121− firing n = group ("firing " <> show n) $ Focused.firing n
122− merging n = group ("merging " <> show n) $ Focused.merging n
123− dynamics n = group ("dynamics " <> show n) $ Focused.dynamics n
124− cases = concat
125− [ sub 100 40
126− , dynamics 100
127− , dynamics 1000
128− , firing 1000
129− , firing 10000
130− , merging 10
131− , merging 50
132− , merging 100
133− , merging 200
134− ]
135−
136−pattern RunTestCaseFlag = "--run-test-case"
137−
138−spawnBenchmark :: String -> Benchmark
139−spawnBenchmark name = Benchmark name $ Benchmarkable $ \n -> do
140− self <- getExecutablePath
141− callProcess self [RunTestCaseFlag, name, show n, "+RTS", "-N1"]
142−
143−foreign import ccall unsafe "myCapabilityHasOtherRunnableThreads" myCapabilityHasOtherRunnableThreads :: IO Bool
144−
145−main :: IO ()
146−main = do
147− args <- getArgs
148− case args of
149− RunTestCaseFlag : t -> case t of
150− [name, readMaybe -> Just count] -> do
151− case lookup name benchmarks of
152− Just testCase -> testCase count
153− performGC
154− fix $ \loop -> bool (return ()) (yield >> loop) =<< myCapabilityHasOtherRunnableThreads
155− return ()
156− _ -> $failure "--run-test-case: expected test name and iteration count to follow"
157− _ -> defaultMainWith (defaultConfig { timeLimit = 20, csvFile = Just "dmap-original.csv", reportFile = Just "report.html" }) $ fmap (spawnBenchmark . fst) benchmarks
cabal.projectnew file+1
@@ -0,0 +1 @@
1+packages: .
default.nixdeleted−28
@@ -1,28 +0,0 @@
1−{ mkDerivation, ghc, base, bifunctors, containers, deepseq
2−, dependent-map, dependent-sum, exception-transformers
3−, haskell-src-exts, haskell-src-meta, hlint, lens, MemoTrie
4−, monad-control, mtl, primitive, ref-tf, semigroupoids
5−, semigroups, split, stdenv, stm, syb, template-haskell
6−, these, transformers, transformers-compat, prim-uniq
7−, data-default
8−}:
9−mkDerivation {
10− pname = "reflex";
11− version = "0.5.0";
12− src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
13− libraryHaskellDepends = [
14− base bifunctors containers dependent-map dependent-sum
15− exception-transformers haskell-src-exts haskell-src-meta lens
16− MemoTrie monad-control mtl primitive ref-tf semigroupoids
17− semigroups stm syb template-haskell these transformers
18− transformers-compat prim-uniq
19− base bifunctors containers deepseq dependent-map dependent-sum
20− mtl ref-tf split transformers data-default
21− ];
22− testHaskellDepends = if ghc.isGhcjs or false then [] else [
23− hlint
24− ];
25− homepage = "https://github.com/reflex-frp/reflex";
26− description = "Higher-order Functional Reactive Programming";
27− license = stdenv.lib.licenses.bsd3;
28−}
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−156
@@ -1,156 +0,0 @@
1−Name: reflex
2−Version: 0.5.0
3−Synopsis: Higher-order Functional Reactive Programming
4−Description: Reflex is a high-performance, deterministic, higher-order Functional Reactive Programming system
5−License: BSD3
6−License-file: LICENSE
7−Author: Ryan Trinkle
8−Maintainer: ryan.trinkle@gmail.com
9−Stability: Experimental
10−Category: FRP
11−Build-type: Simple
12−Cabal-version: >=1.9.2
13−homepage: https://github.com/reflex-frp/reflex
14−bug-reports: https://github.com/reflex-frp/reflex/issues
15−
16−flag specialize-to-spidertimeline-global
17− description: Specialize all Reflex functions to the SpiderTimeline Global implementation. This may improve performance, but will disable all other implementations.
18− default: False
19− manual: True
20−
21−flag use-reflex-optimizer
22− description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
23− default: False
24− manual: True
25−
26−library
27− hs-source-dirs: src
28− build-depends:
29− MemoTrie == 0.6.*,
30− base >= 4.7 && < 4.10,
31− bifunctors >= 5.2 && < 5.5,
32− containers == 0.5.*,
33− data-default >= 0.5 && < 0.8,
34− dependent-map >= 0.2.2 && < 0.3,
35− dependent-sum == 0.3.*,
36− exception-transformers == 0.4.*,
37− ghc,
38− haskell-src-exts >= 1.16 && < 1.18,
39− haskell-src-meta == 0.6.*,
40− lens >= 4.7 && < 5,
41− monad-control >= 1.0.1 && < 1.1,
42− mtl >= 2.1 && < 2.3,
43− prim-uniq >= 0.1.0.1 && < 0.2,
44− primitive >= 0.5 && < 0.7,
45− ref-tf == 0.4.*,
46− reflection == 2.1.*,
47− semigroupoids >= 4.0 && < 6,
48− semigroups >= 0.16 && < 0.19,
49− stm == 2.4.*,
50− syb >= 0.5 && < 0.7,
51− template-haskell >= 2.9 && < 2.12,
52− these >= 0.4 && < 0.8,
53− transformers >= 0.2,
54− transformers-compat >= 0.3
55−
56− exposed-modules:
57− Data.Functor.Misc,
58− Data.WeakBag,
59− Reflex,
60− Reflex.Class,
61− Reflex.Dynamic,
62− Reflex.Dynamic.TH,
63− Reflex.Dynamic.Uniq,
64− Reflex.DynamicWriter,
65− Reflex.FunctorMaybe,
66− Reflex.Host.Class,
67− Reflex.Optimizer,
68− Reflex.Patch,
69− Reflex.PerformEvent.Base,
70− Reflex.PerformEvent.Class,
71− Reflex.PostBuild.Class,
72− Reflex.Spider,
73− Reflex.Spider.Internal
74−
75− other-extensions: TemplateHaskell
76− ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
77− ghc-prof-options: -auto-all
78−
79− if flag(specialize-to-spidertimeline-global)
80− cpp-options: -DSPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
81− else
82− exposed-modules: Reflex.Pure
83−
84−test-suite semantics
85− type: exitcode-stdio-1.0
86− main-is: semantics.hs
87− hs-source-dirs: test
88− ghc-options: -O2 -Wall -rtsopts
89− build-depends:
90− base,
91− bifunctors,
92− containers,
93− deepseq >= 1.3 && < 1.5,
94− dependent-map,
95− dependent-sum,
96− mtl,
97− ref-tf,
98− reflex,
99− split,
100− transformers >= 0.3
101− if flag(specialize-to-spidertimeline-global)
102− buildable: False
103−
104−test-suite hlint
105− type: exitcode-stdio-1.0
106− main-is: hlint.hs
107− hs-source-dirs: test
108− build-depends: base, hlint == 1.9.*
109− if impl(ghcjs)
110− buildable: False
111−
112−benchmark spider-bench
113− type: exitcode-stdio-1.0
114− hs-source-dirs: bench
115− main-is: Main.hs
116− ghc-options: -Wall -O2 -rtsopts
117− build-depends:
118− base,
119− criterion == 1.1.*,
120− deepseq >= 1.3 && < 1.5,
121− dependent-map,
122− dependent-sum,
123− mtl,
124− primitive,
125− reflex,
126− stm,
127− transformers >= 0.3
128−
129−benchmark saulzar-bench
130− type: exitcode-stdio-1.0
131− hs-source-dirs: bench test
132− c-sources: bench-cbits/checkCapability.c
133− main-is: RunAll.hs
134− ghc-options: -Wall -O2 -rtsopts -threaded
135− ghc-prof-options: -auto-all
136− build-depends:
137− base,
138− containers == 0.5.*,
139− criterion == 1.1.*,
140− deepseq >= 1.3 && < 1.5,
141− dependent-map,
142− dependent-sum,
143− loch-th,
144− mtl,
145− primitive,
146− process,
147− ref-tf,
148− reflex,
149− split,
150− stm,
151− time,
152− transformers >= 0.3
153−
154−source-repository head
155− type: git
156− location: https://github.com/reflex-frp/reflex
release.nixnew file+49
@@ -0,0 +1,49 @@
1+{ reflex-platform-fun ? import ./dep/reflex-platform
2+}:
3+
4+let
5+ native-reflex-platform = reflex-platform-fun {};
6+ inherit (native-reflex-platform.nixpkgs) lib;
7+ systems = ["x86_64-linux" "x86_64-darwin"];
8+
9+ perPlatform = lib.genAttrs systems (system: let
10+ reflex-platform = reflex-platform-fun { inherit system; };
11+ compilers = [
12+ "ghc"
13+ "ghcjs"
14+ ] ++ lib.optionals (reflex-platform.androidSupport) [
15+ "ghcAndroidAarch64"
16+ "ghcAndroidAarch32"
17+ ] ++ lib.optionals (reflex-platform.iosSupport) [
18+ "ghcIosAarch64"
19+ ];
20+ compilerPkgs = lib.genAttrs compilers (ghc: let
21+ reflex-platform = reflex-platform-fun {
22+ inherit system;
23+ haskellOverlays = [
24+ # Use this package's source for reflex
25+ (self: super: {
26+ _dep = super._dep // {
27+ patch = builtins.filterSource (path: type: !(builtins.elem (baseNameOf path) [
28+ "release.nix"
29+ ".git"
30+ "dist"
31+ "dist-newstyle"
32+ "cabal.haskell-ci"
33+ "cabal.project"
34+ ".travis.yml"
35+ ])) ./.;
36+ };
37+ })
38+ ];
39+ };
40+ in reflex-platform.${ghc}.patch);
41+ in compilerPkgs // {
42+ cache = reflex-platform.pinBuildInputs "patch-${system}"
43+ (builtins.attrValues compilerPkgs);
44+ });
45+
46+ metaCache = native-reflex-platform.pinBuildInputs "patch-everywhere"
47+ (map (a: a.cache) (builtins.attrValues perPlatform));
48+
49+in perPlatform // { inherit metaCache; }
src/Data/Functor/Misc.hs+74−71
@@ -1,55 +1,55 @@
1{-# LANGUAGE CPP #-}
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE DeriveDataTypeable #-}
3+{-# LANGUAGE DeriveFunctor #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GADTs #-}
6{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE KindSignatures #-}
7{-# LANGUAGE LambdaCase #-}
7{-# LANGUAGE LambdaCase #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE MultiParamTypeClasses #-}
9{-# LANGUAGE PolyKinds #-}
9{-# LANGUAGE PolyKinds #-}
10{-# LANGUAGE RankNTypes #-}
10{-# LANGUAGE RankNTypes #-}
11{-# LANGUAGE ScopedTypeVariables #-}
11{-# LANGUAGE ScopedTypeVariables #-}
12{-# LANGUAGE StandaloneDeriving #-}
12{-# LANGUAGE StandaloneDeriving #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16-- | This module provides types and functions with no particular theme, but
13-- | This module provides types and functions with no particular theme, but
17-- which are relevant to the use of 'Functor'-based datastructures like
14-- which are relevant to the use of 'Functor'-based datastructures like
18-- 'Data.Dependent.Map.DMap'.
15-- 'Data.Dependent.Map.DMap'.
19module Data.Functor.Misc
16module Data.Functor.Misc
20− ( -- * Const2
17+ ( -- * Const2
21− Const2 (..)
18+ Const2 (..)
19+ , unConst2
22− , dmapToMap
20+ , dmapToMap
21+ , dmapToIntMap
22+ , dmapToMapWith
23− , mapToDMap
23+ , mapToDMap
24+ , weakenDMapWith
24− -- * WrapArg
25+ -- * WrapArg
25− , WrapArg (..)
26+ , WrapArg (..)
26− -- * Convenience functions for DMap
27+ -- * Convenience functions for DMap
27− , mapWithFunctorToDMap
28+ , mapWithFunctorToDMap
29+ , intMapWithFunctorToDMap
28− , mapKeyValuePairsMonotonic
30+ , mapKeyValuePairsMonotonic
29− , combineDMapsWithKey
31+ , combineDMapsWithKey
30− , EitherTag (..)
32+ , EitherTag (..)
31− , dmapToThese
33+ , dmapToThese
32− , eitherToDSum
34+ , eitherToDSum
33− , dsumToEither
35+ , dsumToEither
34− -- * Deprecated functions
36+ , ComposeMaybe (..)
35− , sequenceDmap
36− , wrapDMap
37− , rewrapDMap
38− , unwrapDMap
39− , unwrapDMapMaybe
40− , extractFunctorDMap
41− ) where
37+ ) where
42−
38+
43−import Control.Applicative (Applicative, (<$>))
39+import Control.Applicative ((<$>))
44−import Control.Monad.Identity
45import Data.Dependent.Map (DMap)
40import Data.Dependent.Map (DMap)
46import qualified Data.Dependent.Map as DMap
41import qualified Data.Dependent.Map as DMap
47import Data.Dependent.Sum
42import Data.Dependent.Sum
43+import Data.Functor.Identity
48import Data.GADT.Compare
44import Data.GADT.Compare
49import Data.GADT.Show
45import Data.GADT.Show
46+import Data.IntMap (IntMap)
47+import qualified Data.IntMap as IntMap
50import Data.Map (Map)
48import Data.Map (Map)
51import qualified Data.Map as Map
49import qualified Data.Map as Map
50+import Data.Some (Some, mkSome)
52import Data.These
51import Data.These
52+import Data.Type.Equality ((:~:)(Refl))
53import Data.Typeable hiding (Refl)
53import Data.Typeable hiding (Refl)
54
54
55--------------------------------------------------------------------------------
55--------------------------------------------------------------------------------
@@ -62,14 +62,18 @@ data Const2 :: * -> x -> x -> * where
62 Const2 :: k -> Const2 k v v
62 Const2 :: k -> Const2 k v v
63 deriving (Typeable)
63 deriving (Typeable)
64
64
65+-- | Extract the value from a Const2
66+unConst2 :: Const2 k v v' -> k
67+unConst2 (Const2 k) = k
68+
69+deriving instance Eq k => Eq (Const2 k v v')
70+deriving instance Ord k => Ord (Const2 k v v')
65−deriving instance Show k => Show (Const2 k v v)
71+deriving instance Show k => Show (Const2 k v v')
72+deriving instance Read k => Read (Const2 k v v)
66
73
67instance Show k => GShow (Const2 k v) where
74instance Show k => GShow (Const2 k v) where
68 gshowsPrec n x@(Const2 _) = showsPrec n x
75 gshowsPrec n x@(Const2 _) = showsPrec n x
69
76
70−instance (Show k, Show (f v)) => ShowTag (Const2 k v) f where
71− showTaggedPrec (Const2 _) = showsPrec
72−
73instance Eq k => GEq (Const2 k v) where
77instance Eq k => GEq (Const2 k v) where
74 geq (Const2 a) (Const2 b) =
78 geq (Const2 a) (Const2 b) =
75 if a == b
79 if a == b
@@ -86,6 +90,15 @@ instance Ord k => GCompare (Const2 k v) where
86dmapToMap :: DMap (Const2 k v) Identity -> Map k v
90dmapToMap :: DMap (Const2 k v) Identity -> Map k v
87dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
91dmapToMap = Map.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
88
92
93+-- | Convert a 'DMap' to an 'IntMap'
94+dmapToIntMap :: DMap (Const2 IntMap.Key v) Identity -> IntMap v
95+dmapToIntMap = IntMap.fromDistinctAscList . map (\(Const2 k :=> Identity v) -> (k, v)) . DMap.toAscList
96+
97+-- | Convert a 'DMap' to a regular 'Map', applying the given function to remove
98+-- the wrapping 'Functor'
99+dmapToMapWith :: (f v -> v') -> DMap (Const2 k v) f -> Map k v'
100+dmapToMapWith f = Map.fromDistinctAscList . map (\(Const2 k :=> v) -> (k, f v)) . DMap.toAscList
101+
89-- | Convert a regular 'Map' to a 'DMap'
102-- | Convert a regular 'Map' to a 'DMap'
90mapToDMap :: Map k v -> DMap (Const2 k v) Identity
103mapToDMap :: Map k v -> DMap (Const2 k v) Identity
91mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
104mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v) . Map.toAscList
@@ -95,6 +108,16 @@ mapToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> Identity v)
95mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
108mapWithFunctorToDMap :: Map k (f v) -> DMap (Const2 k v) f
96mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
109mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . Map.toAscList
97
110
111+-- | Convert a regular 'IntMap', where the values are already wrapped in a
112+-- functor, to a 'DMap'
113+intMapWithFunctorToDMap :: IntMap (f v) -> DMap (Const2 IntMap.Key v) f
114+intMapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v) . IntMap.toAscList
115+
116+-- | Convert a 'DMap' to a regular 'Map' by forgetting the types associated with
117+-- the keys, using a function to remove the wrapping 'Functor'
118+weakenDMapWith :: (forall a. v a -> v') -> DMap k v -> Map (Some k) v'
119+weakenDMapWith f = Map.fromDistinctAscList . map (\(k :=> v) -> (mkSome k, f v)) . DMap.toAscList
120+
98--------------------------------------------------------------------------------
121--------------------------------------------------------------------------------
99-- WrapArg
122-- WrapArg
100--------------------------------------------------------------------------------
123--------------------------------------------------------------------------------
@@ -105,6 +128,11 @@ mapWithFunctorToDMap = DMap.fromDistinctAscList . map (\(k, v) -> Const2 k :=> v
105data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
128data WrapArg :: (k -> *) -> (k -> *) -> * -> * where
106 WrapArg :: f a -> WrapArg g f (g a)
129 WrapArg :: f a -> WrapArg g f (g a)
107
130
131+deriving instance Eq (f a) => Eq (WrapArg g f (g' a))
132+deriving instance Ord (f a) => Ord (WrapArg g f (g' a))
133+deriving instance Show (f a) => Show (WrapArg g f (g' a))
134+deriving instance Read (f a) => Read (WrapArg g f (g a))
135+
108instance GEq f => GEq (WrapArg g f) where
136instance GEq f => GEq (WrapArg g f) where
109 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
137 geq (WrapArg a) (WrapArg b) = (\Refl -> Refl) <$> geq a b
110
138
@@ -154,6 +182,11 @@ dmapToThese m = case (DMap.lookup LeftTag m, DMap.lookup RightTag m) of
154data EitherTag l r a where
182data EitherTag l r a where
155 LeftTag :: EitherTag l r l
183 LeftTag :: EitherTag l r l
156 RightTag :: EitherTag l r r
184 RightTag :: EitherTag l r r
185+ deriving (Typeable)
186+
187+deriving instance Show (EitherTag l r a)
188+deriving instance Eq (EitherTag l r a)
189+deriving instance Ord (EitherTag l r a)
157
190
158instance GEq (EitherTag l r) where
191instance GEq (EitherTag l r) where
159 geq a b = case (a, b) of
192 geq a b = case (a, b) of
@@ -173,11 +206,6 @@ instance GShow (EitherTag l r) where
173 LeftTag -> showString "LeftTag"
206 LeftTag -> showString "LeftTag"
174 RightTag -> showString "RightTag"
207 RightTag -> showString "RightTag"
175
208
176−instance (Show l, Show r) => ShowTag (EitherTag l r) Identity where
177− showTaggedPrec t n (Identity a) = case t of
178− LeftTag -> showsPrec n a
179− RightTag -> showsPrec n a
180−
181-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
209-- | Convert 'Either' to a 'DSum'. Inverse of 'dsumToEither'.
182eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
210eitherToDSum :: Either a b -> DSum (EitherTag a b) Identity
183eitherToDSum = \case
211eitherToDSum = \case
@@ -190,39 +218,14 @@ dsumToEither = \case
190 (LeftTag :=> Identity a) -> Left a
218 (LeftTag :=> Identity a) -> Left a
191 (RightTag :=> Identity b) -> Right b
219 (RightTag :=> Identity b) -> Right b
192
220
193−
194--------------------------------------------------------------------------------
221--------------------------------------------------------------------------------
195−-- Deprecated functions
222+-- ComposeMaybe
196--------------------------------------------------------------------------------
223--------------------------------------------------------------------------------
197
224
198−{-# INLINE sequenceDmap #-}
225+-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
199−{-# DEPRECATED sequenceDmap "Use 'Data.Dependent.Map.traverseWithKey (\\_ t -> fmap Identity t)' instead" #-}
226+-- make the 'f' parameter have a nominal type role. We need f to be
200−-- | Run the actions contained in the 'DMap'
227+-- representational so that we can use safe 'coerce'.
201−sequenceDmap :: Applicative t => DMap f t -> t (DMap f Identity)
228+newtype ComposeMaybe f a =
202−sequenceDmap = DMap.traverseWithKey $ \_ t -> Identity <$> t
229+ ComposeMaybe { getComposeMaybe :: Maybe (f a) } deriving (Show, Eq, Ord)
203−
230+
204−{-# DEPRECATED wrapDMap "Use 'Data.Dependent.Map.map (f . runIdentity)' instead" #-}
231+deriving instance Functor f => Functor (ComposeMaybe f)
205−-- | Replace the 'Identity' functor for a 'DMap''s values with a different functor
206−wrapDMap :: (forall a. a -> f a) -> DMap k Identity -> DMap k f
207−wrapDMap f = DMap.map $ f . runIdentity
208−
209−{-# DEPRECATED rewrapDMap "Use 'Data.Dependent.Map.map' instead" #-}
210−-- | Replace one functor for a 'DMap''s values with a different functor
211−rewrapDMap :: (forall (a :: *). f a -> g a) -> DMap k f -> DMap k g
212−rewrapDMap = DMap.map
213−
214−{-# DEPRECATED unwrapDMap "Use 'Data.Dependent.Map.map (Identity . f)' instead" #-}
215−-- | Replace one functor for a 'DMap''s values with the 'Identity' functor
216−unwrapDMap :: (forall a. f a -> a) -> DMap k f -> DMap k Identity
217−unwrapDMap f = DMap.map $ Identity . f
218−
219−{-# DEPRECATED unwrapDMapMaybe "Use 'Data.Dependent.Map.mapMaybeWithKey (\\_ a -> fmap Identity $ f a)' instead" #-}
220−-- | Like 'unwrapDMap', but possibly delete some values from the DMap
221−unwrapDMapMaybe :: GCompare k => (forall a. f a -> Maybe a) -> DMap k f -> DMap k Identity
222−unwrapDMapMaybe f = DMap.mapMaybeWithKey $ \_ a -> Identity <$> f a
223−
224−{-# DEPRECATED extractFunctorDMap "Use 'mapKeyValuePairsMonotonic (\\(Const2 k :=> Identity v) -> Const2 k :=> v)' instead" #-}
225−-- | Eliminate the 'Identity' functor in a 'DMap' and replace it with the
226−-- underlying functor
227−extractFunctorDMap :: DMap (Const2 k (f v)) Identity -> DMap (Const2 k v) f
228−extractFunctorDMap = mapKeyValuePairsMonotonic $ \(Const2 k :=> Identity v) -> Const2 k :=> v
src/Data/Monoid/DecidablyEmpty.hsnew file+115
@@ -0,0 +1,115 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DefaultSignatures #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE StandaloneDeriving #-}
5+{-# LANGUAGE TypeOperators #-}
6+
7+-- TODO upstream somwhere else?
8+module Data.Monoid.DecidablyEmpty where
9+
10+import Data.Functor.Identity
11+import Data.Functor.Const
12+import Data.Monoid
13+import Data.Maybe (isNothing)
14+#if MIN_VERSION_base(4,11,0)
15+import Data.Ord
16+#endif
17+import Data.Proxy
18+import Data.Semigroup hiding (First, Last)
19+#if MIN_VERSION_base(4,12,0)
20+import GHC.Generics
21+#endif
22+
23+import qualified Data.IntSet as IntSet
24+import qualified Data.IntMap as IntMap
25+import qualified Data.Map as Map
26+import qualified Data.Sequence as Seq
27+import qualified Data.Set as Set
28+
29+import Data.GADT.Compare
30+import qualified Data.Dependent.Map as DMap
31+
32+-- | A 'DecidablyEmpty' is one where it can be computed whether or not an
33+-- arbitrary value is 'mempty'.
34+--
35+-- By using this class rather than 'Eq', we avoid unnecessary constraining the
36+-- contents of 'Functor's. This makes it possible to efficiently combine and/or
37+-- nest patch maps with 'Eq'-lacking values (e.g. functions) at the leaves.
38+class Monoid a => DecidablyEmpty a where
39+ isEmpty :: a -> Bool
40+ default isEmpty :: Eq a => a -> Bool
41+ isEmpty = (==) mempty
42+
43+-- base
44+
45+instance DecidablyEmpty Ordering
46+instance DecidablyEmpty ()
47+instance DecidablyEmpty Any
48+instance DecidablyEmpty All
49+-- instance DecidablyEmpty Lifetime
50+-- instance DecidablyEmpty Event
51+instance DecidablyEmpty [a] where
52+ isEmpty = null
53+instance
54+#if MIN_VERSION_base(4,11,0)
55+ Semigroup a
56+#else
57+ Monoid a
58+#endif
59+ => DecidablyEmpty (Maybe a) where
60+ isEmpty = isNothing
61+deriving instance (Num a, DecidablyEmpty a) => DecidablyEmpty (Product a)
62+deriving instance (DecidablyEmpty a, Num a) => DecidablyEmpty (Sum a)
63+deriving instance DecidablyEmpty a => DecidablyEmpty (Dual a)
64+instance DecidablyEmpty (First a) where
65+ isEmpty (First a) = isNothing a
66+instance DecidablyEmpty (Last a) where
67+ isEmpty (Last a) = isNothing a
68+deriving instance DecidablyEmpty a => DecidablyEmpty (Identity a)
69+instance Semigroup a => DecidablyEmpty (Option a) where
70+ isEmpty (Option a) = isNothing a
71+deriving instance DecidablyEmpty m => DecidablyEmpty (WrappedMonoid m)
72+instance (Ord a, Bounded a) => DecidablyEmpty (Max a)
73+instance (Ord a, Bounded a) => DecidablyEmpty (Min a)
74+instance DecidablyEmpty (Proxy s)
75+deriving instance DecidablyEmpty a => DecidablyEmpty (Const a b)
76+#if MIN_VERSION_base(4,11,0)
77+deriving instance DecidablyEmpty a => DecidablyEmpty (Down a)
78+#endif
79+#if MIN_VERSION_base(4,12,0)
80+deriving instance DecidablyEmpty p => DecidablyEmpty (Par1 p)
81+instance DecidablyEmpty (U1 p)
82+deriving instance DecidablyEmpty (f p) => DecidablyEmpty (Rec1 f p)
83+deriving instance DecidablyEmpty (f p) => DecidablyEmpty (M1 i c f p)
84+deriving instance DecidablyEmpty c => DecidablyEmpty (K1 i c p)
85+instance (DecidablyEmpty (f p), DecidablyEmpty (g p)) => DecidablyEmpty ((f :*: g) p) where
86+ isEmpty (x :*: y) = isEmpty x && isEmpty y
87+deriving instance DecidablyEmpty (f (g p)) => DecidablyEmpty ((f :.: g) p)
88+#endif
89+
90+instance (DecidablyEmpty a, DecidablyEmpty b) => DecidablyEmpty (a, b) where
91+ isEmpty (a, b) = isEmpty a && isEmpty b
92+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c) => DecidablyEmpty (a, b, c) where
93+ isEmpty (a, b, c) = isEmpty a && isEmpty b && isEmpty c
94+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d) => DecidablyEmpty (a, b, c, d) where
95+ isEmpty (a, b, c, d) = isEmpty a && isEmpty b && isEmpty c && isEmpty d
96+instance (DecidablyEmpty a, DecidablyEmpty b, DecidablyEmpty c, DecidablyEmpty d, DecidablyEmpty e) => DecidablyEmpty (a, b, c, d, e) where
97+ isEmpty (a, b, c, d, e) = isEmpty a && isEmpty b && isEmpty c && isEmpty d && isEmpty e
98+
99+-- containers
100+
101+instance DecidablyEmpty IntSet.IntSet where
102+ isEmpty = IntSet.null
103+instance DecidablyEmpty (IntMap.IntMap v) where
104+ isEmpty = IntMap.null
105+instance Ord k => DecidablyEmpty (Map.Map k v) where
106+ isEmpty = Map.null
107+instance DecidablyEmpty (Seq.Seq v) where
108+ isEmpty = Seq.null
109+instance Ord k => DecidablyEmpty (Set.Set k) where
110+ isEmpty = Set.null
111+
112+-- dependent-map
113+
114+instance GCompare k => DecidablyEmpty (DMap.DMap k v) where
115+ isEmpty = DMap.null
src/Data/Patch.hsnew file+95
@@ -0,0 +1,95 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE StandaloneDeriving #-}
4+{-# LANGUAGE TypeFamilies #-}
5+{-# LANGUAGE TypeOperators #-}
6+-- |
7+-- Module:
8+-- Data.Patch
9+-- Description:
10+-- This module defines the 'Patch' class.
11+module Data.Patch
12+ ( module Data.Patch
13+ , module X
14+ ) where
15+
16+import Control.Applicative
17+import Data.Functor.Const (Const (..))
18+import Data.Functor.Identity
19+import Data.Map.Monoidal (MonoidalMap)
20+import Data.Proxy
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
23+#endif
24+import GHC.Generics
25+
26+import Data.Semigroup.Additive as X
27+import Data.Patch.Class as X
28+import Data.Patch.DMap as X hiding (getDeletions)
29+import Data.Patch.DMapWithMove as X
30+ ( PatchDMapWithMove, const2PatchDMapWithMoveWith, mapPatchDMapWithMove
31+ , patchDMapWithMoveToPatchMapWithMoveWith
32+ , traversePatchDMapWithMoveWithKey, unPatchDMapWithMove
33+ , unsafePatchDMapWithMove, weakenPatchDMapWithMoveWith
34+ )
35+import Data.Patch.IntMap as X hiding (getDeletions)
36+import Data.Patch.Map as X
37+import Data.Patch.MapWithMove as X
38+ ( PatchMapWithMove, patchMapWithMoveNewElements
39+ , patchMapWithMoveNewElementsMap, unPatchMapWithMove
40+ , unsafePatchMapWithMove
41+ )
42+
43+-- | A 'Group' is a 'Monoid' where every element has an inverse.
44+class (Semigroup q, Monoid q) => Group q where
45+ negateG :: q -> q
46+ (~~) :: q -> q -> q
47+ r ~~ s = r <> negateG s
48+
49+-- | The elements of an 'Additive' 'Semigroup' can be considered as patches of their own type.
50+newtype AdditivePatch p = AdditivePatch { unAdditivePatch :: p }
51+
52+instance Additive p => Patch (AdditivePatch p) where
53+ type PatchTarget (AdditivePatch p) = p
54+ apply (AdditivePatch p) q = Just $ p <> q
55+
56+instance (Ord k, Group q) => Group (MonoidalMap k q) where
57+ negateG = fmap negateG
58+
59+-- | Trivial group.
60+instance Group () where
61+ negateG _ = ()
62+ _ ~~ _ = ()
63+
64+-- | Product group. A Pair of groups gives rise to a group
65+instance (Group a, Group b) => Group (a, b) where
66+ negateG (a, b) = (negateG a, negateG b)
67+ (a, b) ~~ (c, d) = (a ~~ c, b ~~ d)
68+
69+-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
70+-- Base does not define Monoid (Compose f g a) so this is the best we can
71+-- really do for functor composition.
72+instance Group (f (g a)) => Group ((f :.: g) a) where
73+ negateG (Comp1 xs) = Comp1 (negateG xs)
74+ Comp1 xs ~~ Comp1 ys = Comp1 (xs ~~ ys)
75+
76+-- | Product of groups, Functor style.
77+instance (Group (f a), Group (g a)) => Group ((f :*: g) a) where
78+ negateG (a :*: b) = negateG a :*: negateG b
79+ (a :*: b) ~~ (c :*: d) = (a ~~ c) :*: (b ~~ d)
80+
81+-- | Trivial group, Functor style
82+instance Group (Proxy x) where
83+ negateG _ = Proxy
84+ _ ~~ _ = Proxy
85+
86+-- | Const lifts groups into a functor.
87+deriving instance Group a => Group (Const a x)
88+
89+-- | Identity lifts groups pointwise (at only one point)
90+deriving instance Group a => Group (Identity a)
91+
92+-- | Functions lift groups pointwise.
93+instance Group b => Group (a -> b) where
94+ negateG f = negateG . f
95+ (~~) = liftA2 (~~)
src/Data/Patch/Class.hsnew file+45
@@ -0,0 +1,45 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ScopedTypeVariables #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- | The interface for types which represent changes made to other types
5+module Data.Patch.Class where
6+
7+import Data.Functor.Identity
8+import Data.Kind (Type)
9+import Data.Maybe
10+#if !MIN_VERSION_base(4,11,0)
11+import Data.Semigroup (Semigroup(..))
12+#endif
13+import Data.Proxy
14+
15+-- | A 'Patch' type represents a kind of change made to a datastructure.
16+--
17+-- If an instance of 'Patch' is also an instance of 'Semigroup', it should obey
18+-- the law that @applyAlways (f <> g) == applyAlways f . applyAlways g@.
19+class Patch p where
20+ type PatchTarget p :: Type
21+ -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
22+ -- 'Nothing'.
23+ apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
24+
25+-- | Apply a 'Patch'; if it does nothing, return the original value
26+applyAlways :: Patch p => p -> PatchTarget p -> PatchTarget p
27+applyAlways p t = fromMaybe t $ apply p t
28+
29+-- | 'Identity' can be used as a 'Patch' that always fully replaces the value
30+instance Patch (Identity a) where
31+ type PatchTarget (Identity a) = a
32+ apply (Identity a) _ = Just a
33+
34+-- | 'Proxy' can be used as a 'Patch' that does nothing.
35+instance forall (a :: Type). Patch (Proxy a) where
36+ type PatchTarget (Proxy a) = a
37+ apply ~Proxy _ = Nothing
38+
39+-- | Like '(.)', but composes functions that return patches rather than
40+-- functions that return new values. The Semigroup instance for patches must
41+-- apply patches right-to-left, like '(.)'.
42+composePatchFunctions :: (Patch p, Semigroup p) => (PatchTarget p -> p) -> (PatchTarget p -> p) -> PatchTarget p -> p
43+composePatchFunctions g f a =
44+ let fp = f a
45+ in g (applyAlways fp a) <> fp
src/Data/Patch/DMap.hsnew file+93
@@ -0,0 +1,93 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE PolyKinds #-}
5+{-# LANGUAGE Rank2Types #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+-- | 'Patch'es on 'DMap' that consist only of insertions (or overwrites) and deletions.
11+module Data.Patch.DMap where
12+
13+import Data.Patch.Class
14+import Data.Patch.IntMap
15+import Data.Patch.Map
16+
17+import Data.Dependent.Map (DMap)
18+import Data.Dependent.Sum (DSum (..))
19+import Data.GADT.Compare (GCompare (..))
20+import qualified Data.Dependent.Map as DMap
21+import Data.Functor.Constant
22+import Data.Functor.Misc
23+import qualified Data.IntMap as IntMap
24+import qualified Data.Map as Map
25+import Data.Monoid.DecidablyEmpty
26+#if !MIN_VERSION_base(4,11,0)
27+import Data.Semigroup (Semigroup (..))
28+#endif
29+import Data.Some (Some)
30+
31+-- | A set of changes to a 'DMap'. Any element may be inserted/updated or deleted.
32+-- Insertions are represented as @'ComposeMaybe' (Just value)@,
33+-- while deletions are represented as @'ComposeMaybe' Nothing@.
34+newtype PatchDMap k v = PatchDMap { unPatchDMap :: DMap k (ComposeMaybe v) }
35+
36+deriving instance GCompare k => Semigroup (PatchDMap k v)
37+
38+deriving instance GCompare k => Monoid (PatchDMap k v)
39+
40+-- It won't let me derive for some reason
41+instance GCompare k => DecidablyEmpty (PatchDMap k v) where
42+ isEmpty (PatchDMap m) = DMap.null m
43+
44+-- | Apply the insertions or deletions to a given 'DMap'.
45+instance GCompare k => Patch (PatchDMap k v) where
46+ type PatchTarget (PatchDMap k v) = DMap k v
47+ apply (PatchDMap diff) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
48+ where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
49+ deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
50+ nothingToJust = \case
51+ Nothing -> Just $ Constant ()
52+ Just _ -> Nothing
53+
54+-- | Map a function @v a -> v' a@ over any inserts/updates in the given
55+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
56+mapPatchDMap :: (forall a. v a -> v' a) -> PatchDMap k v -> PatchDMap k v'
57+mapPatchDMap f (PatchDMap p) = PatchDMap $ DMap.map (ComposeMaybe . fmap f . getComposeMaybe) p
58+
59+-- | Map an effectful function @v a -> f (v' a)@ over any inserts/updates in the given
60+-- @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
61+traversePatchDMap :: Applicative f => (forall a. v a -> f (v' a)) -> PatchDMap k v -> f (PatchDMap k v')
62+traversePatchDMap f = traversePatchDMapWithKey $ const f
63+
64+-- | Map an effectful function @k a -> v a -> f (v' a)@ over any inserts/updates
65+-- in the given @'PatchDMap' k v@ to produce a @'PatchDMap' k v'@.
66+traversePatchDMapWithKey :: Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMap k v -> m (PatchDMap k v')
67+traversePatchDMapWithKey f (PatchDMap p) = PatchDMap <$> DMap.traverseWithKey (\k (ComposeMaybe v) -> ComposeMaybe <$> traverse (f k) v) p
68+
69+-- | Weaken a @'PatchDMap' k v@ to a @'PatchMap' (Some k) v'@ using a function
70+-- @v a -> v'@ to weaken each value contained in the patch.
71+weakenPatchDMapWith :: (forall a. v a -> v') -> PatchDMap k v -> PatchMap (Some k) v'
72+weakenPatchDMapWith f (PatchDMap p) = PatchMap $ weakenDMapWith (fmap f . getComposeMaybe) p
73+
74+-- | Convert a weak @'PatchDMap' ('Const2' k a) v@ where the @a@ is known by way of
75+-- the @Const2@ into a @'PatchMap' k v'@ using a rank 1 function @v a -> v'@.
76+patchDMapToPatchMapWith :: (v a -> v') -> PatchDMap (Const2 k a) v -> PatchMap k v'
77+patchDMapToPatchMapWith f (PatchDMap p) = PatchMap $ dmapToMapWith (fmap f . getComposeMaybe) p
78+
79+-- | Convert a @'PatchMap' k v@ into a @'PatchDMap' ('Const2' k a) v'@ using a function @v -> v' a@.
80+const2PatchDMapWith :: forall k v v' a. (v -> v' a) -> PatchMap k v -> PatchDMap (Const2 k a) v'
81+const2PatchDMapWith f (PatchMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
82+ where g :: (k, Maybe v) -> DSum (Const2 k a) (ComposeMaybe v')
83+ g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
84+
85+-- | Convert a @'PatchIntMap' v@ into a @'PatchDMap' ('Const2' Int a) v'@ using a function @v -> v' a@.
86+const2IntPatchDMapWith :: forall v f a. (v -> f a) -> PatchIntMap v -> PatchDMap (Const2 IntMap.Key a) f
87+const2IntPatchDMapWith f (PatchIntMap p) = PatchDMap $ DMap.fromDistinctAscList $ g <$> IntMap.toAscList p
88+ where g :: (IntMap.Key, Maybe v) -> DSum (Const2 IntMap.Key a) (ComposeMaybe f)
89+ g (k, e) = Const2 k :=> ComposeMaybe (f <$> e)
90+
91+-- | Get the values that will be replaced or deleted if the given patch is applied to the given 'DMap'.
92+getDeletions :: GCompare k => PatchDMap k v -> DMap k v' -> DMap k v'
93+getDeletions (PatchDMap p) m = DMap.intersectionWithKey (\_ v _ -> v) m p
src/Data/Patch/DMapWithMove.hsnew file+371
@@ -0,0 +1,371 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE Rank2Types #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+
14+-- |Module containing @'PatchDMapWithMove' k v@ and associated functions, which represents a 'Patch' to a @'DMap' k v@ which can insert, update, delete, and
15+-- move values between keys.
16+module Data.Patch.DMapWithMove where
17+
18+import Data.Patch.Class
19+import Data.Patch.MapWithMove (PatchMapWithMove (..))
20+import qualified Data.Patch.MapWithMove as MapWithMove
21+
22+import Data.Constraint.Extras
23+import Data.Dependent.Map (DMap)
24+import Data.Dependent.Sum (DSum (..))
25+import qualified Data.Dependent.Map as DMap
26+import Data.Functor.Constant
27+import Data.Functor.Misc
28+import Data.Functor.Product
29+import Data.GADT.Compare (GEq (..), GCompare (..))
30+import Data.GADT.Show (GShow, gshow)
31+import qualified Data.Map as Map
32+import Data.Maybe
33+import Data.Monoid.DecidablyEmpty
34+#if !MIN_VERSION_base(4,11,0)
35+import Data.Semigroup (Semigroup (..))
36+#endif
37+import Data.Some (Some, mkSome)
38+import Data.These
39+
40+-- | Like 'PatchMapWithMove', but for 'DMap'. Each key carries a 'NodeInfo' which describes how it will be changed by the patch and connects move sources and
41+-- destinations.
42+--
43+-- Invariants:
44+--
45+-- * A key should not move to itself.
46+-- * A move should always be represented with both the destination key (as a 'From_Move') and the source key (as a @'ComposeMaybe' ('Just' destination)@)
47+newtype PatchDMapWithMove k v = PatchDMapWithMove (DMap k (NodeInfo k v))
48+
49+-- It won't let me derive for some reason
50+instance GCompare k => DecidablyEmpty (PatchDMapWithMove k v) where
51+ isEmpty (PatchDMapWithMove m) = DMap.null m
52+
53+-- |Structure which represents what changes apply to a particular key. @_nodeInfo_from@ specifies what happens to this key, and in particular what other key
54+-- the current key is moving from, while @_nodeInfo_to@ specifies what key the current key is moving to if involved in a move.
55+data NodeInfo k v a = NodeInfo
56+ { _nodeInfo_from :: !(From k v a)
57+ -- ^Change applying to the current key, be it an insert, move, or delete.
58+ , _nodeInfo_to :: !(To k a)
59+ -- ^Where this key is moving to, if involved in a move. Should only be @ComposeMaybe (Just k)@ when there is a corresponding 'From_Move'.
60+ }
61+ deriving (Show)
62+
63+-- |Structure describing a particular change to a key, be it inserting a new key (@From_Insert@), updating an existing key (@From_Insert@ again), deleting a
64+-- key (@From_Delete@), or moving a key (@From_Move@).
65+data From (k :: a -> *) (v :: a -> *) :: a -> * where
66+ -- |Insert a new or update an existing key with the given value @v a@
67+ From_Insert :: v a -> From k v a
68+ -- |Delete the existing key
69+ From_Delete :: From k v a
70+ -- |Move the value from the given key @k a@ to this key. The source key should also have an entry in the patch giving the current key as @_nodeInfo_to@,
71+ -- usually but not necessarily with @From_Delete@.
72+ From_Move :: !(k a) -> From k v a
73+ deriving (Show, Read, Eq, Ord)
74+
75+-- |Type alias for the "to" part of a 'NodeInfo'. @'ComposeMaybe' ('Just' k)@ means the key is moving to another key, @ComposeMaybe Nothing@ for any other
76+-- operation.
77+type To = ComposeMaybe
78+
79+-- |Test whether a 'PatchDMapWithMove' satisfies its invariants.
80+validPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Bool
81+validPatchDMapWithMove = not . null . validationErrorsForPatchDMapWithMove
82+
83+-- |Enumerate what reasons a 'PatchDMapWithMove' doesn't satisfy its invariants, returning @[]@ if it's valid.
84+validationErrorsForPatchDMapWithMove :: forall k v. (GCompare k, GShow k) => DMap k (NodeInfo k v) -> [String]
85+validationErrorsForPatchDMapWithMove m =
86+ noSelfMoves <> movesBalanced
87+ where
88+ noSelfMoves = mapMaybe selfMove . DMap.toAscList $ m
89+ selfMove (dst :=> NodeInfo (From_Move src) _) | Just _ <- dst `geq` src = Just $ "self move of key " <> gshow src <> " at destination side"
90+ selfMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) | Just _ <- src `geq` dst = Just $ "self move of key " <> gshow dst <> " at source side"
91+ selfMove _ = Nothing
92+ movesBalanced = mapMaybe unbalancedMove . DMap.toAscList $ m
93+ unbalancedMove (dst :=> NodeInfo (From_Move src) _) =
94+ case DMap.lookup src m of
95+ Nothing -> Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key is not in the patch"
96+ Just (NodeInfo _ (ComposeMaybe (Just dst'))) ->
97+ if isNothing (dst' `geq` dst)
98+ then Just $ "unbalanced move at destination key " <> gshow dst <> " from " <> gshow src <> " is going to " <> gshow dst' <> " instead"
99+ else Nothing
100+ _ ->
101+ Just $ "unbalanced move at destination key " <> gshow dst <> " supposedly from " <> gshow src <> " but source key has no move to key"
102+ unbalancedMove (src :=> NodeInfo _ (ComposeMaybe (Just dst))) =
103+ case DMap.lookup dst m of
104+ Nothing -> Just $ " unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not in the patch"
105+ Just (NodeInfo (From_Move src') _) ->
106+ if isNothing (src' `geq` src)
107+ then Just $ "unbalanced move at source key " <> gshow src <> " to " <> gshow dst <> " is coming from " <> gshow src' <> " instead"
108+ else Nothing
109+
110+ _ ->
111+ Just $ "unbalanced move at source key " <> gshow src <> " supposedly going to " <> gshow dst <> " but destination key is not moving"
112+ unbalancedMove _ = Nothing
113+
114+-- |Test whether two @'PatchDMapWithMove' k v@ contain the same patch operations.
115+instance (GEq k, Has' Eq k (NodeInfo k v)) => Eq (PatchDMapWithMove k v) where
116+ PatchDMapWithMove a == PatchDMapWithMove b = a == b
117+
118+-- |Higher kinded 2-tuple, identical to @Data.Functor.Product@ from base ≥ 4.9
119+data Pair1 f g a = Pair1 (f a) (g a)
120+
121+-- |Helper data structure used for composing patches using the monoid instance.
122+data Fixup k v a
123+ = Fixup_Delete
124+ | Fixup_Update (These (From k v a) (To k a))
125+
126+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
127+instance GCompare k => Semigroup (PatchDMapWithMove k v) where
128+ PatchDMapWithMove ma <> PatchDMapWithMove mb = PatchDMapWithMove m
129+ where
130+ connections = DMap.toList $ DMap.intersectionWithKey (\_ a b -> Pair1 (_nodeInfo_to a) (_nodeInfo_from b)) ma mb
131+ h :: DSum k (Pair1 (ComposeMaybe k) (From k v)) -> [DSum k (Fixup k v)]
132+ h (_ :=> Pair1 (ComposeMaybe mToAfter) editBefore) = case (mToAfter, editBefore) of
133+ (Just toAfter, From_Move fromBefore)
134+ | isJust $ fromBefore `geq` toAfter
135+ -> [toAfter :=> Fixup_Delete]
136+ | otherwise
137+ -> [ toAfter :=> Fixup_Update (This editBefore)
138+ , fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))
139+ ]
140+ (Nothing, From_Move fromBefore) -> [fromBefore :=> Fixup_Update (That (ComposeMaybe mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
141+ (Just toAfter, _) -> [toAfter :=> Fixup_Update (This editBefore)]
142+ (Nothing, _) -> []
143+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
144+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
145+ | This x <- a, That y <- b
146+ = Fixup_Update $ These x y
147+ | That y <- a, This x <- b
148+ = Fixup_Update $ These x y
149+ mergeFixups _ _ _ = error "PatchDMapWithMove: incompatible fixups"
150+ fixups = DMap.fromListWithKey mergeFixups $ concatMap h connections
151+ combineNodeInfos _ nia nib = NodeInfo
152+ { _nodeInfo_from = _nodeInfo_from nia
153+ , _nodeInfo_to = _nodeInfo_to nib
154+ }
155+ applyFixup _ ni = \case
156+ Fixup_Delete -> Nothing
157+ Fixup_Update u -> Just $ NodeInfo
158+ { _nodeInfo_from = fromMaybe (_nodeInfo_from ni) $ getHere u
159+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
160+ }
161+ m = DMap.differenceWithKey applyFixup (DMap.unionWithKey combineNodeInfos ma mb) fixups
162+ getHere :: These a b -> Maybe a
163+ getHere = \case
164+ This a -> Just a
165+ These a _ -> Just a
166+ That _ -> Nothing
167+ getThere :: These a b -> Maybe b
168+ getThere = \case
169+ This _ -> Nothing
170+ These _ b -> Just b
171+ That b -> Just b
172+
173+-- |Compose patches having the same effect as applying the patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
174+instance GCompare k => Monoid (PatchDMapWithMove k v) where
175+ mempty = PatchDMapWithMove mempty
176+ mappend = (<>)
177+
178+{-
179+mappendPatchDMapWithMoveSlow :: forall k v. (ShowTag k v, GCompare k) => PatchDMapWithMove k v -> PatchDMapWithMove k v -> PatchDMapWithMove k v
180+PatchDMapWithMove dstAfter srcAfter `mappendPatchDMapWithMoveSlow` PatchDMapWithMove dstBefore srcBefore = PatchDMapWithMove dst src
181+ where
182+ getDstAction k m = fromMaybe (From_Move k) $ DMap.lookup k m -- Any key that isn't present is treated as that key moving to itself
183+ removeRedundantDst toKey (From_Move fromKey) | isJust (toKey `geq` fromKey) = Nothing
184+ removeRedundantDst _ a = Just a
185+ f :: forall a. k a -> From k v a -> Maybe (From k v a)
186+ f toKey _ = removeRedundantDst toKey $ case getDstAction toKey dstAfter of
187+ From_Move fromKey -> getDstAction fromKey dstBefore
188+ nonMove -> nonMove
189+ dst = DMap.mapMaybeWithKey f $ DMap.union dstAfter dstBefore
190+ getSrcAction k m = fromMaybe (ComposeMaybe $ Just k) $ DMap.lookup k m
191+ removeRedundantSrc fromKey (ComposeMaybe (Just toKey)) | isJust (fromKey `geq` toKey) = Nothing
192+ removeRedundantSrc _ a = Just a
193+ g :: forall a. k a -> ComposeMaybe k a -> Maybe (ComposeMaybe k a)
194+ g fromKey _ = removeRedundantSrc fromKey $ case getSrcAction fromKey srcBefore of
195+ ComposeMaybe Nothing -> ComposeMaybe Nothing
196+ ComposeMaybe (Just toKeyBefore) -> getSrcAction toKeyBefore srcAfter
197+ src = DMap.mapMaybeWithKey g $ DMap.union srcAfter srcBefore
198+-}
199+
200+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of inserting or updating a value @v a@ to the given key @k a@, like 'DMap.insert'.
201+insertDMapKey :: k a -> v a -> PatchDMapWithMove k v
202+insertDMapKey k v =
203+ PatchDMapWithMove . DMap.singleton k $ NodeInfo (From_Insert v) (ComposeMaybe Nothing)
204+
205+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of moving the value from the first key @k a@ to the second key @k a@, equivalent to:
206+--
207+-- @
208+-- 'DMap.delete' src (maybe dmap ('DMap.insert' dst) (DMap.lookup src dmap))
209+-- @
210+moveDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
211+moveDMapKey src dst = case src `geq` dst of
212+ Nothing -> PatchDMapWithMove $ DMap.fromList
213+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe Nothing)
214+ , src :=> NodeInfo From_Delete (ComposeMaybe $ Just dst)
215+ ]
216+ Just _ -> mempty
217+
218+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
219+--
220+-- @
221+-- let aMay = DMap.lookup a dmap
222+-- bMay = DMap.lookup b dmap
223+-- in maybe id (DMap.insert a) (bMay <> aMay)
224+-- . maybe id (DMap.insert b) (aMay <> bMay)
225+-- . DMap.delete a . DMap.delete b $ dmap
226+-- @
227+swapDMapKey :: GCompare k => k a -> k a -> PatchDMapWithMove k v
228+swapDMapKey src dst = case src `geq` dst of
229+ Nothing -> PatchDMapWithMove $ DMap.fromList
230+ [ dst :=> NodeInfo (From_Move src) (ComposeMaybe $ Just src)
231+ , src :=> NodeInfo (From_Move dst) (ComposeMaybe $ Just dst)
232+ ]
233+ Just _ -> mempty
234+
235+-- |Make a @'PatchDMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'DMap.delete'.
236+deleteDMapKey :: k a -> PatchDMapWithMove k v
237+deleteDMapKey k = PatchDMapWithMove $ DMap.singleton k $ NodeInfo From_Delete $ ComposeMaybe Nothing
238+
239+{-
240+k1, k2 :: Const2 Int () ()
241+k1 = Const2 1
242+k2 = Const2 2
243+p1, p2 :: PatchDMapWithMove (Const2 Int ()) Identity
244+p1 = moveDMapKey k1 k2
245+p2 = moveDMapKey k2 k1
246+p12 = p1 <> p2
247+p21 = p2 <> p1
248+p12Slow = p1 `mappendPatchDMapWithMoveSlow` p2
249+p21Slow = p2 `mappendPatchDMapWithMoveSlow` p1
250+
251+testPatchDMapWithMove = do
252+ print p1
253+ print p2
254+ print $ p12 == deleteDMapKey k1
255+ print $ p21 == deleteDMapKey k2
256+ print $ p12Slow == deleteDMapKey k1
257+ print $ p21Slow == deleteDMapKey k2
258+
259+dst (PatchDMapWithMove x _) = x
260+src (PatchDMapWithMove _ x) = x
261+-}
262+
263+-- |Extract the 'DMap' representing the patch changes from the 'PatchDMapWithMove'.
264+unPatchDMapWithMove :: PatchDMapWithMove k v -> DMap k (NodeInfo k v)
265+unPatchDMapWithMove (PatchDMapWithMove p) = p
266+
267+-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove', without checking any invariants.
268+--
269+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchDMapWithMove' are preserved; they will not be checked.
270+unsafePatchDMapWithMove :: DMap k (NodeInfo k v) -> PatchDMapWithMove k v
271+unsafePatchDMapWithMove = PatchDMapWithMove
272+
273+-- |Wrap a 'DMap' representing patch changes into a 'PatchDMapWithMove' while checking invariants. If the invariants are satisfied, @Right p@ is returned
274+-- otherwise @Left errors@.
275+patchDMapWithMove :: (GCompare k, GShow k) => DMap k (NodeInfo k v) -> Either [String] (PatchDMapWithMove k v)
276+patchDMapWithMove dm =
277+ case validationErrorsForPatchDMapWithMove dm of
278+ [] -> Right $ unsafePatchDMapWithMove dm
279+ errs -> Left errs
280+
281+-- |Map a natural transform @v -> v'@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @'PatchDMapWithMove' k v'@.
282+mapPatchDMapWithMove :: forall k v v'. (forall a. v a -> v' a) -> PatchDMapWithMove k v -> PatchDMapWithMove k v'
283+mapPatchDMapWithMove f (PatchDMapWithMove p) = PatchDMapWithMove $
284+ DMap.map (\ni -> ni { _nodeInfo_from = g $ _nodeInfo_from ni }) p
285+ where g :: forall a. From k v a -> From k v' a
286+ g = \case
287+ From_Insert v -> From_Insert $ f v
288+ From_Delete -> From_Delete
289+ From_Move k -> From_Move k
290+
291+-- |Traverse an effectful function @forall a. v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
292+traversePatchDMapWithMove :: forall m k v v'. Applicative m => (forall a. v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
293+traversePatchDMapWithMove f = traversePatchDMapWithMoveWithKey $ const f
294+
295+-- |Map an effectful function @forall a. k a -> v a -> m (v ' a)@ over the given patch, transforming @'PatchDMapWithMove' k v@ into @m ('PatchDMapWithMove' k v')@.
296+traversePatchDMapWithMoveWithKey :: forall m k v v'. Applicative m => (forall a. k a -> v a -> m (v' a)) -> PatchDMapWithMove k v -> m (PatchDMapWithMove k v')
297+traversePatchDMapWithMoveWithKey f (PatchDMapWithMove p) = PatchDMapWithMove <$> DMap.traverseWithKey (nodeInfoMapFromM . g) p
298+ where g :: forall a. k a -> From k v a -> m (From k v' a)
299+ g k = \case
300+ From_Insert v -> From_Insert <$> f k v
301+ From_Delete -> pure From_Delete
302+ From_Move fromKey -> pure $ From_Move fromKey
303+
304+-- |Map a function which transforms @'From' k v a@ into a @'From' k v' a@ over a @'NodeInfo' k v a@.
305+nodeInfoMapFrom :: (From k v a -> From k v' a) -> NodeInfo k v a -> NodeInfo k v' a
306+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
307+
308+-- |Map an effectful function which transforms @'From' k v a@ into a @f ('From' k v' a)@ over a @'NodeInfo' k v a@.
309+nodeInfoMapFromM :: Functor f => (From k v a -> f (From k v' a)) -> NodeInfo k v a -> f (NodeInfo k v' a)
310+nodeInfoMapFromM f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
311+
312+-- |Weaken a 'PatchDMapWithMove' to a 'PatchMapWithMove' by weakening the keys from @k a@ to @'Some' k@ and applying a given weakening function @v a -> v'@ to
313+-- values.
314+weakenPatchDMapWithMoveWith :: forall k v v'. (forall a. v a -> v') -> PatchDMapWithMove k v -> PatchMapWithMove (Some k) v'
315+weakenPatchDMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ weakenDMapWith g p
316+ where g :: forall a. NodeInfo k v a -> MapWithMove.NodeInfo (Some k) v'
317+ g ni = MapWithMove.NodeInfo
318+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
319+ From_Insert v -> MapWithMove.From_Insert $ f v
320+ From_Delete -> MapWithMove.From_Delete
321+ From_Move k -> MapWithMove.From_Move $ mkSome k
322+ , MapWithMove._nodeInfo_to = mkSome <$> getComposeMaybe (_nodeInfo_to ni)
323+ }
324+
325+-- |"Weaken" a @'PatchDMapWithMove' (Const2 k a) v@ to a @'PatchMapWithMove' k v'@. Weaken is in scare quotes because the 'Const2' has already disabled any
326+-- dependency in the typing and all points are already @a@, hence the function to map each value to @v'@ is not higher rank.
327+patchDMapWithMoveToPatchMapWithMoveWith :: forall k v v' a. (v a -> v') -> PatchDMapWithMove (Const2 k a) v -> PatchMapWithMove k v'
328+patchDMapWithMoveToPatchMapWithMoveWith f (PatchDMapWithMove p) = PatchMapWithMove $ dmapToMapWith g p
329+ where g :: NodeInfo (Const2 k a) v a -> MapWithMove.NodeInfo k v'
330+ g ni = MapWithMove.NodeInfo
331+ { MapWithMove._nodeInfo_from = case _nodeInfo_from ni of
332+ From_Insert v -> MapWithMove.From_Insert $ f v
333+ From_Delete -> MapWithMove.From_Delete
334+ From_Move (Const2 k) -> MapWithMove.From_Move k
335+ , MapWithMove._nodeInfo_to = unConst2 <$> getComposeMaybe (_nodeInfo_to ni)
336+ }
337+
338+-- |"Strengthen" a @'PatchMapWithMove' k v@ into a @'PatchDMapWithMove ('Const2' k a)@; that is, turn a non-dependently-typed patch into a dependently typed
339+-- one but which always has a constant key type represented by 'Const2'. Apply the given function to each @v@ to produce a @v' a@.
340+-- Completemented by 'patchDMapWithMoveToPatchMapWithMoveWith'
341+const2PatchDMapWithMoveWith :: forall k v v' a. (v -> v' a) -> PatchMapWithMove k v -> PatchDMapWithMove (Const2 k a) v'
342+const2PatchDMapWithMoveWith f (PatchMapWithMove p) = PatchDMapWithMove $ DMap.fromDistinctAscList $ g <$> Map.toAscList p
343+ where g :: (k, MapWithMove.NodeInfo k v) -> DSum (Const2 k a) (NodeInfo (Const2 k a) v')
344+ g (k, ni) = Const2 k :=> NodeInfo
345+ { _nodeInfo_from = case MapWithMove._nodeInfo_from ni of
346+ MapWithMove.From_Insert v -> From_Insert $ f v
347+ MapWithMove.From_Delete -> From_Delete
348+ MapWithMove.From_Move fromKey -> From_Move $ Const2 fromKey
349+ , _nodeInfo_to = ComposeMaybe $ Const2 <$> MapWithMove._nodeInfo_to ni
350+ }
351+
352+-- | Apply the insertions, deletions, and moves to a given 'DMap'.
353+instance GCompare k => Patch (PatchDMapWithMove k v) where
354+ type PatchTarget (PatchDMapWithMove k v) = DMap k v
355+ apply (PatchDMapWithMove p) old = Just $! insertions `DMap.union` (old `DMap.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
356+ where insertions = DMap.mapMaybeWithKey insertFunc p
357+ insertFunc :: forall a. k a -> NodeInfo k v a -> Maybe (v a)
358+ insertFunc _ ni = case _nodeInfo_from ni of
359+ From_Insert v -> Just v
360+ From_Move k -> DMap.lookup k old
361+ From_Delete -> Nothing
362+ deletions = DMap.mapMaybeWithKey deleteFunc p
363+ deleteFunc :: forall a. k a -> NodeInfo k v a -> Maybe (Constant () a)
364+ deleteFunc _ ni = case _nodeInfo_from ni of
365+ From_Delete -> Just $ Constant ()
366+ _ -> Nothing
367+
368+-- | Get the values that will be replaced, deleted, or moved if the given patch is applied to the given 'DMap'.
369+getDeletionsAndMoves :: GCompare k => PatchDMapWithMove k v -> DMap k v' -> DMap k (Product v' (ComposeMaybe k))
370+getDeletionsAndMoves (PatchDMapWithMove p) m = DMap.intersectionWithKey f m p
371+ where f _ v ni = Pair v $ _nodeInfo_to ni
src/Data/Patch/IntMap.hsnew file+76
@@ -0,0 +1,76 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE StandaloneDeriving #-}
7+{-# LANGUAGE TemplateHaskell #-}
8+{-# LANGUAGE TypeFamilies #-}
9+
10+-- | Module containing 'PatchIntMap', a 'Patch' for 'IntMap' which allows for
11+-- insert/update or delete of associations.
12+module Data.Patch.IntMap where
13+
14+import Control.Lens
15+import Data.IntMap.Strict (IntMap)
16+import qualified Data.IntMap.Strict as IntMap
17+import Data.Maybe
18+import Data.Monoid.DecidablyEmpty
19+#if !MIN_VERSION_base(4,11,0)
20+import Data.Semigroup (Semigroup (..))
21+#endif
22+import Data.Patch.Class
23+
24+-- | 'Patch' for 'IntMap' which represents insertion or deletion of keys in the mapping.
25+-- Internally represented by 'IntMap (Maybe a)', where @Just@ means insert/update
26+-- and @Nothing@ means delete.
27+newtype PatchIntMap a = PatchIntMap { unPatchIntMap :: IntMap (Maybe a) }
28+ deriving ( Show, Read, Eq, Ord
29+ , Functor, Foldable, Traversable
30+ , Monoid, DecidablyEmpty
31+ )
32+
33+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
34+-- If the same key is modified by both patches, the one on the left will take
35+-- precedence.
36+deriving instance Semigroup (PatchIntMap v)
37+
38+makeWrapped ''PatchIntMap
39+
40+-- | Apply the insertions or deletions to a given 'IntMap'.
41+instance Patch (PatchIntMap a) where
42+ type PatchTarget (PatchIntMap a) = IntMap a
43+ apply (PatchIntMap p) v = if IntMap.null p then Nothing else Just $
44+ let removes = IntMap.filter isNothing p
45+ adds = IntMap.mapMaybe id p
46+ in IntMap.union adds $ v `IntMap.difference` removes
47+
48+instance FunctorWithIndex Int PatchIntMap
49+instance FoldableWithIndex Int PatchIntMap
50+instance TraversableWithIndex Int PatchIntMap where
51+ itraverse = itraversed . Indexed
52+ itraversed = _Wrapped .> itraversed <. traversed
53+
54+-- | Map a function @Int -> a -> b@ over all @a@s in the given @'PatchIntMap' a@
55+-- (that is, all inserts/updates), producing a @PatchIntMap b@.
56+mapIntMapPatchWithKey :: (Int -> a -> b) -> PatchIntMap a -> PatchIntMap b
57+mapIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap $ IntMap.mapWithKey (\ k mv -> f k <$> mv) m
58+
59+-- | Map an effectful function @Int -> a -> f b@ over all @a@s in the given @'PatchIntMap' a@
60+-- (that is, all inserts/updates), producing a @f (PatchIntMap b)@.
61+traverseIntMapPatchWithKey :: Applicative f => (Int -> a -> f b) -> PatchIntMap a -> f (PatchIntMap b)
62+traverseIntMapPatchWithKey f (PatchIntMap m) = PatchIntMap <$> IntMap.traverseWithKey (traverse . f) m
63+
64+-- | Extract all @a@s inserted/updated by the given @'PatchIntMap' a@.
65+patchIntMapNewElements :: PatchIntMap a -> [a]
66+patchIntMapNewElements (PatchIntMap m) = catMaybes $ IntMap.elems m
67+
68+-- | Convert the given @'PatchIntMap' a@ into an @'IntMap' a@ with all
69+-- the inserts/updates in the given patch.
70+patchIntMapNewElementsMap :: PatchIntMap a -> IntMap a
71+patchIntMapNewElementsMap (PatchIntMap m) = IntMap.mapMaybe id m
72+
73+-- | Subset the given @'IntMap' a@ to contain only the keys that would be
74+-- deleted by the given @'PatchIntMap' a@.
75+getDeletions :: PatchIntMap v -> IntMap v' -> IntMap v'
76+getDeletions (PatchIntMap m) v = IntMap.intersection v m
src/Data/Patch/Map.hsnew file+72
@@ -0,0 +1,72 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE StandaloneDeriving #-}
8+{-# LANGUAGE TemplateHaskell #-}
9+{-# LANGUAGE TypeFamilies #-}
10+
11+-- | 'Patch'es on 'Map' that consist only of insertions (including overwrites)
12+-- and deletions
13+module Data.Patch.Map where
14+
15+import Data.Patch.Class
16+
17+import Control.Lens
18+import Data.Map (Map)
19+import qualified Data.Map as Map
20+import Data.Maybe
21+import Data.Monoid.DecidablyEmpty
22+import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid)
23+
24+-- | A set of changes to a 'Map'. Any element may be inserted/updated or
25+-- deleted. Insertions are represented as values wrapped in 'Just', while
26+-- deletions are represented as 'Nothing's
27+newtype PatchMap k v = PatchMap { unPatchMap :: Map k (Maybe v) }
28+ deriving ( Show, Read, Eq, Ord
29+ , Foldable, Traversable
30+ , DecidablyEmpty
31+ )
32+
33+-- | 'fmap'ping a 'PatchMap' will alter all of the values it will insert.
34+-- Deletions are unaffected.
35+deriving instance Functor (PatchMap k)
36+-- | The empty 'PatchMap' contains no insertions or deletions
37+deriving instance Ord k => Monoid (PatchMap k v)
38+
39+-- | @a <> b@ will apply the changes of @b@ and then apply the changes of @a@.
40+-- If the same key is modified by both patches, the one on the left will take
41+-- precedence.
42+instance Ord k => Semigroup (PatchMap k v) where
43+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
44+ -- PatchMap is idempotent, so stimes n is id for every n
45+ stimes = stimesIdempotentMonoid
46+
47+-- | Apply the insertions or deletions to a given 'Map'.
48+instance Ord k => Patch (PatchMap k v) where
49+ type PatchTarget (PatchMap k v) = Map k v
50+ {-# INLINABLE apply #-}
51+ apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
52+ where insertions = Map.mapMaybeWithKey (const id) p
53+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
54+ nothingToJust = \case
55+ Nothing -> Just ()
56+ Just _ -> Nothing
57+
58+instance FunctorWithIndex k (PatchMap k)
59+instance FoldableWithIndex k (PatchMap k)
60+instance TraversableWithIndex k (PatchMap k) where
61+ itraverse = itraversed . Indexed
62+ itraversed = _Wrapped .> itraversed <. traversed
63+
64+-- | Returns all the new elements that will be added to the 'Map'
65+patchMapNewElements :: PatchMap k v -> [v]
66+patchMapNewElements (PatchMap p) = catMaybes $ Map.elems p
67+
68+-- | Returns all the new elements that will be added to the 'Map'
69+patchMapNewElementsMap :: PatchMap k v -> Map k v
70+patchMapNewElementsMap (PatchMap p) = Map.mapMaybe id p
71+
72+makeWrapped ''PatchMap
src/Data/Patch/MapWithMove.hsnew file+312
@@ -0,0 +1,312 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE DeriveTraversable #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE PatternGuards #-}
9+{-# LANGUAGE PatternSynonyms #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE StandaloneDeriving #-}
12+{-# LANGUAGE TemplateHaskell #-}
13+{-# LANGUAGE TypeApplications #-}
14+{-# LANGUAGE TypeFamilies #-}
15+{-# LANGUAGE ViewPatterns #-}
16+
17+-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
18+-- another
19+module Data.Patch.MapWithMove
20+ ( PatchMapWithMove
21+ ( PatchMapWithMove
22+ , unPatchMapWithMove
23+ , ..
24+ )
25+ , patchMapWithMove
26+ , patchMapWithMoveInsertAll
27+ , insertMapKey
28+ , moveMapKey
29+ , swapMapKey
30+ , deleteMapKey
31+ , unsafePatchMapWithMove
32+ , patchMapWithMoveNewElements
33+ , patchMapWithMoveNewElementsMap
34+ , patchThatSortsMapWith
35+ , patchThatChangesAndSortsMapWith
36+ , patchThatChangesMap
37+
38+ -- * Node Info
39+ , NodeInfo
40+ ( NodeInfo
41+ , _nodeInfo_to
42+ , _nodeInfo_from
43+ , ..
44+ )
45+ , bitraverseNodeInfo
46+ , nodeInfoMapFrom
47+ , nodeInfoMapMFrom
48+ , nodeInfoSetTo
49+
50+ -- * From
51+ , From
52+ ( From_Insert
53+ , From_Delete
54+ , From_Move
55+ , ..
56+ )
57+ , bitraverseFrom
58+
59+ -- * To
60+ , To
61+ ) where
62+
63+import Data.Coerce
64+import Data.Kind (Type)
65+import Data.Patch.Class
66+import Data.Patch.MapWithPatchingMove (PatchMapWithPatchingMove(..), To)
67+import qualified Data.Patch.MapWithPatchingMove as PM -- already a transparent synonym
68+
69+import Control.Lens
70+import Data.List
71+import Data.Map (Map)
72+import qualified Data.Map as Map
73+import Data.Proxy
74+#if !MIN_VERSION_base(4,11,0)
75+import Data.Semigroup (Semigroup (..))
76+#endif
77+import Data.Traversable (foldMapDefault)
78+
79+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
80+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
81+-- and vice versa. There should never be any unpaired From/To keys.
82+newtype PatchMapWithMove k (v :: Type) = PatchMapWithMove'
83+ { -- | Extract the underlying 'PatchMapWithPatchingMove k (Proxy v)'
84+ unPatchMapWithMove' :: PatchMapWithPatchingMove k (Proxy v)
85+ }
86+ deriving ( Show, Read, Eq, Ord
87+-- Haddock cannot handle documentation here before GHC 8.6
88+ ,
89+#if __GLASGOW_HASKELL__ >= 806
90+ -- | Compose patches having the same effect as applying the
91+ -- patches in turn: @'applyAlways' (p <> q) == 'applyAlways' p .
92+ -- 'applyAlways' q@
93+#endif
94+ Semigroup
95+ , Monoid
96+ )
97+
98+pattern Coerce :: Coercible a b => a -> b
99+pattern Coerce x <- (coerce -> x)
100+ where Coerce x = coerce x
101+
102+{-# COMPLETE PatchMapWithMove #-}
103+pattern PatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
104+-- | Extract the representation of the 'PatchMapWithMove' as a map of
105+-- 'NodeInfo'.
106+unPatchMapWithMove :: PatchMapWithMove k v -> Map k (NodeInfo k v)
107+pattern PatchMapWithMove { unPatchMapWithMove } = PatchMapWithMove' (PatchMapWithPatchingMove (Coerce unPatchMapWithMove))
108+
109+_PatchMapWithMove
110+ :: Iso
111+ (PatchMapWithMove k0 v0)
112+ (PatchMapWithMove k1 v1)
113+ (Map k0 (NodeInfo k0 v0))
114+ (Map k1 (NodeInfo k1 v1))
115+_PatchMapWithMove = iso unPatchMapWithMove PatchMapWithMove
116+
117+instance Functor (PatchMapWithMove k) where
118+ fmap f = runIdentity . traverse (Identity . f)
119+
120+instance Foldable (PatchMapWithMove k) where
121+ foldMap = foldMapDefault
122+
123+instance Traversable (PatchMapWithMove k) where
124+ traverse =
125+ _PatchMapWithMove .
126+ traverse .
127+ traverse
128+
129+instance FunctorWithIndex k (PatchMapWithMove k)
130+instance FoldableWithIndex k (PatchMapWithMove k)
131+instance TraversableWithIndex k (PatchMapWithMove k) where
132+ itraverse = itraversed . Indexed
133+ itraversed =
134+ _PatchMapWithMove .>
135+ itraversed <.
136+ traverse
137+
138+-- | Create a 'PatchMapWithMove', validating it
139+patchMapWithMove :: Ord k => Map k (NodeInfo k v) -> Maybe (PatchMapWithMove k v)
140+patchMapWithMove = fmap PatchMapWithMove' . PM.patchMapWithPatchingMove . coerce
141+
142+-- | Create a 'PatchMapWithMove' that inserts everything in the given 'Map'
143+patchMapWithMoveInsertAll :: Map k v -> PatchMapWithMove k v
144+patchMapWithMoveInsertAll = PatchMapWithMove' . PM.patchMapWithPatchingMoveInsertAll
145+
146+-- | Make a @'PatchMapWithMove' k v@ which has the effect of inserting or updating a value @v@ to the given key @k@, like 'Map.insert'.
147+insertMapKey :: k -> v -> PatchMapWithMove k v
148+insertMapKey k v = PatchMapWithMove' $ PM.insertMapKey k v
149+
150+-- |Make a @'PatchMapWithMove' k v@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
151+--
152+-- @
153+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
154+-- @
155+moveMapKey :: Ord k => k -> k -> PatchMapWithMove k v
156+moveMapKey src dst = PatchMapWithMove' $ PM.moveMapKey src dst
157+
158+-- |Make a @'PatchMapWithMove' k v@ which has the effect of swapping two keys in the mapping, equivalent to:
159+--
160+-- @
161+-- let aMay = Map.lookup a map
162+-- bMay = Map.lookup b map
163+-- in maybe id (Map.insert a) (bMay <> aMay)
164+-- . maybe id (Map.insert b) (aMay <> bMay)
165+-- . Map.delete a . Map.delete b $ map
166+-- @
167+swapMapKey :: Ord k => k -> k -> PatchMapWithMove k v
168+swapMapKey src dst = PatchMapWithMove' $ PM.swapMapKey src dst
169+
170+-- |Make a @'PatchMapWithMove' k v@ which has the effect of deleting a key in the mapping, equivalent to 'Map.delete'.
171+deleteMapKey :: k -> PatchMapWithMove k v
172+deleteMapKey = PatchMapWithMove' . PM.deleteMapKey
173+
174+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithMove' k v@, without checking any invariants.
175+--
176+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithMove' are preserved; they will not be checked.
177+unsafePatchMapWithMove :: Map k (NodeInfo k v) -> PatchMapWithMove k v
178+unsafePatchMapWithMove = coerce PM.unsafePatchMapWithPatchingMove
179+
180+-- | Apply the insertions, deletions, and moves to a given 'Map'
181+instance Ord k => Patch (PatchMapWithMove k v) where
182+ type PatchTarget (PatchMapWithMove k v) = Map k v
183+ apply (PatchMapWithMove' p) = apply p
184+
185+-- | Returns all the new elements that will be added to the 'Map'.
186+patchMapWithMoveNewElements :: PatchMapWithMove k v -> [v]
187+patchMapWithMoveNewElements = PM.patchMapWithPatchingMoveNewElements . unPatchMapWithMove'
188+
189+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithMove' k v@.
190+patchMapWithMoveNewElementsMap :: PatchMapWithMove k v -> Map k v
191+patchMapWithMoveNewElementsMap = PM.patchMapWithPatchingMoveNewElementsMap . unPatchMapWithMove'
192+
193+-- | Create a 'PatchMapWithMove' that, if applied to the given 'Map', will sort
194+-- its values using the given ordering function. The set keys of the 'Map' is
195+-- not changed.
196+patchThatSortsMapWith :: Ord k => (v -> v -> Ordering) -> Map k v -> PatchMapWithMove k v
197+patchThatSortsMapWith cmp = PatchMapWithMove' . PM.patchThatSortsMapWith cmp
198+
199+-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
200+-- will produce a 'Map' with the same values as the second 'Map' but with the
201+-- values sorted with the given ordering function.
202+patchThatChangesAndSortsMapWith :: (Ord k, Ord v) => (v -> v -> Ordering) -> Map k v -> Map k v -> PatchMapWithMove k v
203+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
204+ where newList = Map.toList newByIndexUnsorted
205+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
206+
207+-- | Create a 'PatchMapWithMove' that, if applied to the first 'Map' provided,
208+-- will produce the second 'Map'.
209+patchThatChangesMap :: (Ord k, Ord v) => Map k v -> Map k v -> PatchMapWithMove k v
210+patchThatChangesMap oldByIndex newByIndex = PatchMapWithMove' $
211+ PM.patchThatChangesMap oldByIndex newByIndex
212+
213+--
214+-- NodeInfo
215+--
216+
217+-- | Holds the information about each key: where its new value should come from,
218+-- and where its old value should go to
219+newtype NodeInfo k (v :: Type) = NodeInfo' { unNodeInfo' :: PM.NodeInfo k (Proxy v) }
220+
221+deriving instance (Show k, Show p) => Show (NodeInfo k p)
222+deriving instance (Read k, Read p) => Read (NodeInfo k p)
223+deriving instance (Eq k, Eq p) => Eq (NodeInfo k p)
224+deriving instance (Ord k, Ord p) => Ord (NodeInfo k p)
225+
226+{-# COMPLETE NodeInfo #-}
227+pattern NodeInfo :: To k -> From k v -> NodeInfo k v
228+_nodeInfo_to :: NodeInfo k v -> To k
229+_nodeInfo_from :: NodeInfo k v -> From k v
230+pattern NodeInfo { _nodeInfo_to, _nodeInfo_from } = NodeInfo'
231+ PM.NodeInfo
232+ { PM._nodeInfo_to = _nodeInfo_to
233+ , PM._nodeInfo_from = Coerce _nodeInfo_from
234+ }
235+
236+_NodeInfo
237+ :: Iso
238+ (NodeInfo k0 v0)
239+ (NodeInfo k1 v1)
240+ (PM.NodeInfo k0 (Proxy v0))
241+ (PM.NodeInfo k1 (Proxy v1))
242+_NodeInfo = iso unNodeInfo' NodeInfo'
243+
244+instance Functor (NodeInfo k) where
245+ fmap f = runIdentity . traverse (Identity . f)
246+
247+instance Foldable (NodeInfo k) where
248+ foldMap = foldMapDefault
249+
250+instance Traversable (NodeInfo k) where
251+ traverse = bitraverseNodeInfo pure
252+
253+bitraverseNodeInfo
254+ :: Applicative f
255+ => (k0 -> f k1)
256+ -> (v0 -> f v1)
257+ -> NodeInfo k0 v0 -> f (NodeInfo k1 v1)
258+bitraverseNodeInfo fk fv = fmap NodeInfo'
259+ . PM.bitraverseNodeInfo fk (\ ~Proxy -> pure Proxy) fv
260+ . coerce
261+
262+-- | Change the 'From' value of a 'NodeInfo'
263+nodeInfoMapFrom :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
264+nodeInfoMapFrom f = coerce $ PM.nodeInfoMapFrom (unFrom' . f . From')
265+
266+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
267+-- 'Applicative', 'Monad', etc.) action to get the new value
268+nodeInfoMapMFrom
269+ :: Functor f
270+ => (From k v -> f (From k v))
271+ -> NodeInfo k v -> f (NodeInfo k v)
272+nodeInfoMapMFrom f = fmap NodeInfo'
273+ . PM.nodeInfoMapMFrom (fmap unFrom' . f . From')
274+ . coerce
275+
276+-- | Set the 'To' field of a 'NodeInfo'
277+nodeInfoSetTo :: To k -> NodeInfo k v -> NodeInfo k v
278+nodeInfoSetTo = coerce . PM.nodeInfoSetTo
279+
280+--
281+-- From
282+--
283+
284+-- | Describe how a key's new value should be produced
285+newtype From k (v :: Type) = From' { unFrom' :: PM.From k (Proxy v) }
286+
287+{-# COMPLETE From_Insert, From_Delete, From_Move #-}
288+
289+-- | Insert the given value here
290+pattern From_Insert :: v -> From k v
291+pattern From_Insert v = From' (PM.From_Insert v)
292+
293+-- | Delete the existing value, if any, from here
294+pattern From_Delete :: From k v
295+pattern From_Delete = From' PM.From_Delete
296+
297+-- | Move the value here from the given key
298+pattern From_Move :: k -> From k v
299+pattern From_Move k = From' (PM.From_Move k Proxy)
300+
301+bitraverseFrom
302+ :: Applicative f
303+ => (k0 -> f k1)
304+ -> (v0 -> f v1)
305+ -> From k0 v0 -> f (From k1 v1)
306+bitraverseFrom fk fv = fmap From'
307+ . PM.bitraverseFrom fk (\ ~Proxy -> pure Proxy) fv
308+ . coerce
309+
310+makeWrapped ''PatchMapWithMove
311+makeWrapped ''NodeInfo
312+makeWrapped ''From
src/Data/Patch/MapWithPatchingMove.hsnew file+432
@@ -0,0 +1,432 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE PatternGuards #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE StandaloneDeriving #-}
10+{-# LANGUAGE TemplateHaskell #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE UndecidableInstances #-}
13+
14+-- | 'Patch'es on 'Map' that can insert, delete, and move values from one key to
15+-- another
16+module Data.Patch.MapWithPatchingMove
17+ ( PatchMapWithPatchingMove (..)
18+ , patchMapWithPatchingMove
19+ , patchMapWithPatchingMoveInsertAll
20+ , insertMapKey
21+ , moveMapKey
22+ , swapMapKey
23+ , deleteMapKey
24+ , unsafePatchMapWithPatchingMove
25+ , patchMapWithPatchingMoveNewElements
26+ , patchMapWithPatchingMoveNewElementsMap
27+ , patchThatSortsMapWith
28+ , patchThatChangesAndSortsMapWith
29+ , patchThatChangesMap
30+
31+ -- * Node Info
32+ , NodeInfo (..)
33+ , bitraverseNodeInfo
34+ , nodeInfoMapFrom
35+ , nodeInfoMapMFrom
36+ , nodeInfoSetTo
37+
38+ -- * From
39+ , From(..)
40+ , bitraverseFrom
41+
42+ -- * To
43+ , To
44+
45+ -- TODO internals module
46+ , Fixup (..)
47+ ) where
48+
49+import Data.Patch.Class
50+
51+import Control.Lens hiding (from, to)
52+import Control.Lens.TH (makeWrapped)
53+import Data.Align (align)
54+import Data.Foldable (toList)
55+import Data.Function
56+import Data.List
57+import Data.Map (Map)
58+import qualified Data.Map as Map
59+import Data.Maybe
60+#if !MIN_VERSION_base(4,11,0)
61+import Data.Semigroup (Semigroup (..))
62+#endif
63+import Data.Monoid.DecidablyEmpty
64+import Data.Set (Set)
65+import qualified Data.Set as Set
66+import Data.These (These (..))
67+
68+-- | Patch a Map with additions, deletions, and moves. Invariant: If key @k1@
69+-- is coming from @From_Move k2@, then key @k2@ should be going to @Just k1@,
70+-- and vice versa. There should never be any unpaired From/To keys.
71+newtype PatchMapWithPatchingMove k p = PatchMapWithPatchingMove
72+ { -- | Extract the internal representation of the 'PatchMapWithPatchingMove'
73+ unPatchMapWithPatchingMove :: Map k (NodeInfo k p)
74+ }
75+
76+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (PatchMapWithPatchingMove k p)
77+deriving instance (Ord k, Read k, Read p, Read (PatchTarget p)) => Read (PatchMapWithPatchingMove k p)
78+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (PatchMapWithPatchingMove k p)
79+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (PatchMapWithPatchingMove k p)
80+
81+deriving instance ( Ord k
82+#if !MIN_VERSION_base(4,11,0)
83+ , Semigroup p
84+#endif
85+ , DecidablyEmpty p
86+ , Patch p
87+ ) => DecidablyEmpty (PatchMapWithPatchingMove k p)
88+
89+-- | Create a 'PatchMapWithPatchingMove', validating it
90+patchMapWithPatchingMove
91+ :: Ord k => Map k (NodeInfo k p) -> Maybe (PatchMapWithPatchingMove k p)
92+patchMapWithPatchingMove m = if valid then Just $ PatchMapWithPatchingMove m else Nothing
93+ where valid = forwardLinks == backwardLinks
94+ forwardLinks = Map.mapMaybe _nodeInfo_to m
95+ backwardLinks = Map.fromList $ catMaybes $ flip fmap (Map.toList m) $ \(to, p) ->
96+ case _nodeInfo_from p of
97+ From_Move from _ -> Just (from, to)
98+ _ -> Nothing
99+
100+-- | Create a 'PatchMapWithPatchingMove' that inserts everything in the given 'Map'
101+patchMapWithPatchingMoveInsertAll
102+ :: Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
103+patchMapWithPatchingMoveInsertAll m = PatchMapWithPatchingMove $ flip fmap m $ \v -> NodeInfo
104+ { _nodeInfo_from = From_Insert v
105+ , _nodeInfo_to = Nothing
106+ }
107+
108+-- | Make a @'PatchMapWithPatchingMove' k p@ which has the effect of inserting or replacing a value @v@ at the given key @k@, like 'Map.insert'.
109+insertMapKey
110+ :: k -> PatchTarget p -> PatchMapWithPatchingMove k p
111+insertMapKey k v = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo (From_Insert v) Nothing
112+
113+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of moving the value from the first key @k@ to the second key @k@, equivalent to:
114+--
115+-- @
116+-- 'Map.delete' src (maybe map ('Map.insert' dst) (Map.lookup src map))
117+-- @
118+moveMapKey
119+ :: ( DecidablyEmpty p
120+#if !MIN_VERSION_base(4,11,0)
121+ , Semigroup p
122+#endif
123+ , Patch p
124+ )
125+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
126+moveMapKey src dst
127+ | src == dst = mempty
128+ | otherwise =
129+ PatchMapWithPatchingMove $ Map.fromList
130+ [ (dst, NodeInfo (From_Move src mempty) Nothing)
131+ , (src, NodeInfo From_Delete (Just dst))
132+ ]
133+
134+-- |Make a @'PatchMapWithPatchingMove' k p@ which has the effect of swapping two keys in the mapping, equivalent to:
135+--
136+-- @
137+-- let aMay = Map.lookup a map
138+-- bMay = Map.lookup b map
139+-- in maybe id (Map.insert a) (bMay <> aMay)
140+-- . maybe id (Map.insert b) (aMay <> bMay)
141+-- . Map.delete a . Map.delete b $ map
142+-- @
143+swapMapKey
144+ :: ( DecidablyEmpty p
145+#if !MIN_VERSION_base(4,11,0)
146+ , Semigroup p
147+#endif
148+ , Patch p
149+ )
150+ => Ord k => k -> k -> PatchMapWithPatchingMove k p
151+swapMapKey src dst
152+ | src == dst = mempty
153+ | otherwise =
154+ PatchMapWithPatchingMove $ Map.fromList
155+ [ (dst, NodeInfo (From_Move src mempty) (Just src))
156+ , (src, NodeInfo (From_Move dst mempty) (Just dst))
157+ ]
158+
159+-- | Make a @'PatchMapWithPatchingMove' k v@ which has the effect of deleting a key in
160+-- the mapping, equivalent to 'Map.delete'.
161+deleteMapKey
162+ :: k -> PatchMapWithPatchingMove k v
163+deleteMapKey k = PatchMapWithPatchingMove . Map.singleton k $ NodeInfo From_Delete Nothing
164+
165+-- | Wrap a @'Map' k (NodeInfo k v)@ representing patch changes into a @'PatchMapWithPatchingMove' k v@, without checking any invariants.
166+--
167+-- __Warning:__ when using this function, you must ensure that the invariants of 'PatchMapWithPatchingMove' are preserved; they will not be checked.
168+unsafePatchMapWithPatchingMove
169+ :: Map k (NodeInfo k p) -> PatchMapWithPatchingMove k p
170+unsafePatchMapWithPatchingMove = PatchMapWithPatchingMove
171+
172+-- | Apply the insertions, deletions, and moves to a given 'Map'
173+instance (Ord k, Patch p) => Patch (PatchMapWithPatchingMove k p) where
174+ type PatchTarget (PatchMapWithPatchingMove k p) = Map k (PatchTarget p)
175+ -- TODO: return Nothing sometimes
176+ -- Note: the strict application here is critical to ensuring that incremental
177+ -- merges don't hold onto all their prerequisite events forever; can we make
178+ -- this more robust?
179+ apply (PatchMapWithPatchingMove m) old = Just $! insertions `Map.union` (old `Map.difference` deletions)
180+ where insertions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
181+ From_Insert v -> Just v
182+ From_Move k p -> applyAlways p <$> Map.lookup k old
183+ From_Delete -> Nothing
184+ deletions = flip Map.mapMaybeWithKey m $ \_ ni -> case _nodeInfo_from ni of
185+ From_Delete -> Just ()
186+ _ -> Nothing
187+
188+-- | Returns all the new elements that will be added to the 'Map'
189+patchMapWithPatchingMoveNewElements
190+ :: PatchMapWithPatchingMove k p -> [PatchTarget p]
191+patchMapWithPatchingMoveNewElements = Map.elems . patchMapWithPatchingMoveNewElementsMap
192+
193+-- | Return a @'Map' k v@ with all the inserts/updates from the given @'PatchMapWithPatchingMove' k v@.
194+patchMapWithPatchingMoveNewElementsMap
195+ :: PatchMapWithPatchingMove k p -> Map k (PatchTarget p)
196+patchMapWithPatchingMoveNewElementsMap (PatchMapWithPatchingMove p) = Map.mapMaybe f p
197+ where f ni = case _nodeInfo_from ni of
198+ From_Insert v -> Just v
199+ From_Move _ _ -> Nothing
200+ From_Delete -> Nothing
201+
202+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the given 'Map', will sort
203+-- its values using the given ordering function. The set keys of the 'Map' is
204+-- not changed.
205+patchThatSortsMapWith
206+ :: (Ord k, Monoid p)
207+ => (PatchTarget p -> PatchTarget p -> Ordering)
208+ -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
209+patchThatSortsMapWith cmp m = PatchMapWithPatchingMove $ Map.fromList $ catMaybes $ zipWith g unsorted sorted
210+ where unsorted = Map.toList m
211+ sorted = sortBy (cmp `on` snd) unsorted
212+ f (to, _) (from, _) = if to == from then Nothing else
213+ Just (from, to)
214+ reverseMapping = Map.fromList $ catMaybes $ zipWith f unsorted sorted
215+ g (to, _) (from, _) = if to == from then Nothing else
216+ let Just movingTo = Map.lookup from reverseMapping
217+ in Just (to, NodeInfo (From_Move from mempty) $ Just movingTo)
218+
219+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
220+-- will produce a 'Map' with the same values as the second 'Map' but with the
221+-- values sorted with the given ordering function.
222+patchThatChangesAndSortsMapWith
223+ :: forall k p. (Ord k, Ord (PatchTarget p), Monoid p)
224+ => (PatchTarget p -> PatchTarget p -> Ordering)
225+ -> Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
226+patchThatChangesAndSortsMapWith cmp oldByIndex newByIndexUnsorted = patchThatChangesMap oldByIndex newByIndex
227+ where newList = Map.toList newByIndexUnsorted
228+ newByIndex = Map.fromList $ zip (fst <$> newList) $ sortBy cmp $ snd <$> newList
229+
230+-- | Create a 'PatchMapWithPatchingMove' that, if applied to the first 'Map' provided,
231+-- will produce the second 'Map'.
232+-- Note: this will never produce a patch on a value.
233+patchThatChangesMap
234+ :: forall k p
235+ . (Ord k, Ord (PatchTarget p), Monoid p)
236+ => Map k (PatchTarget p) -> Map k (PatchTarget p) -> PatchMapWithPatchingMove k p
237+patchThatChangesMap oldByIndex newByIndex = patch
238+ where invert :: Map k (PatchTarget p) -> Map (PatchTarget p) (Set k)
239+ invert = Map.fromListWith (<>) . fmap (\(k, v) -> (v, Set.singleton k)) . Map.toList
240+ -- In the places where we use unionDistinct, a non-distinct key indicates a bug in this function
241+ unionDistinct :: forall k' v'. Ord k' => Map k' v' -> Map k' v' -> Map k' v'
242+ unionDistinct = Map.unionWith (error "patchThatChangesMap: non-distinct keys")
243+ unionPairDistinct :: (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k)) -> (Map k (From k v), Map k (To k))
244+ unionPairDistinct (oldFroms, oldTos) (newFroms, newTos) = (unionDistinct oldFroms newFroms, unionDistinct oldTos newTos)
245+ -- Generate patch info for a single value
246+ -- Keys that are found in both the old and new sets will not be patched
247+ -- Keys that are found in only the old set will be moved to a new position if any are available; otherwise they will be deleted
248+ -- Keys that are found in only the new set will be populated by moving an old key if any are available; otherwise they will be inserted
249+ patchSingleValue :: PatchTarget p -> Set k -> Set k -> (Map k (From k p), Map k (To k))
250+ patchSingleValue v oldKeys newKeys = foldl' unionPairDistinct mempty $ align (toList $ oldKeys `Set.difference` newKeys) (toList $ newKeys `Set.difference` oldKeys) <&> \case
251+ This oldK -> (mempty, Map.singleton oldK Nothing) -- There's nowhere for this value to go, so we know we are deleting it
252+ That newK -> (Map.singleton newK $ From_Insert v, mempty) -- There's nowhere fo this value to come from, so we know we are inserting it
253+ These oldK newK -> (Map.singleton newK $ From_Move oldK mempty, Map.singleton oldK $ Just newK)
254+ -- Run patchSingleValue on a These. Missing old or new sets are considered empty
255+ patchSingleValueThese :: PatchTarget p -> These (Set k) (Set k) -> (Map k (From k p), Map k (To k))
256+ patchSingleValueThese v = \case
257+ This oldKeys -> patchSingleValue v oldKeys mempty
258+ That newKeys -> patchSingleValue v mempty newKeys
259+ These oldKeys newKeys -> patchSingleValue v oldKeys newKeys
260+ -- Generate froms and tos for all values, then merge them together
261+ (froms, tos) = foldl' unionPairDistinct mempty $ Map.mapWithKey patchSingleValueThese $ align (invert oldByIndex) (invert newByIndex)
262+ patch = unsafePatchMapWithPatchingMove $ align froms tos <&> \case
263+ This from -> NodeInfo from Nothing -- Since we don't have a 'to' record for this key, that must mean it isn't being moved anywhere, so it should be deleted.
264+ That to -> NodeInfo From_Delete to -- Since we don't have a 'from' record for this key, it must be getting deleted
265+ These from to -> NodeInfo from to
266+
267+--
268+-- NodeInfo
269+--
270+
271+-- | Holds the information about each key: where its new value should come from,
272+-- and where its old value should go to
273+data NodeInfo k p = NodeInfo
274+ { _nodeInfo_from :: !(From k p)
275+ -- ^ Where do we get the new value for this key?
276+ , _nodeInfo_to :: !(To k)
277+ -- ^ If the old value is being kept (i.e. moved rather than deleted or
278+ -- replaced), where is it going?
279+ }
280+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (NodeInfo k p)
281+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (NodeInfo k p)
282+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (NodeInfo k p)
283+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (NodeInfo k p)
284+
285+-- | Traverse the 'NodeInfo' over the key, patch, and patch target. Because of
286+-- the type families here, this doesn't it any bi- or tri-traversal class.
287+bitraverseNodeInfo
288+ :: Applicative f
289+ => (k0 -> f k1)
290+ -> (p0 -> f p1)
291+ -> (PatchTarget p0 -> f (PatchTarget p1))
292+ -> NodeInfo k0 p0 -> f (NodeInfo k1 p1)
293+bitraverseNodeInfo fk fp fpt (NodeInfo from to) = NodeInfo
294+ <$> bitraverseFrom fk fp fpt from
295+ <*> traverse fk to
296+
297+-- | Change the 'From' value of a 'NodeInfo'
298+nodeInfoMapFrom
299+ :: (From k v -> From k v) -> NodeInfo k v -> NodeInfo k v
300+nodeInfoMapFrom f ni = ni { _nodeInfo_from = f $ _nodeInfo_from ni }
301+
302+-- | Change the 'From' value of a 'NodeInfo', using a 'Functor' (or
303+-- 'Applicative', 'Monad', etc.) action to get the new value
304+nodeInfoMapMFrom
305+ :: Functor f => (From k v -> f (From k v)) -> NodeInfo k v -> f (NodeInfo k v)
306+nodeInfoMapMFrom f ni = fmap (\result -> ni { _nodeInfo_from = result }) $ f $ _nodeInfo_from ni
307+
308+-- | Set the 'To' field of a 'NodeInfo'
309+nodeInfoSetTo
310+ :: To k -> NodeInfo k v -> NodeInfo k v
311+nodeInfoSetTo to ni = ni { _nodeInfo_to = to }
312+
313+--
314+-- From
315+--
316+
317+-- | Describe how a key's new value should be produced
318+data From k p
319+ = From_Insert (PatchTarget p) -- ^ Insert the given value here
320+ | From_Delete -- ^ Delete the existing value, if any, from here
321+ | From_Move !k !p -- ^ Move the value here from the given key, and apply the given patch
322+
323+deriving instance (Show k, Show p, Show (PatchTarget p)) => Show (From k p)
324+deriving instance (Read k, Read p, Read (PatchTarget p)) => Read (From k p)
325+deriving instance (Eq k, Eq p, Eq (PatchTarget p)) => Eq (From k p)
326+deriving instance (Ord k, Ord p, Ord (PatchTarget p)) => Ord (From k p)
327+
328+-- | Traverse the 'From' over the key, patch, and patch target. Because of
329+-- the type families here, this doesn't it any bi- or tri-traversal class.
330+bitraverseFrom
331+ :: Applicative f
332+ => (k0 -> f k1)
333+ -> (p0 -> f p1)
334+ -> (PatchTarget p0 -> f (PatchTarget p1))
335+ -> From k0 p0 -> f (From k1 p1)
336+bitraverseFrom fk fp fpt = \case
337+ From_Insert pt -> From_Insert <$> fpt pt
338+ From_Delete -> pure From_Delete
339+ From_Move k p -> From_Move <$> fk k <*> fp p
340+
341+--
342+-- To
343+--
344+
345+-- | Describe where a key's old value will go. If this is 'Just', that means
346+-- the key's old value will be moved to the given other key; if it is 'Nothing',
347+-- that means it will be deleted.
348+type To = Maybe
349+
350+--
351+-- Fixup
352+--
353+
354+-- | Helper data structure used for composing patches using the monoid instance.
355+data Fixup k v
356+ = Fixup_Delete
357+ | Fixup_Update (These (From k v) (To k))
358+
359+-- | Compose patches having the same effect as applying the patches in turn:
360+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
361+instance ( Ord k
362+#if !MIN_VERSION_base(4,11,0)
363+ , Semigroup p
364+#endif
365+ , DecidablyEmpty p
366+ , Patch p
367+ ) => Semigroup (PatchMapWithPatchingMove k p) where
368+ PatchMapWithPatchingMove ma <> PatchMapWithPatchingMove mb = PatchMapWithPatchingMove m
369+ where
370+ connections = Map.toList $ Map.intersectionWithKey (\_ a b -> (_nodeInfo_to a, _nodeInfo_from b)) ma mb
371+ h :: (k, (Maybe k, From k p)) -> [(k, Fixup k p)]
372+ h (_, (mToAfter, editBefore)) = case (mToAfter, editBefore) of
373+ (Just toAfter, From_Move fromBefore p)
374+ | fromBefore == toAfter && isEmpty p
375+ -> [(toAfter, Fixup_Delete)]
376+ | otherwise
377+ -> [ (toAfter, Fixup_Update (This editBefore))
378+ , (fromBefore, Fixup_Update (That mToAfter))
379+ ]
380+ (Nothing, From_Move fromBefore _) -> [(fromBefore, Fixup_Update (That mToAfter))] -- The item is destroyed in the second patch, so indicate that it is destroyed in the source map
381+ (Just toAfter, _) -> [(toAfter, Fixup_Update (This editBefore))]
382+ (Nothing, _) -> []
383+ mergeFixups _ Fixup_Delete Fixup_Delete = Fixup_Delete
384+ mergeFixups _ (Fixup_Update a) (Fixup_Update b)
385+ | This x <- a, That y <- b
386+ = Fixup_Update $ These x y
387+ | That y <- a, This x <- b
388+ = Fixup_Update $ These x y
389+ mergeFixups _ _ _ = error "PatchMapWithPatchingMove: incompatible fixups"
390+ fixups = Map.fromListWithKey mergeFixups $ concatMap h connections
391+ combineNodeInfos _ nia nib = NodeInfo
392+ { _nodeInfo_from = _nodeInfo_from nia
393+ , _nodeInfo_to = _nodeInfo_to nib
394+ }
395+ applyFixup _ ni = \case
396+ Fixup_Delete -> Nothing
397+ Fixup_Update u -> Just $ NodeInfo
398+ { _nodeInfo_from = case _nodeInfo_from ni of
399+ f@(From_Move _ p') -> case getHere u of -- The `from` fixup comes from the "old" patch
400+ Nothing -> f -- If there's no `from` fixup, just use the "new" `from`
401+ Just (From_Insert v) -> From_Insert $ applyAlways p' v
402+ Just From_Delete -> From_Delete
403+ Just (From_Move oldKey p) -> From_Move oldKey $ p' <> p
404+ _ -> error "PatchMapWithPatchingMove: fixup for non-move From"
405+ , _nodeInfo_to = fromMaybe (_nodeInfo_to ni) $ getThere u
406+ }
407+ m = Map.differenceWithKey applyFixup (Map.unionWithKey combineNodeInfos ma mb) fixups
408+ getHere :: These a b -> Maybe a
409+ getHere = \case
410+ This a -> Just a
411+ These a _ -> Just a
412+ That _ -> Nothing
413+ getThere :: These a b -> Maybe b
414+ getThere = \case
415+ This _ -> Nothing
416+ These _ b -> Just b
417+ That b -> Just b
418+
419+--TODO: Figure out how to implement this in terms of PatchDMapWithPatchingMove rather than duplicating it here
420+-- | Compose patches having the same effect as applying the patches in turn:
421+-- @'applyAlways' (p <> q) == 'applyAlways' p . 'applyAlways' q@
422+instance ( Ord k
423+#if !MIN_VERSION_base(4,11,0)
424+ , Semigroup p
425+#endif
426+ , DecidablyEmpty p
427+ , Patch p
428+ ) => Monoid (PatchMapWithPatchingMove k p) where
429+ mempty = PatchMapWithPatchingMove mempty
430+ mappend = (<>)
431+
432+makeWrapped ''PatchMapWithPatchingMove
src/Data/Semigroup/Additive.hsnew file+54
@@ -0,0 +1,54 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeOperators #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- |
5+-- Module:
6+-- Data.Semigroup.Additive
7+-- Description:
8+-- This module defines a class for commutative semigroups, until it is moved
9+-- to another library.
10+module Data.Semigroup.Additive
11+ ( Additive
12+ ) where
13+
14+import Data.Functor.Const (Const (..))
15+import Data.Functor.Identity
16+import Data.Proxy
17+#if !MIN_VERSION_base(4,12,0)
18+-- for :*: and :.: semigroup instances
19+import Data.Orphans ()
20+#endif
21+#if !MIN_VERSION_base(4,11,0)
22+import Data.Semigroup (Semigroup (..))
23+#endif
24+import GHC.Generics
25+
26+-- | An 'Additive' 'Semigroup' is one where (<>) is commutative
27+class Semigroup q => Additive q where
28+
29+-- | Trivial additive semigroup.
30+instance Additive ()
31+
32+-- | Product additive semigroup.
33+-- A Pair of additive semigroups gives rise to a additive semigroup
34+instance (Additive a, Additive b) => Additive (a, b)
35+
36+-- See https://gitlab.haskell.org/ghc/ghc/issues/11135#note_111802 for the reason Compose is not also provided.
37+-- Base does not define Monoid (Compose f g a) so this is the best we can
38+-- really do for functor composition.
39+instance Additive (f (g a)) => Additive ((f :.: g) a)
40+
41+-- | Product of additive semigroups, Functor style.
42+instance (Additive (f a), Additive (g a)) => Additive ((f :*: g) a)
43+
44+-- | Trivial additive semigroup, Functor style
45+instance Additive (Proxy x)
46+
47+-- | Const lifts additive semigroups into a functor.
48+instance Additive a => Additive (Const a x)
49+
50+-- | Identity lifts additive semigroups pointwise (at only one point)
51+instance Additive a => Additive (Identity a)
52+
53+-- | Functions lift additive semigroups pointwise.
54+instance Additive b => Additive (a -> b)
src/Data/WeakBag.hsdeleted−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− ) where
20−
21−import Control.Exception
22−import Control.Monad hiding (forM_, mapM_)
23−import Control.Monad.IO.Class
24−import Data.Foldable (forM_, mapM_)
25−import Data.IntMap.Strict (IntMap)
26−import qualified Data.IntMap.Strict as IntMap
27−import Data.IORef
28−import System.Mem.Weak
29−
30−import Prelude hiding (mapM_, traverse)
31−
32−-- | A @WeakBag a@ holds a set of values of type @a@, but does not retain them -
33−-- that is, they can still be garbage-collected. As long as the @a@s remain
34−-- alive, the 'WeakBag' will continue to refer to them.
35−data WeakBag a = WeakBag
36− { _weakBag_nextId :: {-# UNPACK #-} !(IORef Int) --TODO: what if this wraps around?
37− , _weakBag_children :: {-# UNPACK #-} !(IORef (IntMap (Weak a)))
38− }
39−
40−-- | When inserting an item into a 'WeakBag', a 'WeakBagTicket' is returned. If
41−-- the caller retains the ticket, the item is guranteed to stay in memory (and
42−-- thus in the 'WeakBag'). The ticket can also be used to remove the item from
43−-- the 'WeakBag' prematurely (i.e. while it is still alive), using 'remove'.
44−data WeakBagTicket = forall a. WeakBagTicket
45− { _weakBagTicket_weakItem :: {-# UNPACK #-} !(Weak a)
46− , _weakBagTicket_item :: {-# NOUNPACK #-} !a
47− }
48−
49−-- | Insert an item into a 'WeakBag'.
50−{-# INLINE insert #-}
51−insert :: a -- ^ The item
52− -> WeakBag a -- ^ The 'WeakBag' to insert into
53− -> IORef (Weak b) -- ^ An arbitrary value to be used in the following
54− -- callback
55− -> (b -> IO ()) -- ^ A callback to be invoked when the item is removed
56− -- (whether automatically by the item being garbage
57− -- collected or manually via 'remove')
58− -> IO WeakBagTicket -- ^ Returns a 'WeakBagTicket' that ensures the item
59− -- is retained and allows the item to be removed.
60−insert a (WeakBag nextId children) wbRef finalizer = {-# SCC "insert" #-} do
61− a' <- evaluate a
62− wbRef' <- evaluate wbRef
63− myId <- atomicModifyIORef' nextId $ \n -> (succ n, n)
64− let cleanup = do
65− wb <- readIORef wbRef'
66− mb <- deRefWeak wb
67− forM_ mb $ \b -> do
68− csWithoutMe <- atomicModifyIORef children $ \cs ->
69− let !csWithoutMe = IntMap.delete myId cs
70− in (csWithoutMe, csWithoutMe)
71− when (IntMap.null csWithoutMe) $ finalizer b
72− return ()
73− return ()
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−12
@@ -1,12 +0,0 @@
1−-- | This module exports all of the commonly-used functionality of Reflex; if
2−-- you are just getting started with Reflex, this is probably what you want.
3−module Reflex (module X) where
4−
5−import Reflex.Class as X
6−import Reflex.Dynamic as X
7−import Reflex.Dynamic.TH as X
8−import Reflex.Dynamic.Uniq as X
9−import Reflex.DynamicWriter as X
10−import Reflex.PerformEvent.Class as X
11−import Reflex.PostBuild.Class as X
12−import Reflex.Spider as X
src/Reflex/Class.hsdeleted−910
@@ -1,910 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE RankNTypes #-}
10−{-# LANGUAGE RecursiveDo #-}
11−{-# LANGUAGE RoleAnnotations #-}
12−{-# LANGUAGE ScopedTypeVariables #-}
13−{-# LANGUAGE TypeFamilies #-}
14−{-# LANGUAGE TypeOperators #-}
15−{-# LANGUAGE UndecidableInstances #-}
16−#ifdef USE_REFLEX_OPTIMIZER
17−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
18−#endif
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−{-# LANGUAGE EmptyDataDecls #-}
21−{-# OPTIONS_GHC -Wno-redundant-constraints #-}
22−#endif
23−
24−-- | This module contains the Reflex interface, as well as a variety of
25−-- convenience functions for working with 'Event's, 'Behavior's, and other
26−-- signals.
27−module Reflex.Class
28− ( module Reflex.Patch
29− -- * Primitives
30− , Reflex (..)
31− , coerceBehavior
32− , coerceEvent
33− , coerceDynamic
34− , MonadSample (..)
35− , MonadHold (..)
36− -- ** 'fan'-related types
37− , EventSelector (..)
38− -- ** 'Incremental'-related types
39− -- * Convenience functions
40− , constDyn
41− , pushAlways
42− -- ** Combining 'Event's
43− , leftmost
44− , mergeMap
45− , mergeList
46− , mergeWith
47− , difference
48− -- ** Breaking up 'Event's
49− , splitE
50− , fanEither
51− , fanThese
52− , fanMap
53− , dmapToThese
54− , EitherTag (..)
55− , eitherToDSum
56− , dsumToEither
57− -- ** Switching between 'Event's
58− , switchPromptly
59− , switchPromptOnly
60− -- ** Using 'Event's to sample 'Behavior's
61− , tag
62− , attach
63− , attachWith
64− , attachWithMaybe
65− -- ** Blocking an 'Event' based on a 'Behavior'
66− , gate
67− -- ** Combining 'Dynamic's
68− , distributeDMapOverDynPure
69− , distributeListOverDyn
70− , distributeListOverDynWith
71− , zipDyn
72− , zipDynWith
73− -- ** Accumulating state
74− , Accumulator (..)
75− , mapAccum_
76− , mapAccumM_
77− , mapAccumMaybe_
78− , mapAccumMaybeM_
79− , zipListWithEvent
80− , headE
81− , tailE
82− , headTailE
83− , switcher
84− -- * Debugging functions
85− , traceEvent
86− , traceEventWith
87− -- * Unsafe functions
88− , unsafeDynamic
89− -- * 'FunctorMaybe'
90− , FunctorMaybe (..)
91− , fforMaybe
92− , ffilter
93− -- * Miscellaneous convenience functions
94− , ffor
95− , MonadAdjust (..)
96− -- * Deprecated functions
97− , appendEvents
98− , onceE
99− , sequenceThese
100−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
101− , Spider
102− , SpiderEnv
103− , SpiderTimeline
104− , Global
105− , Behavior (..)
106− , Event (..)
107− , Dynamic (..)
108− , Incremental (..)
109− , never
110− , constant
111− , push
112− , pull
113− , merge
114− , fan
115− , switch
116− , coincidence
117− , current
118− , updated
119− , unsafeBuildDynamic
120− , unsafeBuildIncremental
121− , mergeIncremental
122− , currentIncremental
123− , updatedIncremental
124− , incrementalToDynamic
125− , behaviorCoercion
126− , eventCoercion
127− , dynamicCoercion
128−#endif
129− ) where
130−
131−import Control.Applicative
132−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
133−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
134−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
135−import Control.Monad.Trans.Cont (ContT ())
136−import Control.Monad.Trans.Except (ExceptT ())
137−import Control.Monad.Trans.RWS (RWST ())
138−import Control.Monad.Trans.Writer (WriterT ())
139−import Data.Align
140−import Data.Bifunctor
141−import Data.Coerce
142−import Data.Default
143−import Data.Dependent.Map (DMap, DSum (..), GCompare (..))
144−import qualified Data.Dependent.Map as DMap
145−import Data.Either
146−import Data.Foldable
147−import Data.Functor.Bind hiding (join)
148−import qualified Data.Functor.Bind as Bind
149−import Data.Functor.Misc
150−import Data.Functor.Plus
151−import Data.List.NonEmpty (NonEmpty (..))
152−import Data.Map (Map)
153−import Data.Maybe
154−import Data.Monoid hiding (Alt, (<>))
155−import Data.Semigroup (Semigroup, sconcat, stimes, (<>))
156−import Data.String
157−import Data.These
158−import Data.Traversable
159−import Data.Type.Coercion
160−import Reflex.FunctorMaybe
161−import Reflex.Patch
162−
163−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
164−import Reflex.Spider.Internal (EventM, Global, HasSpiderTimeline, SpiderHost (..), SpiderHostFrame (..),
165− SpiderPullM, SpiderPushM (..), behaviorHoldIdentity, dynamicConst,
166− dynamicCurrent, dynamicDynIdentity, dynamicHold, dynamicHoldIdentity,
167− dynamicUpdated, newMapDyn, readBehaviorTracked, readBehaviorUntracked,
168− runFrame, runSpiderHostFrame, unsafeDyn)
169−import qualified Reflex.Spider.Internal
170−import qualified Reflex.Spider.Internal as S
171−import Unsafe.Coerce
172−#endif
173−
174−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
175−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
176−
177−import Debug.Trace (trace)
178−
179−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
180−#include "Spider/SpiderTimeline.include.hs"
181−#endif
182−
183−-- | The 'Reflex' class contains all the primitive functionality needed for
184−-- Functional Reactive Programming (FRP). The @t@ type parameter indicates
185−-- which "timeline" is in use. Timelines are fully-independent FRP contexts,
186−-- and the type of the timeline determines the FRP engine to be used. For most
187−-- purposes, the 'Reflex.Spider' implementation is recommended.
188−class ( MonadHold t (PushM t)
189− , MonadSample t (PullM t)
190− , MonadFix (PushM t)
191− , Functor (Dynamic t)
192− , Applicative (Dynamic t) -- Necessary for GHC <= 7.8
193− , Monad (Dynamic t)
194−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
195− , t ~ SpiderTimeline Global
196−#endif
197− ) => Reflex t where
198− -- | A container for a value that can change over time. 'Behavior's can be
199− -- sampled at will, but it is not possible to be notified when they change
200− data Behavior t :: * -> *
201− -- | A stream of occurrences. During any given frame, an 'Event' is either
202− -- occurring or not occurring; if it is occurring, it will contain a value of
203− -- the given type (its "occurrence type")
204− data Event t :: * -> *
205− -- | A container for a value that can change over time and allows
206− -- notifications on changes. Basically a combination of a 'Behavior' and an
207− -- 'Event', with a rule that the 'Behavior' will change if and only if the
208− -- 'Event' fires.
209− data Dynamic t :: * -> *
210− -- | A container for a value that can change over time and allows
211− -- notifications on changes. Basically a combination of a 'Behavior' and an
212− -- 'Event', with a rule that the 'Behavior' will change if and only if the
213− -- 'Event' fires.
214− data Incremental t :: * -> *
215− -- | A monad for doing complex push-based calculations efficiently
216− type PushM t :: * -> *
217− -- | A monad for doing complex pull-based calculations efficiently
218− type PullM t :: * -> *
219−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
220− -- | An 'Event' with no occurrences
221− never :: Event t a
222− -- | Create a 'Behavior' that always has the given value
223− 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
224− -- | Create an 'Event' from another 'Event'; the provided function can sample
225− -- 'Behavior's and hold 'Event's, and use the results to produce a occurring
226− -- (Just) or non-occurring (Nothing) result
227− push :: (a -> PushM t (Maybe b)) -> Event t a -> Event t b
228− -- | Create a 'Behavior' by reading from other 'Behavior's; the result will be
229− -- recomputed whenever any of the read 'Behavior's changes
230− pull :: PullM t a -> Behavior t a
231− -- | Merge a collection of events; the resulting 'Event' will only occur if at
232− -- least one input event is occuring, and will contain all of the input keys
233− -- that are occurring simultaneously
234− 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
235− -- | Efficiently fan-out an event to many destinations. This function should
236− -- be partially applied, and then the result applied repeatedly to create
237− -- child events
238− 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
239− -- | Create an 'Event' that will occur whenever the currently-selected input
240− -- 'Event' occurs
241− switch :: Behavior t (Event t a) -> Event t a
242− -- | Create an 'Event' that will occur whenever the input event is occurring
243− -- and its occurrence value, another 'Event', is also occurring
244− coincidence :: Event t (Event t a) -> Event t a
245− -- | Extract the 'Behavior' of a 'Dynamic'.
246− current :: Dynamic t a -> Behavior t a
247− -- | Extract the 'Event' of the 'Dynamic'.
248− updated :: Dynamic t a -> Event t a
249− -- | Create a new 'Dynamic'. The given 'PullM' must always return the most
250− -- recent firing of the given 'Event', if any.
251− unsafeBuildDynamic :: PullM t a -> Event t a -> Dynamic t a
252− -- | Create a new 'Incremental'. The given 'PullM''s value must always change
253− -- in the same way that the accumulated application of patches would change
254− -- that value.
255− unsafeBuildIncremental :: Patch p => PullM t (PatchTarget p) -> Event t p -> Incremental t p
256− -- | Create a merge whose parents can change over time
257− mergeIncremental :: GCompare k => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
258− -- | Extract the 'Behavior' component of an 'Incremental'
259− currentIncremental :: Patch p => Incremental t p -> Behavior t (PatchTarget p)
260− -- | Extract the 'Event' component of an 'Incremental'
261− updatedIncremental :: Patch p => Incremental t p -> Event t p
262− -- | Convert an 'Incremental' to a 'Dynamic'
263− incrementalToDynamic :: Patch p => Incremental t p -> Dynamic t (PatchTarget p)
264− -- | Construct a 'Coercion' for a 'Behavior' given an 'Coercion' for its
265− -- occurrence type
266− behaviorCoercion :: Coercion a b -> Coercion (Behavior t a) (Behavior t b)
267− -- | Construct a 'Coercion' for an 'Event' given an 'Coercion' for its
268− -- occurrence type
269− eventCoercion :: Coercion a b -> Coercion (Event t a) (Event t b)
270− -- | Construct a 'Coercion' for a 'Dynamic' given an 'Coercion' for its
271− -- occurrence type
272− dynamicCoercion :: Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
273−#endif
274−
275−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
276−instance t ~ SpiderTimeline Global => Reflex t where
277− newtype Behavior t a = SpiderBehavior { unSpiderBehavior :: Reflex.Spider.Internal.Behavior Global a }
278− newtype Event t a = SpiderEvent { unSpiderEvent :: Reflex.Spider.Internal.Event Global a }
279− newtype Dynamic t a = SpiderDynamic { unSpiderDynamic :: Reflex.Spider.Internal.Dynamic Global (Identity a) } -- deriving (Functor, Applicative, Monad)
280− newtype Incremental t p = SpiderIncremental { unSpiderIncremental :: Reflex.Spider.Internal.Dynamic Global p }
281− type PullM t = SpiderPullM Global
282− type PushM t = SpiderPushM Global
283−never :: Reflex t => Event t a
284−constant :: Reflex t => a -> Behavior t a
285−push :: Reflex t => (a -> PushM t (Maybe b)) -> Event t a -> Event t b
286−pull :: Reflex t => PullM t a -> Behavior t a
287−merge :: (Reflex t, GCompare k) => DMap k (Event t) -> Event t (DMap k Identity)
288−fan :: (Reflex t, GCompare k) => Event t (DMap k Identity) -> EventSelector t k
289−switch :: Reflex t => Behavior t (Event t a) -> Event t a
290−coincidence :: Reflex t => Event t (Event t a) -> Event t a
291−current :: Reflex t => Dynamic t a -> Behavior t a
292−updated :: Reflex t => Dynamic t a -> Event t a
293−unsafeBuildDynamic :: Reflex t => PullM t a -> Event t a -> Dynamic t a
294−unsafeBuildIncremental :: (Reflex t, Patch p) => PullM t (PatchTarget p) -> Event t p -> Incremental t p
295−mergeIncremental :: (Reflex t, GCompare k) => Incremental t (PatchDMap k (Event t)) -> Event t (DMap k Identity)
296−currentIncremental :: (Reflex t, Patch p) => Incremental t p -> Behavior t (PatchTarget p)
297−updatedIncremental :: (Reflex t, Patch p) => Incremental t p -> Event t p
298−incrementalToDynamic :: (Reflex t, Patch p) => Incremental t p -> Dynamic t (PatchTarget p)
299−behaviorCoercion :: Reflex t => Coercion a b -> Coercion (Behavior t a) (Behavior t b)
300−eventCoercion :: Reflex t => Coercion a b -> Coercion (Event t a) (Event t b)
301−dynamicCoercion :: Reflex t => Coercion a b -> Coercion (Dynamic t a) (Dynamic t b)
302−{-# INLINE never #-}
303−never = SpiderEvent S.eventNever
304−{-# INLINE constant #-}
305−constant = SpiderBehavior . S.behaviorConst
306−{-# INLINE push #-}
307−push f = SpiderEvent . S.push (coerce f) . unSpiderEvent
308−{-# INLINE pull #-}
309−pull = SpiderBehavior . S.pull . coerce
310−{-# INLINE merge #-}
311−merge = SpiderEvent . S.merge . S.dynamicConst . (coerce :: DMap k (Event (SpiderTimeline x)) -> DMap k (S.Event x))
312−{-# INLINE fan #-}
313−fan e = EventSelector $ SpiderEvent . S.select (S.fan (unSpiderEvent e))
314−{-# INLINE switch #-}
315−switch = SpiderEvent . S.switch . (coerce :: S.Behavior x (Event (SpiderTimeline x) a) -> S.Behavior x (S.Event x a)) . unSpiderBehavior
316−{-# INLINE coincidence #-}
317−coincidence = SpiderEvent . S.coincidence . (coerce :: S.Event x (Event (SpiderTimeline x) a) -> S.Event x (S.Event x a)) . unSpiderEvent
318−{-# INLINE current #-}
319−current = SpiderBehavior . S.dynamicCurrent . unSpiderDynamic
320−{-# INLINE updated #-}
321−updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
322−{-# INLINE unsafeBuildDynamic #-}
323−unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
324−{-# INLINE unsafeBuildIncremental #-}
325−unsafeBuildIncremental readV0 dv = SpiderIncremental $ S.dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
326−{-# INLINE mergeIncremental #-}
327−mergeIncremental = SpiderEvent . S.merge . (unsafeCoerce :: S.Dynamic x (PatchDMap k (Event (SpiderTimeline x))) -> S.Dynamic x (PatchDMap k (S.Event x))) . unSpiderIncremental
328−{-# INLINE currentIncremental #-}
329−currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
330−{-# INLINE updatedIncremental #-}
331−updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
332−{-# INLINE incrementalToDynamic #-}
333−incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip S.push (dynamicUpdated i) $ \p -> do
334− c <- readBehaviorUntracked $ dynamicCurrent i
335− return $ Identity <$> apply p c
336−eventCoercion Coercion = Coercion
337−behaviorCoercion Coercion = Coercion
338−dynamicCoercion = unsafeCoerce
339−#endif
340−
341−-- | Coerce a 'Behavior' between representationally-equivalent value types
342−coerceBehavior :: (Reflex t, Coercible a b) => Behavior t a -> Behavior t b
343−coerceBehavior = coerceWith $ behaviorCoercion Coercion
344−
345−-- | Coerce an 'Event' between representationally-equivalent occurrence types
346−coerceEvent :: (Reflex t, Coercible a b) => Event t a -> Event t b
347−coerceEvent = coerceWith $ eventCoercion Coercion
348−
349−-- | Coerce a 'Dynamic' between representationally-equivalent value types
350−coerceDynamic :: (Reflex t, Coercible a b) => Dynamic t a -> Dynamic t b
351−coerceDynamic = coerceWith $ dynamicCoercion Coercion
352−
353−-- | Construct a 'Dynamic' from a 'Behavior' and an 'Event'. The 'Behavior'
354−-- _must_ change when and only when the 'Event' fires, such that the
355−-- 'Behavior''s value is always equal to the most recent firing of the 'Event';
356−-- if this is not the case, the resulting 'Dynamic' will behave
357−-- nondeterministically.
358−unsafeDynamic :: Reflex t => Behavior t a -> Event t a -> Dynamic t a
359−unsafeDynamic = unsafeBuildDynamic . sample
360−
361−-- | Construct a 'Dynamic' value that never changes
362−constDyn :: Reflex t => a -> Dynamic t a
363−constDyn = pure
364−
365−instance (Reflex t, Default a) => Default (Dynamic t a) where
366− def = pure def
367−
368−-- | 'MonadSample' designates monads that can read the current value of a
369−-- 'Behavior'. This includes both 'PullM' and 'PushM'.
370−class (Applicative m, Monad m) => MonadSample t m | m -> t where
371− -- | Get the current value in the 'Behavior'
372− sample :: Behavior t a -> m a
373−
374−-- | 'MonadHold' designates monads that can create new 'Behavior's based on
375−-- 'Event's; usually this will be 'PushM' or a monad based on it. 'MonadHold'
376−-- is required to create any stateful computations with Reflex.
377−class MonadSample t m => MonadHold t m where
378− -- | Create a new 'Behavior' whose value will initially be equal to the given
379− -- value and will be updated whenever the given 'Event' occurs. The update
380− -- takes effect immediately after the 'Event' occurs; if the occurrence that
381− -- sets the 'Behavior' (or one that is simultaneous with it) is used to sample
382− -- the 'Behavior', it will see the _old_ value of the 'Behavior', not the new
383− -- one.
384− hold :: a -> Event t a -> m (Behavior t a)
385− -- | Create a 'Dynamic' value using the given initial value that changes every
386− -- time the 'Event' occurs.
387− holdDyn :: a -> Event t a -> m (Dynamic t a)
388− -- | Create an 'Incremental' value using the given initial value that changes
389− -- every time the 'Event' occurs.
390− holdIncremental :: Patch p => PatchTarget p -> Event t p -> m (Incremental t p)
391−
392−-- | An 'EventSelector' allows you to efficiently 'select' an 'Event' based on a
393−-- key. This is much more efficient than filtering for each key with
394−-- 'fmapMaybe'.
395−newtype EventSelector t k = EventSelector
396− { -- | Retrieve the 'Event' for the given key. The type of the 'Event' is
397− -- determined by the type of the key, so this can be used to fan-out
398− -- 'Event's whose sub-'Event's have different types.
399− --
400− -- Using 'EventSelector's and the 'fan' primitive is far more efficient than
401− -- (but equivalent to) using 'fmapMaybe' to select only the relevant
402− -- occurrences of an 'Event'.
403− select :: forall a. k a -> Event t a
404− }
405−
406−--------------------------------------------------------------------------------
407−-- Instances
408−--------------------------------------------------------------------------------
409−
410−instance MonadSample t m => MonadSample t (ReaderT r m) where
411− sample = lift . sample
412−
413−instance MonadHold t m => MonadHold t (ReaderT r m) where
414− hold a0 = lift . hold a0
415− holdDyn a0 = lift . holdDyn a0
416− holdIncremental a0 = lift . holdIncremental a0
417−
418−instance (MonadSample t m, Monoid r) => MonadSample t (WriterT r m) where
419− sample = lift . sample
420−
421−instance (MonadHold t m, Monoid r) => MonadHold t (WriterT r m) where
422− hold a0 = lift . hold a0
423− holdDyn a0 = lift . holdDyn a0
424− holdIncremental a0 = lift . holdIncremental a0
425−
426−instance MonadSample t m => MonadSample t (StateT s m) where
427− sample = lift . sample
428−
429−instance MonadHold t m => MonadHold t (StateT s m) where
430− hold a0 = lift . hold a0
431− holdDyn a0 = lift . holdDyn a0
432− holdIncremental a0 = lift . holdIncremental a0
433−
434−instance MonadSample t m => MonadSample t (ExceptT e m) where
435− sample = lift . sample
436−
437−instance MonadHold t m => MonadHold t (ExceptT e m) where
438− hold a0 = lift . hold a0
439− holdDyn a0 = lift . holdDyn a0
440− holdIncremental a0 = lift . holdIncremental a0
441−
442−instance (MonadSample t m, Monoid w) => MonadSample t (RWST r w s m) where
443− sample = lift . sample
444−
445−instance (MonadHold t m, Monoid w) => MonadHold t (RWST r w s m) where
446− hold a0 = lift . hold a0
447− holdDyn a0 = lift . holdDyn a0
448− holdIncremental a0 = lift . holdIncremental a0
449−
450−instance MonadSample t m => MonadSample t (ContT r m) where
451− sample = lift . sample
452−
453−instance MonadHold t m => MonadHold t (ContT r m) where
454− hold a0 = lift . hold a0
455− holdDyn a0 = lift . holdDyn a0
456− holdIncremental a0 = lift . holdIncremental a0
457−
458−--------------------------------------------------------------------------------
459−-- Convenience functions
460−--------------------------------------------------------------------------------
461−
462−-- | Create an 'Event' from another 'Event'. The provided function can sample
463−-- 'Behavior's and hold 'Event's.
464−pushAlways :: Reflex t => (a -> PushM t b) -> Event t a -> Event t b
465−pushAlways f = push (fmap Just . f)
466−
467−-- | Flipped version of 'fmap'.
468−ffor :: Functor f => f a -> (a -> b) -> f b
469−ffor = flip fmap
470−
471−instance Reflex t => Applicative (Behavior t) where
472− pure = constant
473− f <*> x = pull $ sample f `ap` sample x
474− _ *> b = b
475− a <* _ = a
476−
477−instance Reflex t => Apply (Behavior t) where
478− (<.>) = (<*>)
479−
480−instance Reflex t => Bind (Behavior t) where
481− (>>-) = (>>=)
482−
483−instance (Reflex t, Fractional a) => Fractional (Behavior t a) where
484− (/) = liftA2 (/)
485− fromRational = pure . fromRational
486− recip = fmap recip
487−
488−instance Reflex t => Functor (Behavior t) where
489− fmap f = pull . fmap f . sample
490−
491−instance (Reflex t, IsString a) => IsString (Behavior t a) where
492− fromString = pure . fromString
493−
494−instance Reflex t => Monad (Behavior t) where
495− a >>= f = pull $ sample a >>= sample . f
496− -- 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.
497− return = constant
498− fail = error "Monad (Behavior t) does not support fail"
499−
500−instance (Reflex t, Monoid a) => Monoid (Behavior t a) where
501− mempty = constant mempty
502− mappend a b = pull $ liftM2 mappend (sample a) (sample b)
503− mconcat = pull . fmap mconcat . mapM sample
504−
505−instance (Reflex t, Num a) => Num (Behavior t a) where
506− (+) = liftA2 (+)
507− (-) = liftA2 (-)
508− (*) = liftA2 (*)
509− abs = fmap abs
510− fromInteger = pure . fromInteger
511− negate = fmap negate
512− signum = fmap signum
513−
514−instance (Reflex t, Semigroup a) => Semigroup (Behavior t a) where
515− a <> b = pull $ liftM2 (<>) (sample a) (sample b)
516− sconcat = pull . fmap sconcat . mapM sample
517−#if MIN_VERSION_semigroups(0,17,0)
518− stimes n = fmap $ stimes n
519−#else
520− times1p n = fmap $ times1p n
521−#endif
522−
523−-- | Flipped version of 'fmapMaybe'.
524−fforMaybe :: FunctorMaybe f => f a -> (a -> Maybe b) -> f b
525−fforMaybe = flip fmapMaybe
526−
527−-- | Filter 'f a' using the provided predicate.
528−-- Relies on 'fforMaybe'.
529−ffilter :: FunctorMaybe f => (a -> Bool) -> f a -> f a
530−ffilter f = fmapMaybe $ \x -> if f x then Just x else Nothing
531−
532−-- | Left-biased event union (prefers left event on simultaneous
533−-- occurrence).
534−instance Reflex t => Alt (Event t) where
535− ev1 <!> ev2 = leftmost [ev1, ev2]
536−
537−-- | 'Event' intersection (convenient interface to 'coincidence').
538−instance Reflex t => Apply (Event t) where
539− evf <.> evx = coincidence (fmap (<$> evx) evf)
540−
541−-- | 'Event' intersection (convenient interface to 'coincidence').
542−instance Reflex t => Bind (Event t) where
543− evx >>- f = coincidence (f <$> evx)
544− join = coincidence
545−
546−instance Reflex t => Functor (Event t) where
547− {-# INLINE fmap #-}
548− fmap f = fmapMaybe $ Just . f
549−
550−instance Reflex t => FunctorMaybe (Event t) where
551− {-# INLINE fmapMaybe #-}
552− fmapMaybe f = push $ return . f
553−
554−-- | Never: @'zero' = 'never'@.
555−instance Reflex t => Plus (Event t) where
556− zero = never
557−
558−-- | Replace each occurrence value of the 'Event' with the value of the
559−-- 'Behavior' at the time of that occurrence.
560−tag :: Reflex t => Behavior t b -> Event t a -> Event t b
561−tag b = pushAlways $ \_ -> sample b
562−
563−-- | Create a new 'Event' that combines occurences of supplied 'Event' with the
564−-- current value of the 'Behavior'.
565−attach :: Reflex t => Behavior t a -> Event t b -> Event t (a, b)
566−attach = attachWith (,)
567−
568−-- | Create a new 'Event' that occurs when the supplied 'Event' occurs by
569−-- combining it with the current value of the 'Behavior'.
570−attachWith :: Reflex t => (a -> b -> c) -> Behavior t a -> Event t b -> Event t c
571−attachWith f = attachWithMaybe $ \a b -> Just $ f a b
572−
573−-- | Create a new 'Event' by combining each occurence with the current value of
574−-- the 'Behavior'. The occurrence is discarded if the combining function returns
575−-- Nothing
576−attachWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Behavior t a -> Event t b -> Event t c
577−attachWithMaybe f b e = flip push e $ \o -> (`f` o) <$> sample b
578−
579−-- | Create a new 'Event' that only occurs only once, on the first occurence of
580−-- the supplied 'Event'.
581−headE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
582−headE e = do
583− rec be <- hold e $ fmap (const never) e'
584− let e' = switch be
585− return e'
586−
587−-- | Create a new 'Event' that occurs on all but the first occurence of the
588−-- supplied 'Event'.
589−tailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
590−tailE e = snd <$> headTailE e
591−
592−-- | Create a tuple of two 'Event's with the first one occuring only the first
593−-- time the supplied 'Event' occurs and the second occuring on all but the first
594−-- occurence.
595−headTailE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a, Event t a)
596−headTailE e = do
597− eHead <- headE e
598− be <- hold never $ fmap (const e) eHead
599− return (eHead, switch be)
600−
601−-- | Split the supplied 'Event' into two individual 'Event's occuring at the
602−-- same time with the respective values from the tuple.
603−splitE :: Reflex t => Event t (a, b) -> (Event t a, Event t b)
604−splitE e = (fmap fst e, fmap snd e)
605−
606−-- | Print the supplied 'String' and the value of the 'Event' on each
607−-- occurence. This should /only/ be used for debugging.
608−--
609−-- Note: As with Debug.Trace.trace, the message will only be printed if the
610−-- 'Event' is actually used.
611−traceEvent :: (Reflex t, Show a) => String -> Event t a -> Event t a
612−traceEvent s = traceEventWith $ \x -> s <> ": " <> show x
613−
614−-- | Print the output of the supplied function on each occurence of the
615−-- 'Event'. This should /only/ be used for debugging.
616−--
617−-- Note: As with Debug.Trace.trace, the message will only be printed if the
618−-- 'Event' is actually used.
619−traceEventWith :: Reflex t => (a -> String) -> Event t a -> Event t a
620−traceEventWith f = push $ \x -> trace (f x) $ return $ Just x
621−
622−instance (Semigroup a, Reflex t) => Semigroup (Event t a) where
623− (<>) = alignWith (mergeThese (<>))
624− sconcat = fmap sconcat . mergeList . toList
625−#if MIN_VERSION_semigroups(0,17,0)
626− stimes n = fmap $ stimes n
627−#else
628− times1p n = fmap $ times1p n
629−#endif
630−
631−instance (Semigroup a, Reflex t) => Monoid (Event t a) where
632− mempty = never
633− mappend = (<>)
634− mconcat = fmap sconcat . mergeList
635−
636−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
637−-- list occurs. If multiple occur at the same time they are folded from the left
638−-- with the given function.
639−{-# INLINE mergeWith #-}
640−mergeWith :: Reflex t => (a -> a -> a) -> [Event t a] -> Event t a
641−mergeWith f es = fmap (Prelude.foldl1 f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList)
642− . merge
643− . DMap.fromDistinctAscList
644− . map (\(k, v) -> Const2 k :=> v)
645− $ zip [0 :: Int ..] es
646−
647−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
648−-- list occurs. If multiple occur at the same time the value is the value of the
649−-- leftmost event.
650−{-# INLINE leftmost #-}
651−leftmost :: Reflex t => [Event t a] -> Event t a
652−leftmost = mergeWith const
653−
654−-- | Create a new 'Event' that occurs if at least one of the 'Event's in the
655−-- list occurs and has a list of the values of all 'Event's occuring at that
656−-- time.
657−mergeList :: Reflex t => [Event t a] -> Event t (NonEmpty a)
658−mergeList [] = never
659−mergeList es = mergeWith (<>) $ map (fmap (:|[])) es
660−
661−-- | Create a new 'Event' combining the map of 'Event's into an 'Event' that
662−-- occurs if at least one of them occurs and has a map of values of all 'Event's
663−-- occuring at that time.
664−mergeMap :: (Reflex t, Ord k) => Map k (Event t a) -> Event t (Map k a)
665−mergeMap = fmap dmapToMap . merge . mapWithFunctorToDMap
666−
667−-- | Split the event into separate events for 'Left' and 'Right' values.
668−fanEither :: Reflex t => Event t (Either a b) -> (Event t a, Event t b)
669−fanEither e =
670− let justLeft = either Just (const Nothing)
671− justRight = either (const Nothing) Just
672− in (fmapMaybe justLeft e, fmapMaybe justRight e)
673−
674−-- | Split the event into separate events for 'This' and 'That' values,
675−-- allowing them to fire simultaneously when the input value is 'These'.
676−fanThese :: Reflex t => Event t (These a b) -> (Event t a, Event t b)
677−fanThese e =
678− let this (This x) = Just x
679− this (These x _) = Just x
680− this _ = Nothing
681− that (That y) = Just y
682− that (These _ y) = Just y
683− that _ = Nothing
684− in (fmapMaybe this e, fmapMaybe that e)
685−
686−-- | Split the event into an 'EventSelector' that allows efficient selection of
687−-- the individual 'Event's.
688−fanMap :: (Reflex t, Ord k) => Event t (Map k a) -> EventSelector t (Const2 k a)
689−fanMap = fan . fmap mapToDMap
690−
691−-- | Switches to the new event whenever it receives one. Whenever a new event
692−-- is provided, if it is firing, its value will be the resulting event's value;
693−-- if it is not firing, but the old one is, the old one's value will be used.
694−switchPromptly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
695−switchPromptly ea0 eea = do
696− bea <- hold ea0 eea
697− let eLag = switch bea
698− eCoincidences = coincidence eea
699− return $ leftmost [eCoincidences, eLag]
700−
701−switchPromptOnly :: (Reflex t, MonadHold t m) => Event t a -> Event t (Event t a) -> m (Event t a)
702−switchPromptOnly e0 e' = do
703− eLag <- switch <$> hold e0 e'
704− return $ coincidence $ leftmost [e', eLag <$ eLag]
705−
706−instance Reflex t => Align (Event t) where
707− nil = never
708− align = alignWithMaybe Just
709−
710−-- | Create a new 'Event' that only occurs if the supplied 'Event' occurs and
711−-- the 'Behavior' is true at the time of occurence.
712−gate :: Reflex t => Behavior t Bool -> Event t a -> Event t a
713−gate = attachWithMaybe $ \allow a -> if allow then Just a else Nothing
714−
715−-- | Create a new behavior given a starting behavior and switch to a the behvior
716−-- carried by the event when it fires.
717−switcher :: (Reflex t, MonadHold t m)
718− => Behavior t a -> Event t (Behavior t a) -> m (Behavior t a)
719−switcher b eb = pull . (sample <=< sample) <$> hold b eb
720−
721−-- | Combine two 'Dynamic's. The result will change whenever either (or both)
722−-- input 'Dynamic' changes. Equivalent to @zipDynWith (,)@.
723−zipDyn :: Reflex t => Dynamic t a -> Dynamic t b -> Dynamic t (a, b)
724−zipDyn = zipDynWith (,)
725−
726−-- | Combine two 'Dynamic's with a combining function. The result will change
727−-- whenever either (or both) input 'Dynamic' changes.
728−-- More efficient than liftA2.
729−zipDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> Dynamic t c
730−zipDynWith f da db =
731− let eab = align (updated da) (updated db)
732− ec = flip push eab $ \o -> do
733− (a, b) <- case o of
734− This a -> do
735− b <- sample $ current db
736− return (a, b)
737− That b -> do
738− a <- sample $ current da
739− return (a, b)
740− These a b -> return (a, b)
741− return $ Just $ f a b
742− in unsafeBuildDynamic (f <$> sample (current da) <*> sample (current db)) ec
743−
744−instance (Reflex t, Monoid a) => Monoid (Dynamic t a) where
745− mconcat = distributeListOverDynWith mconcat
746− mempty = constDyn mempty
747− mappend = zipDynWith mappend
748−
749−-- | This function converts a 'DMap' whose elements are 'Dynamic's into a
750−-- 'Dynamic' 'DMap'. Its implementation is more efficient than doing the same
751−-- through the use of multiple uses of 'zipWithDyn' or 'Applicative' operators.
752−distributeDMapOverDynPure :: forall t k. (Reflex t, GCompare k) => DMap k (Dynamic t) -> Dynamic t (DMap k Identity)
753−distributeDMapOverDynPure dm = case DMap.toList dm of
754− [] -> constDyn DMap.empty
755− [k :=> v] -> fmap (DMap.singleton k . Identity) v
756− _ ->
757− let getInitial = DMap.traverseWithKey (\_ -> fmap Identity . sample . current) dm
758− edmPre = merge $ DMap.map updated dm
759− result = unsafeBuildDynamic getInitial $ flip pushAlways edmPre $ \news -> do
760− olds <- sample $ current result
761− return $ DMap.unionWithKey (\_ _ new -> new) olds news
762− in result
763−
764−-- | Convert a list of 'Dynamic's into a 'Dynamic' list.
765−distributeListOverDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
766−distributeListOverDyn = distributeListOverDynWith id
767−
768−-- | Create a new 'Dynamic' by applying a combining function to a list of 'Dynamic's
769−distributeListOverDynWith :: Reflex t => ([a] -> b) -> [Dynamic t a] -> Dynamic t b
770−distributeListOverDynWith f = fmap (f . map (\(Const2 _ :=> Identity v) -> v) . DMap.toList) . distributeDMapOverDynPure . DMap.fromList . map (\(k, v) -> Const2 k :=> v) . zip [0 :: Int ..]
771−
772−-- | Create a new 'Event' that occurs when the first supplied 'Event' occurs
773−-- unless the second supplied 'Event' occurs simultaneously.
774−difference :: Reflex t => Event t a -> Event t b -> Event t a
775−difference = alignWithMaybe $ \case
776− This a -> Just a
777− _ -> Nothing
778−
779−-- (intentially not exported, for now)
780−alignWithMaybe
781− :: Reflex t => (These a b -> Maybe c) -> Event t a -> Event t b -> Event t c
782−alignWithMaybe f ea eb =
783− fmapMaybe (f <=< dmapToThese)
784− $ merge
785− $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
786−
787−
788−--------------------------------------------------------------------------------
789−-- Accumulator
790−--------------------------------------------------------------------------------
791−
792−-- | An 'Accumulator' type can be built by accumulating occurrences of an
793−-- 'Event'.
794−class Reflex t => Accumulator t f | f -> t where
795− accum :: (MonadHold t m, MonadFix m) => (a -> b -> a) -> a -> Event t b -> m (f a)
796− accum f = accumMaybe $ \v o -> Just $ f v o
797− accumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t a) -> a -> Event t b -> m (f a)
798− accumM f = accumMaybeM $ \v o -> Just <$> f v o
799− accumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> Maybe a) -> a -> Event t b -> m (f a)
800− accumMaybe f = accumMaybeM $ \v o -> return $ f v o
801− accumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a)) -> a -> Event t b -> m (f a)
802− mapAccum :: (MonadHold t m, MonadFix m) => (a -> b -> (a, c)) -> a -> Event t b -> m (f a, Event t c)
803− mapAccum f = mapAccumMaybe $ \v o -> bimap Just Just $ f v o
804− mapAccumM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (a, c)) -> a -> Event t b -> m (f a, Event t c)
805− mapAccumM f = mapAccumMaybeM $ \v o -> bimap Just Just <$> f v o
806− mapAccumMaybe :: (MonadHold t m, MonadFix m) => (a -> b -> (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
807− mapAccumMaybe f = mapAccumMaybeM $ \v o -> return $ f v o
808− mapAccumMaybeM :: (MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe a, Maybe c)) -> a -> Event t b -> m (f a, Event t c)
809−
810−-- | Accumulate occurrences of an 'Event', producing an output occurrence each
811−-- time. Discard the underlying 'Accumulator'.
812−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)
813−mapAccum_ f z e = do
814− (_ :: Behavior t a, result) <- mapAccum f z e
815− return result
816−
817−-- | Accumulate occurrences of an 'Event', possibly producing an output
818−-- occurrence each time. Discard the underlying 'Accumulator'.
819−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)
820−mapAccumMaybe_ f z e = do
821− (_ :: Behavior t a, result) <- mapAccumMaybe f z e
822− return result
823−
824−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and producing
825−-- an output occurrence each time. Discard the underlying 'Accumulator'.
826−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)
827−mapAccumM_ f z e = do
828− (_ :: Behavior t a, result) <- mapAccumM f z e
829− return result
830−
831−-- | Accumulate occurrences of an 'Event', using a 'PushM' action and possibly
832−-- producing an output occurrence each time. Discard the underlying
833−-- 'Accumulator'.
834−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)
835−mapAccumMaybeM_ f z e = do
836− (_ :: Behavior t a, result) <- mapAccumMaybeM f z e
837− return result
838−
839−instance Reflex t => Accumulator t (Dynamic t) where
840− accumMaybeM f z e = do
841− rec let e' = flip push e $ \o -> do
842− v <- sample $ current d'
843− f v o
844− d' <- holdDyn z e'
845− return d'
846− mapAccumMaybeM f z e = do
847− rec let e' = flip push e $ \o -> do
848− v <- sample $ current d'
849− result <- f v o
850− return $ case result of
851− (Nothing, Nothing) -> Nothing
852− _ -> Just result
853− d' <- holdDyn z $ fmapMaybe fst e'
854− return (d', fmapMaybe snd e')
855−
856−instance Reflex t => Accumulator t (Behavior t) where
857− accumMaybeM f z e = current <$> accumMaybeM f z e
858− mapAccumMaybeM f z e = first current <$> mapAccumMaybeM f z e
859−
860−instance Reflex t => Accumulator t (Event t) where
861− accumMaybeM f z e = updated <$> accumMaybeM f z e
862− mapAccumMaybeM f z e = first updated <$> mapAccumMaybeM f z e
863−
864−-- | Create a new 'Event' by combining each occurence with the next value of the
865−-- list using the supplied function. If the list runs out of items, all
866−-- subsequent 'Event' occurrences will be ignored.
867−zipListWithEvent :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> c) -> [a] -> Event t b -> m (Event t c)
868−zipListWithEvent f l e = do
869− let f' a b = case a of
870− h:t -> (Just t, Just $ f h b)
871− _ -> (Nothing, Nothing) --TODO: Unsubscribe the event?
872− mapAccumMaybe_ f' l e
873−
874−class (Reflex t, Monad m) => MonadAdjust t m | m -> t where
875− runWithReplace :: m a -> Event t (m b) -> m (a, Event t b)
876− sequenceDMapWithAdjust :: GCompare k => DMap k m -> Event t (PatchDMap k m) -> m (DMap k Identity, Event t (PatchDMap k Identity))
877−
878−instance (Reflex t, MonadAdjust t m) => MonadAdjust t (ReaderT r m) where
879− runWithReplace a0 a' = do
880− r <- ask
881− lift $ runWithReplace (runReaderT a0 r) $ fmap (`runReaderT` r) a'
882− sequenceDMapWithAdjust dm0 dm' = do
883− r <- ask
884− let loweredDm0 = DMap.map (`runReaderT` r) dm0
885− loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
886− DMap.map (\(ComposeMaybe mv) -> ComposeMaybe $ fmap (`runReaderT` r) mv) p
887− lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
888−
889−--------------------------------------------------------------------------------
890−-- Deprecated functions
891−--------------------------------------------------------------------------------
892−
893−-- | Create a new 'Event' that occurs if at least one of the supplied 'Event's
894−-- occurs. If both occur at the same time they are combined using 'mappend'.
895−{-# DEPRECATED appendEvents "If a 'Semigroup a' instance is available, use 'mappend'; otherwise, use 'alignWith (mergeThese mappend)' instead" #-}
896−appendEvents :: (Reflex t, Monoid a) => Event t a -> Event t a -> Event t a
897−appendEvents = alignWith $ mergeThese mappend
898−
899−-- | Alias for 'headE'
900−{-# DEPRECATED onceE "Use 'headE' instead" #-}
901−onceE :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> m (Event t a)
902−onceE = headE
903−
904−{-# DEPRECATED sequenceThese "Use bisequenceA or bisequence from the bifunctors package instead" #-}
905−{-# ANN sequenceThese "HLint: ignore Use fmap" #-}
906−sequenceThese :: Monad m => These (m a) (m b) -> m (These a b)
907−sequenceThese t = case t of
908− This ma -> liftM This ma
909− These ma mb -> liftM2 These ma mb
910− That mb -> liftM That mb
src/Reflex/Dynamic.hsdeleted−575
@@ -1,575 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE NoMonomorphismRestriction #-}
9−{-# LANGUAGE PolyKinds #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−#ifdef USE_REFLEX_OPTIMIZER
16−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
17−#endif
18−-- | This module contains various functions for working with 'Dynamic' values.
19−-- 'Dynamic' and its primitives have been moved to the 'Reflex' class.
20−module Reflex.Dynamic
21− ( -- * Basics
22− Dynamic -- Abstract so we can preserve the law that the current value is always equal to the most recent update
23− , current
24− , updated
25− , holdDyn
26− , constDyn
27− , uniqDyn
28− , uniqDynBy
29− , count
30− , toggle
31− , switchPromptlyDyn
32− , tagPromptlyDyn
33− , attachPromptlyDyn
34− , attachPromptlyDynWith
35− , attachPromptlyDynWithMaybe
36− , foldDyn
37− , foldDynM
38− , foldDynMaybe
39− , foldDynMaybeM
40− , joinDynThroughMap
41− , traceDyn
42− , traceDynWith
43− , splitDynPure
44− , distributeMapOverDynPure
45− , distributeDMapOverDynPure
46− , distributeListOverDynPure
47− , Demux
48− , demux
49− , demuxed
50− -- Things that probably aren't very useful:
51− , HList (..)
52− , FHList (..)
53− , collectDynPure
54− , distributeFHListOverDynPure
55− -- Unsafe
56− , unsafeDynamic
57− -- * Deprecated functions
58− , apDyn
59− , attachDyn
60− , attachDynWith
61− , attachDynWithMaybe
62− , collectDyn
63− , combineDyn
64− , distributeDMapOverDyn
65− , distributeFHListOverDyn
66− , forDyn
67− , forDynM
68− , getDemuxed
69− , joinDyn
70− , mapDyn
71− , mapDynM
72− , mconcatDyn
73− , nubDyn
74− , splitDyn
75− , tagDyn
76− ) where
77−
78−import Prelude hiding (mapM, mapM_)
79−
80−import Data.Functor.Misc
81−import Reflex.Class
82−
83−import Control.Applicative ((<*>))
84−import Control.Monad hiding (forM, forM_, mapM, mapM_)
85−import Control.Monad.Fix
86−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_)
87−import Data.Align
88−import Data.Dependent.Map (DMap)
89−import qualified Data.Dependent.Map as DMap
90−import Data.Dependent.Sum (DSum (..))
91−import Data.GADT.Compare ((:~:) (..), GCompare (..), GEq (..), GOrdering (..))
92−import Data.Map (Map)
93−import Data.Monoid
94−import Data.These
95−
96−import Debug.Trace
97−
98−-- | Create a new 'Dynamic' that only signals changes if the values actually
99−-- changed.
100−uniqDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
101−uniqDyn = uniqDynBy (==)
102−
103−-- | Create a new 'Dynamic' that changes only when the underlying 'Dynamic'
104−-- changes and the given function returns 'False' when given both the old and
105−-- the new values.
106−uniqDynBy :: Reflex t => (a -> a -> Bool) -> Dynamic t a -> Dynamic t a
107−uniqDynBy eq d =
108− let e' = attachWithMaybe (\x x' -> if x' `eq` x then Nothing else Just x') (current d) (updated d)
109− in unsafeDynamic (current d) e'
110−
111−-- | Create a 'Dynamic' using the initial value and change it each time the
112−-- 'Event' occurs using a folding function on the previous value and the value
113−-- of the 'Event'.
114−foldDyn :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> b) -> b -> Event t a -> m (Dynamic t b)
115−foldDyn = accum . flip
116−
117−-- | Like 'foldDyn', but the combining function is a 'PushM' action, so it
118−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
119−foldDynM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t b) -> b -> Event t a -> m (Dynamic t b)
120−foldDynM = accumM . flip
121−
122−-- | Create a 'Dynamic' using the provided initial value and change it each time
123−-- the provided 'Event' occurs, using a function to combine the old value with
124−-- the 'Event''s value. If the function returns 'Nothing', the value is not
125−-- changed; this is distinct from returning 'Just' the old value, since the
126−-- 'Dynamic''s 'updated' 'Event' will fire in the 'Just' case, and will not fire
127−-- in the 'Nothing' case.
128−foldDynMaybe :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> Maybe b) -> b -> Event t a -> m (Dynamic t b)
129−foldDynMaybe = accumMaybe . flip
130−
131−-- | Like 'foldDynMaybe', but the combining function is a 'PushM' action, so it
132−-- can 'sample' existing 'Behaviors' and 'hold' new ones.
133−foldDynMaybeM :: (Reflex t, MonadHold t m, MonadFix m) => (a -> b -> PushM t (Maybe b)) -> b -> Event t a -> m (Dynamic t b)
134−foldDynMaybeM = accumMaybeM . flip
135−
136−-- | Create a new 'Dynamic' that counts the occurences of the 'Event'.
137−count :: (Reflex t, MonadHold t m, MonadFix m, Num b) => Event t a -> m (Dynamic t b)
138−count e = holdDyn 0 =<< zipListWithEvent const (iterate (+1) 1) e
139−
140−-- | Create a new 'Dynamic' using the initial value that flips its
141−-- value every time the 'Event' occurs.
142−toggle :: (Reflex t, MonadHold t m, MonadFix m) => Bool -> Event t a -> m (Dynamic t Bool)
143−toggle = foldDyn (const not)
144−
145−-- | Switches to the new 'Event' whenever it receives one. Switching occurs
146−-- *before* the inner 'Event' fires - so if the 'Dynamic' changes and both the
147−-- old and new inner Events fire simultaneously, the output will fire with the
148−-- value of the *new* 'Event'.
149−switchPromptlyDyn :: forall t a. Reflex t => Dynamic t (Event t a) -> Event t a
150−switchPromptlyDyn de =
151− let eLag = switch $ current de
152− eCoincidences = coincidence $ updated de
153− in leftmost [eCoincidences, eLag]
154−
155−-- | Split a 'Dynamic' pair into a pair of 'Dynamic's
156−splitDynPure :: Reflex t => Dynamic t (a, b) -> (Dynamic t a, Dynamic t b)
157−splitDynPure d = (fmap fst d, fmap snd d)
158−
159−-- | Convert a 'Map' with 'Dynamic' elements into a 'Dynamic' of a 'Map' with
160−-- non-'Dynamic' elements.
161−distributeMapOverDynPure :: (Reflex t, Ord k) => Map k (Dynamic t v) -> Dynamic t (Map k v)
162−distributeMapOverDynPure = fmap dmapToMap . distributeDMapOverDynPure . mapWithFunctorToDMap
163−
164−-- | Convert a list with 'Dynamic' elements into a 'Dynamic' of a list with
165−-- non-'Dynamic' elements, preserving the order of the elements.
166−distributeListOverDynPure :: Reflex t => [Dynamic t v] -> Dynamic t [v]
167−distributeListOverDynPure =
168− fmap (map fromDSum . DMap.toAscList) .
169− distributeDMapOverDynPure .
170− DMap.fromDistinctAscList .
171− zipWith toDSum [0..]
172− where
173− toDSum :: Int -> Dynamic t a -> DSum (Const2 Int a) (Dynamic t)
174− toDSum k v = Const2 k :=> v
175− fromDSum :: DSum (Const2 Int a) Identity -> a
176− fromDSum (Const2 _ :=> Identity v) = v
177−
178−--TODO: Generalize this to functors other than Maps
179−-- | Combine a 'Dynamic' of a 'Map' of 'Dynamic's into a 'Dynamic'
180−-- with the current values of the 'Dynamic's in a map.
181−joinDynThroughMap :: forall t k a. (Reflex t, Ord k) => Dynamic t (Map k (Dynamic t a)) -> Dynamic t (Map k a)
182−joinDynThroughMap = joinDyn . fmap distributeMapOverDynPure
183−
184−-- | Print the value of the 'Dynamic' on each change and prefix it
185−-- with the provided string. This should /only/ be used for debugging.
186−--
187−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
188−-- else in the system is depending on it.
189−traceDyn :: (Reflex t, Show a) => String -> Dynamic t a -> Dynamic t a
190−traceDyn s = traceDynWith $ \x -> s <> ": " <> show x
191−
192−-- | Print the result of applying the provided function to the value
193−-- of the 'Dynamic' on each change. This should /only/ be used for
194−-- debugging.
195−--
196−-- Note: Just like Debug.Trace.trace, the value will only be shown if something
197−-- else in the system is depending on it.
198−traceDynWith :: Reflex t => (a -> String) -> Dynamic t a -> Dynamic t a
199−traceDynWith f d =
200− let e' = traceEventWith f $ updated d
201− getV0 = do
202− x <- sample $ current d
203− trace (f x) $ return x
204− in unsafeBuildDynamic getV0 e'
205−
206−-- | Replace the value of the 'Event' with the current value of the 'Dynamic'
207−-- each time the 'Event' occurs.
208−--
209−-- Note: `tagPromptlyDyn d e` differs from `tag (current d) e` in the case that `e` is firing
210−-- at the same time that `d` is changing. With `tagPromptlyDyn d e`, the *new* value of `d`
211−-- will replace the value of `e`, whereas with `tag (current d) e`, the *old* value
212−-- will be used, since the 'Behavior' won't be updated until the end of the frame.
213−-- Additionally, this means that the output 'Event' may not be used to directly change
214−-- the input 'Dynamic', because that would mean its value depends on itself. When creating
215−-- cyclic data flows, generally `tag (current d) e` is preferred.
216−tagPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
217−tagPromptlyDyn = attachPromptlyDynWith const
218−
219−-- | Attach the current value of the 'Dynamic' to the value of the
220−-- 'Event' each time it occurs.
221−--
222−-- Note: `attachPromptlyDyn d` is not the same as `attach (current d)`. See 'tagPromptlyDyn' for details.
223−attachPromptlyDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
224−attachPromptlyDyn = attachPromptlyDynWith (,)
225−
226−-- | Combine the current value of the 'Dynamic' with the value of the
227−-- 'Event' each time it occurs.
228−--
229−-- Note: `attachPromptlyDynWith f d` is not the same as `attachWith f (current d)`. See 'tagPromptlyDyn' for details.
230−attachPromptlyDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
231−attachPromptlyDynWith f = attachPromptlyDynWithMaybe $ \a b -> Just $ f a b
232−
233−-- | Create a new 'Event' by combining the value at each occurence with the
234−-- current value of the 'Dynamic' value and possibly filtering if the combining
235−-- function returns 'Nothing'.
236−--
237−-- Note: `attachPromptlyDynWithMaybe f d` is not the same as `attachWithMaybe f
238−-- (current d)`. See 'tagPromptlyDyn' for details.
239−attachPromptlyDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
240−attachPromptlyDynWithMaybe f d e =
241− let e' = attach (current d) e
242− in fforMaybe (align e' $ updated d) $ \case
243− This (a, b) -> f a b -- Only the tagging event is firing, so use that
244− These (_, b) a -> f a b -- Both events are firing, so use the newer value
245− That _ -> Nothing -- The tagging event isn't firing, so don't fire
246−
247−--------------------------------------------------------------------------------
248−-- Demux
249−--------------------------------------------------------------------------------
250−
251−-- | Represents a time changing value together with an 'EventSelector' that can
252−-- efficiently detect when the underlying 'Dynamic' has a particular value.
253−-- This is useful for representing data like the current selection of a long
254−-- list.
255−--
256−-- Semantically,
257−--
258−-- > demuxed (demux d) k === fmap (== k) d
259−--
260−-- However, the when getDemuxed is used multiple times, the complexity is only
261−-- /O(log(n))/, rather than /O(n)/ for fmap.
262−data Demux t k = Demux { demuxValue :: Behavior t k
263− , demuxSelector :: EventSelector t (Const2 k Bool)
264− }
265−
266−-- | Demultiplex an input value to a 'Demux' with many outputs. At any given
267−-- time, whichever output is indicated by the given 'Dynamic' will be 'True'.
268−demux :: (Reflex t, Ord k) => Dynamic t k -> Demux t k
269−demux k = Demux (current k)
270− (fan $ attachWith (\k0 k1 -> if k0 == k1
271− then DMap.empty
272− else DMap.fromList [Const2 k0 :=> Identity False,
273− Const2 k1 :=> Identity True])
274− (current k) (updated k))
275−
276−-- | Select a particular output of the 'Demux'; this is equivalent to (but much
277−-- faster than) mapping over the original 'Dynamic' and checking whether it is
278−-- equal to the given key.
279−demuxed :: (Reflex t, Eq k) => Demux t k -> k -> Dynamic t Bool
280−demuxed d k =
281− let e = select (demuxSelector d) (Const2 k)
282− in unsafeBuildDynamic (fmap (==k) $ sample $ demuxValue d) e
283−
284−--------------------------------------------------------------------------------
285−-- collectDyn
286−--------------------------------------------------------------------------------
287−
288−--TODO: This whole section is badly in need of cleanup
289−
290−-- | A heterogeneous list whose type and length are fixed statically. This is
291−-- reproduced from the 'HList' package due to integration issues, and because
292−-- very little other functionality from that library is needed.
293−data HList (l::[*]) where
294− HNil :: HList '[]
295− HCons :: e -> HList l -> HList (e ': l)
296−
297−infixr 2 `HCons`
298−
299−type family HRevApp (l1 :: [k]) (l2 :: [k]) :: [k]
300−type instance HRevApp '[] l = l
301−type instance HRevApp (e ': l) l' = HRevApp l (e ': l')
302−
303−hRevApp :: HList l1 -> HList l2 -> HList (HRevApp l1 l2)
304−hRevApp HNil l = l
305−hRevApp (HCons x l) l' = hRevApp l (HCons x l')
306−
307−hReverse :: HList l -> HList (HRevApp l '[])
308−hReverse l = hRevApp l HNil
309−
310−hBuild :: (HBuild' '[] r) => r
311−hBuild = hBuild' HNil
312−
313−class HBuild' l r where
314− hBuild' :: HList l -> r
315−
316−instance (l' ~ HRevApp l '[])
317− => HBuild' l (HList l') where
318− hBuild' = hReverse
319−
320−instance HBuild' (a ': l) r
321− => HBuild' l (a->r) where
322− hBuild' l x = hBuild' (HCons x l)
323−
324−-- | Like 'HList', but with a functor wrapping each element.
325−data FHList f l where
326− FHNil :: FHList f '[]
327− FHCons :: f e -> FHList f l -> FHList f (e ': l)
328−
329−instance GEq (HListPtr l) where
330− HHeadPtr `geq` HHeadPtr = Just Refl
331− HHeadPtr `geq` HTailPtr _ = Nothing
332− HTailPtr _ `geq` HHeadPtr = Nothing
333− HTailPtr a `geq` HTailPtr b = a `geq` b
334−
335−instance GCompare (HListPtr l) where -- Warning: This ordering can't change, dmapTo*HList will break
336− HHeadPtr `gcompare` HHeadPtr = GEQ
337− HHeadPtr `gcompare` HTailPtr _ = GLT
338− HTailPtr _ `gcompare` HHeadPtr = GGT
339− HTailPtr a `gcompare` HTailPtr b = a `gcompare` b
340−
341−data HListPtr l a where
342− HHeadPtr :: HListPtr (h ': t) h
343− HTailPtr :: HListPtr t a -> HListPtr (h ': t) a
344−
345−fhlistToDMap :: forall (f :: * -> *) l. FHList f l -> DMap (HListPtr l) f
346−fhlistToDMap = DMap.fromList . go
347− where go :: forall l'. FHList f l' -> [DSum (HListPtr l') f]
348− go = \case
349− FHNil -> []
350− FHCons h t -> (HHeadPtr :=> h) : map (\(p :=> v) -> HTailPtr p :=> v) (go t)
351−
352−class RebuildSortedHList l where
353− rebuildSortedFHList :: [DSum (HListPtr l) f] -> FHList f l
354− rebuildSortedHList :: [DSum (HListPtr l) Identity] -> HList l
355−
356−instance RebuildSortedHList '[] where
357− rebuildSortedFHList l = case l of
358− [] -> FHNil
359− _ : _ -> error "rebuildSortedFHList{'[]}: empty list expected"
360− rebuildSortedHList l = case l of
361− [] -> HNil
362− _ : _ -> error "rebuildSortedHList{'[]}: empty list expected"
363−
364−instance RebuildSortedHList t => RebuildSortedHList (h ': t) where
365− rebuildSortedFHList l = case l of
366− ((HHeadPtr :=> h) : t) -> FHCons h . rebuildSortedFHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
367− _ -> error "rebuildSortedFHList{h':t}: non-empty list with HHeadPtr expected"
368− rebuildSortedHList l = case l of
369− ((HHeadPtr :=> Identity h) : t) -> HCons h . rebuildSortedHList . map (\(HTailPtr p :=> v) -> p :=> v) $ t
370− _ -> error "rebuildSortedHList{h':t}: non-empty list with HHeadPtr expected"
371−
372−dmapToHList :: forall l. RebuildSortedHList l => DMap (HListPtr l) Identity -> HList l
373−dmapToHList = rebuildSortedHList . DMap.toList
374−
375−-- | Collect a hetereogeneous list whose elements are all 'Dynamic's into a
376−-- single 'Dynamic' whose value represents the current values of all of the
377−-- input 'Dynamic's.
378−distributeFHListOverDynPure :: (Reflex t, RebuildSortedHList l) => FHList (Dynamic t) l -> Dynamic t (HList l)
379−distributeFHListOverDynPure l = fmap dmapToHList $ distributeDMapOverDynPure $ fhlistToDMap l
380−
381−class AllAreFunctors (f :: a -> *) (l :: [a]) where
382− type FunctorList f l :: [*]
383− toFHList :: HList (FunctorList f l) -> FHList f l
384− fromFHList :: FHList f l -> HList (FunctorList f l)
385−
386−instance AllAreFunctors f '[] where
387− type FunctorList f '[] = '[]
388− toFHList l = case l of
389− HNil -> FHNil
390−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
391− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
392−#endif
393− fromFHList FHNil = HNil
394−
395−instance AllAreFunctors f t => AllAreFunctors f (h ': t) where
396− type FunctorList f (h ': t) = f h ': FunctorList f t
397− toFHList l = case l of
398− a `HCons` b -> a `FHCons` toFHList b
399−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
400− _ -> error "toFHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
401−#endif
402− fromFHList (a `FHCons` b) = a `HCons` fromFHList b
403−
404−-- | Convert a datastructure whose constituent parts are all 'Dynamic's into a
405−-- single 'Dynamic' whose value represents all the current values of the input's
406−-- consitutent 'Dynamic's.
407−collectDynPure :: ( RebuildSortedHList (HListElems b)
408− , IsHList a, IsHList b
409− , AllAreFunctors (Dynamic t) (HListElems b)
410− , Reflex t
411− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
412− ) => a -> Dynamic t b
413−collectDynPure ds = fmap fromHList $ distributeFHListOverDynPure $ toFHList $ toHList ds
414−
415−-- Poor man's Generic
416−class IsHList a where
417− type HListElems a :: [*]
418− toHList :: a -> HList (HListElems a)
419− fromHList :: HList (HListElems a) -> a
420−
421−instance IsHList (a, b) where
422− type HListElems (a, b) = [a, b]
423− toHList (a, b) = hBuild a b
424− fromHList l = case l of
425− a `HCons` b `HCons` HNil -> (a, b)
426−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
427− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
428−#endif
429−
430−instance IsHList (a, b, c, d) where
431− type HListElems (a, b, c, d) = [a, b, c, d]
432− toHList (a, b, c, d) = hBuild a b c d
433− fromHList l = case l of
434− a `HCons` b `HCons` c `HCons` d `HCons` HNil -> (a, b, c, d)
435−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
436− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
437−#endif
438−
439−instance IsHList (a, b, c, d, e, f) where
440− type HListElems (a, b, c, d, e, f) = [a, b, c, d, e, f]
441− toHList (a, b, c, d, e, f) = hBuild a b c d e f
442− fromHList l = case l of
443− a `HCons` b `HCons` c `HCons` d `HCons` e `HCons` f `HCons` HNil -> (a, b, c, d, e, f)
444−#if !defined(__GLASGOW_HASKELL__) || __GLASGOW_HASKELL__ < 800
445− _ -> error "fromHList: impossible" -- Otherwise, GHC complains of a non-exhaustive pattern match; see https://ghc.haskell.org/trac/ghc/ticket/4139
446−#endif
447−
448−--------------------------------------------------------------------------------
449−-- Deprecated functions
450−--------------------------------------------------------------------------------
451−
452−-- | Map a function over a 'Dynamic'.
453−{-# DEPRECATED mapDyn "Use 'return . fmap f' instead of 'mapDyn f'; consider eliminating monadic style" #-}
454−mapDyn :: (Reflex t, Monad m) => (a -> b) -> Dynamic t a -> m (Dynamic t b)
455−mapDyn f = return . fmap f
456−
457−-- | Map a sampling function over a 'Dynamic'. The sampling function will use the input 'Dynamic''s 'current' 'Behavior' until the first time its 'updated' 'Event' fires, at which point it will be invoked only once each time the 'Event' fires.
458−{-# DEPRECATED mapDynM "Consider using the Monad instance for Dynamic instead." #-}
459−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)
460−mapDynM f d = do
461− let e' = push (fmap Just . f :: a -> PushM t (Maybe b)) $ updated d
462− eb' = fmap constant e'
463− v0 = pull $ f =<< sample (current d)
464− bb' :: Behavior t (Behavior t b) <- hold v0 eb'
465− let b' = pull $ sample =<< sample bb'
466− return $ unsafeDynamic b' e'
467−
468−-- | Flipped version of 'mapDyn'.
469−{-# DEPRECATED forDyn "Use 'return . ffor a' instead of 'forDyn a'; consider eliminating monadic style" #-}
470−forDyn :: (Reflex t, Monad m) => Dynamic t a -> (a -> b) -> m (Dynamic t b)
471−forDyn a = return . ffor a
472−
473−-- | Flipped version of 'mapDynM'
474−{-# DEPRECATED forDynM "Consider using the Monad instance for Dynamic instead." #-}
475−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)
476−forDynM d f = mapDynM f d
477−
478−-- | Split the 'Dynamic' into two 'Dynamic's, each taking the respective value
479−-- of the tuple.
480−{-# DEPRECATED splitDyn "Use 'return . splitDynPure' instead; consider eliminating monadic style" #-}
481−splitDyn :: (Reflex t, Monad m) => Dynamic t (a, b) -> m (Dynamic t a, Dynamic t b)
482−splitDyn = return . splitDynPure
483−
484−-- | Merge the 'Dynamic' values using their 'Monoid' instance.
485−{-# DEPRECATED mconcatDyn "Use 'return . mconcat' instead; consider eliminating monadic style" #-}
486−mconcatDyn :: forall t m a. (Reflex t, Monad m, Monoid a) => [Dynamic t a] -> m (Dynamic t a)
487−mconcatDyn = return . mconcat
488−
489−-- | This function no longer needs to be monadic; see 'distributeMapOverDynPure'.
490−{-# DEPRECATED distributeDMapOverDyn "Use 'return . distributeDMapOverDynPure' instead; consider eliminating monadic style" #-}
491−distributeDMapOverDyn :: (Reflex t, Monad m, GCompare k) => DMap k (Dynamic t) -> m (Dynamic t (DMap k Identity))
492−distributeDMapOverDyn = return . distributeDMapOverDynPure
493−
494−-- | Merge two 'Dynamic's into a new one using the provided function. The new
495−-- 'Dynamic' changes its value each time one of the original 'Dynamic's changes
496−-- its value.
497−{-# DEPRECATED combineDyn "Use 'return (zipDynWith f a b)' instead of 'combineDyn f a b'; consider eliminating monadic style" #-}
498−combineDyn :: forall t m a b c. (Reflex t, Monad m) => (a -> b -> c) -> Dynamic t a -> Dynamic t b -> m (Dynamic t c)
499−combineDyn f a b = return $ zipDynWith f a b
500−
501−-- | A psuedo applicative version of ap for 'Dynamic'. Example useage:
502−--
503−-- > do
504−-- > person <- Person `mapDyn` dynFirstName
505−-- > `apDyn` dynListName
506−-- > `apDyn` dynAge
507−-- > `apDyn` dynAddress
508−{-# 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" #-}
509−{-# ANN apDyn "HLint: ignore Use fmap" #-}
510−apDyn :: forall t m a b. (Reflex t, Monad m)
511− => m (Dynamic t (a -> b))
512− -> Dynamic t a
513− -> m (Dynamic t b)
514−apDyn m a = liftM (<*> a) m
515−
516−--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
517−-- | This function no longer needs to be monadic, so it has been replaced by
518−-- 'demuxed', which is pure.
519−{-# DEPRECATED getDemuxed "Use 'return . demuxed d' instead of 'getDemuxed d'; consider eliminating monadic style" #-}
520−getDemuxed :: (Reflex t, Monad m, Eq k) => Demux t k -> k -> m (Dynamic t Bool)
521−getDemuxed d = return . demuxed d
522−
523−-- | This function no longer needs to be monadic, so it has been replaced by
524−-- 'distributeFHListOverDynPure', which is pure.
525−{-# DEPRECATED distributeFHListOverDyn "Use 'return . distributeFHListOverDynPure' instead; consider eliminating monadic style" #-}
526−distributeFHListOverDyn :: forall t m l. (Reflex t, Monad m, RebuildSortedHList l) => FHList (Dynamic t) l -> m (Dynamic t (HList l))
527−distributeFHListOverDyn = return . distributeFHListOverDynPure
528−
529−-- | This function no longer needs to be monadic, so it has been replaced by
530−-- 'collectDynPure', which is pure.
531−{-# DEPRECATED collectDyn "Use 'return . collectDynPure' instead; consider eliminating monadic style" #-}
532−collectDyn :: ( RebuildSortedHList (HListElems b)
533− , IsHList a, IsHList b
534− , AllAreFunctors (Dynamic t) (HListElems b)
535− , Reflex t, Monad m
536− , HListElems a ~ FunctorList (Dynamic t) (HListElems b)
537− ) => a -> m (Dynamic t b)
538−collectDyn = return . collectDynPure
539−
540−-- | This function has been renamed to 'tagPromptlyDyn' to clarify its
541−-- semantics.
542−{-# DEPRECATED tagDyn "Use 'tagPromptlyDyn' instead" #-}
543−tagDyn :: Reflex t => Dynamic t a -> Event t b -> Event t a
544−tagDyn = tagPromptlyDyn
545−
546−-- | This function has been renamed to 'attachPromptlyDyn' to clarify its
547−-- semantics.
548−{-# DEPRECATED attachDyn "Use 'attachPromptlyDyn' instead" #-}
549−attachDyn :: Reflex t => Dynamic t a -> Event t b -> Event t (a, b)
550−attachDyn = attachPromptlyDyn
551−
552−-- | This function has been renamed to 'attachPromptlyDynWith' to clarify its
553−-- semantics.
554−{-# DEPRECATED attachDynWith "Use 'attachPromptlyDynWith' instead" #-}
555−attachDynWith :: Reflex t => (a -> b -> c) -> Dynamic t a -> Event t b -> Event t c
556−attachDynWith = attachPromptlyDynWith
557−
558−-- | This function has been renamed to 'attachPromptlyDynWithMaybe' to clarify
559−-- its semantics.
560−{-# DEPRECATED attachDynWithMaybe "Use 'attachPromptlyDynWithMaybe' instead" #-}
561−attachDynWithMaybe :: Reflex t => (a -> b -> Maybe c) -> Dynamic t a -> Event t b -> Event t c
562−attachDynWithMaybe = attachPromptlyDynWithMaybe
563−
564−-- | Combine an inner and outer 'Dynamic' such that the resulting 'Dynamic''s
565−-- current value will always be equal to the current value's current value, and
566−-- will change whenever either the inner or the outer (or both) values change.
567−{-# DEPRECATED joinDyn "Use 'join' instead" #-}
568−joinDyn :: Reflex t => Dynamic t (Dynamic t a) -> Dynamic t a
569−joinDyn = join
570−
571−-- | 'nubDyn''s behavior is not quite analogous to 'nub''s behavior, so it has
572−-- been renamed to 'uniqDyn'
573−{-# DEPRECATED nubDyn "Use 'uniqDyn' instead" #-}
574−nubDyn :: (Reflex t, Eq a) => Dynamic t a -> Dynamic t a
575−nubDyn = uniqDyn
src/Reflex/Dynamic/TH.hsdeleted−92
@@ -1,92 +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 (qDyn, unqDyn, mkDyn) where
13−
14−import Reflex.Dynamic
15−
16−import Control.Monad.State
17−import Data.Data
18−import Data.Generics
19−import Data.Monoid
20−import qualified Language.Haskell.Exts as Hs
21−import qualified Language.Haskell.Meta.Syntax.Translate as Hs
22−import Language.Haskell.TH
23−import Language.Haskell.TH.Quote
24−import qualified Language.Haskell.TH.Syntax as TH
25−
26−-- | Quote a 'Dynamic' expression. Within the quoted expression, you can use
27−-- @$(unqDyn [| x |])@ to refer to any expression @x@ of type @Dynamic t a@; the
28−-- unquoted result will be of type @a@
29−qDyn :: Q Exp -> Q Exp
30−qDyn qe = do
31− e <- qe
32− let f :: forall d. Data d => d -> StateT [(Name, Exp)] Q d
33− f d = case eqT of
34− Just (Refl :: d :~: Exp)
35− | AppE (VarE m) eInner <- d
36− , m == 'unqMarker
37− -> do n <- lift $ newName "dynamicQuotedExpressionVariable"
38− modify ((n, eInner):)
39− return $ VarE n
40− _ -> gmapM f d
41− (e', exprsReversed) <- runStateT (gmapM f e) []
42− let exprs = reverse exprsReversed
43− arg = foldr (\a b -> ConE 'FHCons `AppE` a `AppE` b) (ConE 'FHNil) $ map snd exprs
44− param = foldr (\a b -> ConP 'HCons [VarP a, b]) (ConP 'HNil []) $ map fst exprs
45− [| return $ $(return $ LamE [param] e') <$> distributeFHListOverDynPure $(return arg) |]
46−
47−-- | Antiquote a 'Dynamic' expression. This can /only/ be used inside of a
48−-- 'qDyn' quotation.
49−unqDyn :: Q Exp -> Q Exp
50−unqDyn e = [| unqMarker $e |]
51−
52−-- | This type represents an occurrence of unqDyn before it has been processed
53−-- by qDyn. If you see it in a type error, it probably means that unqDyn has
54−-- been used outside of a qDyn context.
55−data UnqDyn
56−
57−-- unqMarker must not be exported; it is used only as a way of smuggling data
58−-- from unqDyn to qDyn
59−
60−--TODO: It would be much nicer if the TH AST was extensible to support this kind of thing without trickery
61−unqMarker :: a -> UnqDyn
62−unqMarker = error "An unqDyn expression was used outside of a qDyn expression"
63−
64−-- | Create a 'Dynamic' value using other 'Dynamic's as inputs. The result is
65−-- sometimes more concise and readable than the equivalent 'Applicative'-based
66−-- expression. For example:
67−--
68−-- > [mkDyn| $x + $v * $t + 1/2 * $a * $t ^ 2 |]
69−--
70−-- would have a very cumbersome 'Applicative' encoding.
71−mkDyn :: QuasiQuoter
72−mkDyn = QuasiQuoter
73− { quoteExp = mkDynExp
74− , quotePat = error "mkDyn: pattern splices are not supported"
75− , quoteType = error "mkDyn: type splices are not supported"
76− , quoteDec = error "mkDyn: declaration splices are not supported"
77− }
78−
79−mkDynExp :: String -> Q Exp
80−mkDynExp s = case Hs.parseExpWithMode (Hs.defaultParseMode { Hs.extensions = [ Hs.EnableExtension Hs.TemplateHaskell ] }) s of
81− Hs.ParseFailed (Hs.SrcLoc _ l c) err -> fail $ "mkDyn:" <> show l <> ":" <> show c <> ": " <> err
82− Hs.ParseOk e -> qDyn $ return $ everywhere (id `extT` reinstateUnqDyn) $ Hs.toExp $ everywhere (id `extT` antiE) e
83− where TH.Name (TH.OccName occName) (TH.NameG _ _ (TH.ModName modName)) = 'unqMarker
84− antiE x = case x of
85− Hs.SpliceExp se ->
86− Hs.App (Hs.Var $ Hs.Qual (Hs.ModuleName modName) (Hs.Ident occName)) $ case se of
87− Hs.IdSplice v -> Hs.Var $ Hs.UnQual $ Hs.Ident v
88− Hs.ParenSplice ps -> ps
89− _ -> x
90− reinstateUnqDyn (TH.Name (TH.OccName occName') (TH.NameQ (TH.ModName modName')))
91− | modName == modName' && occName == occName' = 'unqMarker
92− reinstateUnqDyn x = x
src/Reflex/Dynamic/Uniq.hsdeleted−105
@@ -1,105 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE MagicHash #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−#ifdef USE_REFLEX_OPTIMIZER
6−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
7−#endif
8−-- | This module provides a variation of 'Dynamic' values that uses cheap
9−-- pointer equality checks to reduce the amount of signal propagation needed.
10−module Reflex.Dynamic.Uniq
11− ( UniqDynamic
12− , uniqDynamic
13− , fromUniqDynamic
14− , alreadyUniqDynamic
15− ) where
16−
17−import Control.Applicative (Applicative (..))
18−import GHC.Exts
19−import Reflex.Class
20−import Reflex.Dynamic
21−
22−-- | A 'Dynamic' whose 'updated' 'Event' will never fire with the same value as
23−-- the 'current' 'Behavior''s contents. In order to maintain this constraint,
24−-- the value inside a 'UniqDynamic' is always evaluated to
25−-- <https://wiki.haskell.org/Weak_head_normal_form weak head normal form>.
26−--
27−-- Internally, 'UniqDynamic' uses pointer equality as a heuristic to avoid
28−-- unnecessary update propagation; this is much more efficient than performing
29−-- full comparisons. However, when the 'UniqDynamic' is converted back into a
30−-- regular 'Dynamic', a full comparison is performed.
31−newtype UniqDynamic t a = UniqDynamic { unUniqDynamic :: Dynamic t a }
32−
33−-- | Construct a 'UniqDynamic' by eliminating redundant updates from a 'Dynamic'.
34−uniqDynamic :: Reflex t => Dynamic t a -> UniqDynamic t a
35−uniqDynamic d = UniqDynamic $ unsafeBuildDynamic (sample $ current d) $ flip push (updated d) $ \new -> do --TODO: It would be very nice if we had an uncached push here
36− old <- sample $ current d --TODO: Is it better to sample ourselves here?
37− return $ unsafeJustChanged old new
38−
39−-- | Retrieve a normal 'Dynamic' from a 'UniqDynamic'. This will perform a
40−-- final check using the output type's 'Eq' instance to ensure deterministic
41−-- behavior.
42−--
43−-- WARNING: If used with a type whose 'Eq' instance is not law-abiding -
44−-- specifically, if there are cases where @x /= x@, 'fromUniqDynamic' may
45−-- eliminate more 'updated' occurrences than it should. For example, NaN values
46−-- of 'Double' and 'Float' are considered unequal to themselves by the 'Eq'
47−-- instance, but can be equal by pointer equality. This may cause 'UniqDynamic'
48−-- to lose changes from NaN to NaN.
49−fromUniqDynamic :: (Reflex t, Eq a) => UniqDynamic t a -> Dynamic t a
50−fromUniqDynamic (UniqDynamic d) = uniqDynBy superEq d
51− where
52− -- Only consider values different if they fail both pointer equality /and/
53− -- 'Eq' equality. This is to make things a bit more deterministic in the
54− -- case of unlawful 'Eq' instances. However, it is still possible to
55− -- achieve nondeterminism by constructing elements that are identical in
56− -- value, unequal according to 'Eq', and nondeterministically equal or
57− -- nonequal by pointer quality. I suspect that it is impossible to make the
58− -- behavior deterministic in this case.
59− superEq a b = a `unsafePtrEq` b || a == b
60−
61−-- | Create a UniqDynamic without uniqing it on creation. This will be slightly
62−-- faster than uniqDynamic when used with a Dynamic whose values are always (or
63−-- nearly always) different from its previous values; if used with a Dynamic
64−-- whose values do not change frequently, it may be much slower than uniqDynamic
65−alreadyUniqDynamic :: Dynamic t a -> UniqDynamic t a
66−alreadyUniqDynamic = UniqDynamic
67−
68−unsafePtrEq :: a -> a -> Bool
69−unsafePtrEq a b = case a `seq` b `seq` reallyUnsafePtrEquality# a b of
70− 0# -> False
71− _ -> True
72−
73−unsafeJustChanged :: a -> a -> Maybe a
74−unsafeJustChanged old new =
75− if old `unsafePtrEq` new
76− then Nothing
77− else Just new
78−
79−instance Reflex t => Accumulator t (UniqDynamic t) where
80− accumMaybeM f z e = do
81− let f' old change = do
82− mNew <- f old change
83− return $ unsafeJustChanged old =<< mNew
84− d <- accumMaybeM f' z e
85− return $ UniqDynamic d
86− mapAccumMaybeM f z e = do
87− let f' old change = do
88− (mNew, output) <- f old change
89− return (unsafeJustChanged old =<< mNew, output)
90− (d, out) <- mapAccumMaybeM f' z e
91− return (UniqDynamic d, out)
92−
93−instance Reflex t => Functor (UniqDynamic t) where
94− fmap f (UniqDynamic d) = uniqDynamic $ fmap f d
95−
96−instance Reflex t => Applicative (UniqDynamic t) where
97− pure = UniqDynamic . constDyn
98− UniqDynamic a <*> UniqDynamic b = uniqDynamic $ a <*> b
99− _ *> b = b
100− a <* _ = a
101−
102−instance Reflex t => Monad (UniqDynamic t) where
103− UniqDynamic x >>= f = uniqDynamic $ x >>= unUniqDynamic . f
104− _ >> b = b
105− return = pure
src/Reflex/DynamicWriter.hsdeleted−200
@@ -1,200 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE LambdaCase #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RecursiveDo #-}
9−{-# LANGUAGE ScopedTypeVariables #-}
10−{-# LANGUAGE TypeFamilies #-}
11−{-# LANGUAGE UndecidableInstances #-}
12−#ifdef USE_REFLEX_OPTIMIZER
13−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
14−#endif
15−module Reflex.DynamicWriter where
16−
17−import Control.Monad.Exception
18−import Control.Monad.Identity
19−import Control.Monad.IO.Class
20−import Control.Monad.Reader
21−import Control.Monad.Ref
22−import Control.Monad.State.Strict
23−import Data.Dependent.Map (DMap, DSum (..))
24−import qualified Data.Dependent.Map as DMap
25−import Data.Functor.Misc
26−import Data.Map (Map)
27−import qualified Data.Map as Map
28−import Data.Semigroup
29−import Data.Some (Some)
30−import qualified Data.Some as Some
31−import Reflex.Class
32−import Reflex.Dynamic
33−import Reflex.Host.Class
34−import Reflex.PerformEvent.Class
35−import Reflex.PostBuild.Class
36−
37−instance MonadTrans (DynamicWriterT t w) where
38− lift = DynamicWriterT . lift
39−
40−mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v, Ord k) => Map k v -> Event t (PatchMap k v) -> m (Dynamic t v)
41−mconcatIncremental m0 e = do
42− d <- foldDynMaybe apply m0 e
43− return $ fmap (mconcat . Map.elems) d
44−
45−mapIncrementalMapValuesWithKey :: (Reflex t, Ord k) => (k -> v -> v') -> Incremental t (PatchMap k v) -> Incremental t (PatchMap k v')
46−mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
47−
48−mapIncrementalMapValues :: (Reflex t, Ord k) => (v -> v') -> Incremental t (PatchMap k v) -> Incremental t (PatchMap k v')
49−mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
50−
51−unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (PatchTarget p -> PatchTarget p') -> (p -> p') -> Incremental t p -> Incremental t p'
52−unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
53−
54−incrementalExtractFunctorDMap :: (Reflex t, Ord k) => Incremental t (PatchMap k (f v)) -> Incremental t (PatchDMap (Const2 k v) f)
55−incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap ComposeMaybe m
56−
57−mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t (PatchMap k (Event t v)) -> Event t (Map k v)
58−mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
59−
60−holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap k (Replaceable t v)) -> m (Incremental t (PatchMap k v))
61−holdIncrementalReplaceableMap m0 m' = do
62− rec vals <- holdIncremental m0 $ m' <> valChanges
63− let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
64− return $ mapIncrementalMapValues _replaceable_value vals
65−
66−mergeDynIncremental :: (Reflex t, Ord k) => Incremental t (PatchMap k (Dynamic t v)) -> Incremental t (PatchMap k v)
67−mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
68− where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
69− addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
70−
71−data Replaceable t w = Replaceable
72− { _replaceable_value :: w
73− , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
74− }
75−
76−type DynamicWriterAccumulator t w = [Dynamic t w]
77−
78−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT (DynamicWriterAccumulator t w) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
79−
80−instance MonadRef m => MonadRef (DynamicWriterT t w m) where
81− type Ref (DynamicWriterT t w m) = Ref m
82− newRef = lift . newRef
83− readRef = lift . readRef
84− writeRef r = lift . writeRef r
85−
86−instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
87− atomicModifyRef r = lift . atomicModifyRef r
88−
89−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
90− newEventWithTrigger = lift . newEventWithTrigger
91− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
92−
93−runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
94−runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
95−
96−mconcatIncrementalReplaceableDynMap :: forall m t k v.
97− (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k)
98− => Map k (Replaceable t (Dynamic t v))
99− -> Event t (PatchMap k (Replaceable t (Dynamic t v)))
100− -> Event t ()
101− -> m (Replaceable t (Dynamic t v))
102−mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
103− rec vals <- holdIncremental m0 $ m' <> valChanges
104− let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
105− let i = mapIncrementalMapValues _replaceable_value vals
106− noMoreAdditions <- holdDyn False $ True <$ additionsCeased
107− let replaceSelf nma x = do
108− guard nma
109− case Map.toList x of
110− [] -> return Nothing -- Delete self
111− [(_, n)] -> return $ Just n --TODO: What if this one is also replacing itself simultaneously?
112− _ -> mzero
113− result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
114− return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
115−
116−runDynamicWriterT :: (MonadFix m, Reflex t, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
117−runDynamicWriterT (DynamicWriterT a) = do
118− (result, ws) <- runStateT a []
119− return (result, mconcat $ reverse ws)
120−
121−class Monad m => MonadDynamicWriter t w m | m -> t w where
122− tellDyn :: Dynamic t w -> m ()
123−
124−instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
125− tellDyn w = DynamicWriterT $ modify (w :)
126−
127−instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
128− ask = lift ask
129− local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
130− reader = lift . reader
131−
132−liftDynamicWriterTThroughSync :: Monad m' => (m (a, DynamicWriterAccumulator t w) -> m' (b, (a, DynamicWriterAccumulator t w))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
133−liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
134− s <- get
135− (b, (a, newS)) <- lift $ f $ runStateT child s
136− put newS
137− return (b, a)
138−
139−instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
140− type Performable (DynamicWriterT t w m) = Performable m
141− performEvent_ = lift . performEvent_
142− performEvent = lift . performEvent
143−
144−instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
145− newTriggerEvent = lift newTriggerEvent
146− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
147− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
148−
149−instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
150− getPostBuild = lift getPostBuild
151−
152−instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
153− tellDyn = lift . tellDyn
154−
155−instance MonadState s m => MonadState s (DynamicWriterT t w m) where
156− get = lift get
157− put = lift . put
158−
159−newtype DynamicWriterTLoweredResult t w v = DynamicWriterTLoweredResult { unDynamicWriterTLoweredResult :: (v, Dynamic t w) }
160−
161−instance (MonadAdjust t m, MonadFix m, Monoid w, MonadHold t m, Reflex t) => MonadAdjust t (DynamicWriterT t w m) where
162− runWithReplace a0 a' = do
163− (result0, result') <- lift $ runWithReplace (runDynamicWriterT a0) $ runDynamicWriterT <$> a'
164− tellDyn . join =<< holdDyn (snd result0) (snd <$> result')
165− return (fst result0, fst <$> result')
166− sequenceDMapWithAdjust (dm0 :: DMap k (DynamicWriterT t w m)) dm' = do
167− let loweredDm0 = mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> fmap DynamicWriterTLoweredResult (runDynamicWriterT v)) dm0
168− let loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
169− mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> WrapArg k :=> ComposeMaybe (fmap (fmap DynamicWriterTLoweredResult . runDynamicWriterT) mv)) p
170− (result0, result') <- lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
171− let getValue (DynamicWriterTLoweredResult (v, _)) = v
172− getWritten (DynamicWriterTLoweredResult (_, w)) = w
173− liftedResult0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity r) -> k :=> Identity (getValue r)) result0
174− liftedResult' = ffor result' $ \(PatchDMap p) -> PatchDMap $
175− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (Identity . getValue . runIdentity) mr)) p
176− liftedWritten0 :: Map (Some k) (Dynamic t w)
177− liftedWritten0 = Map.fromDistinctAscList $ (\(WrapArg k :=> Identity r) -> (Some.This k, getWritten r)) <$> DMap.toList result0
178− liftedWritten' = ffor result' $ \(PatchDMap p) -> PatchMap $
179− Map.fromDistinctAscList $ (\(WrapArg k :=> ComposeMaybe mr) -> (Some.This k, fmap (getWritten . runIdentity) mr)) <$> DMap.toList p
180− --TODO: We should be able to improve the performance here by incrementally updating the mconcat of the merged Dynamics
181− i <- holdIncremental liftedWritten0 liftedWritten'
182− tellDyn $ fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
183− return (liftedResult0, liftedResult')
184−
185−withDynamicWriterT :: (Monoid w, Reflex t, MonadHold t m, MonadFix m)
186− => (w -> w')
187− -> DynamicWriterT t w m a
188− -> DynamicWriterT t w' m a
189−withDynamicWriterT f dw = do
190− (r, d) <- lift $ do
191− (r, d) <- runDynamicWriterT dw
192− let d' = fmap f d
193− return (r, d')
194− tellDyn d
195− return r
196−
197−instance MonadRequest t m => MonadRequest t (DynamicWriterT t w m) where
198− type Request (DynamicWriterT t w m) = Request m
199− type Response (DynamicWriterT t w m) = Response m
200− withRequesting f = DynamicWriterT $ withRequesting $ unDynamicWriterT . f
src/Reflex/FunctorMaybe.hsdeleted−25
@@ -1,25 +0,0 @@
1−module Reflex.FunctorMaybe where
2−
3−import Data.Maybe
4−
5−--TODO: See if there's a better class in the standard libraries already
6−
7−-- | A class for values that combines filtering and mapping using 'Maybe'.
8−-- Morally, @'FunctorMaybe' ~ KleisliFunctor 'Maybe'@. Also similar is the
9−-- @Witherable@ typeclass, but it requires @Foldable f@ and @Traverable f@,
10−-- and e.g. 'Event' is instance of neither.
11−--
12−-- A definition of 'fmapMaybe' must satisfy the following laws:
13−--
14−-- [/identity/]
15−-- @'fmapMaybe' 'Just' ≡ 'id'@
16−--
17−-- [/composition/]
18−-- @'fmapMaybe' (f <=< g) ≡ 'fmapMaybe' f . 'fmapMaybe' g@
19−class FunctorMaybe f where
20− -- | Combined mapping and filtering function.
21− fmapMaybe :: (a -> Maybe b) -> f a -> f b
22−
23−instance FunctorMaybe [] where
24− fmapMaybe f = catMaybes . fmap f
25−
src/Reflex/Host/Class.hsdeleted−299
@@ -1,299 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE RankNTypes #-}
9−{-# LANGUAGE RoleAnnotations #-}
10−{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE TypeFamilies #-}
12−{-# LANGUAGE UndecidableInstances #-}
13−#ifdef USE_REFLEX_OPTIMIZER
14−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
15−#endif
16−-- | This module provides the interface for hosting 'Reflex' engines. This
17−-- should only be necessary if you're writing a binding or some other library
18−-- that provides a core event loop.
19−module Reflex.Host.Class
20− ( ReflexHost (..)
21− , MonadSubscribeEvent (..)
22− , MonadReadEvent (..)
23− , MonadReflexCreateTrigger (..)
24− , MonadReflexHost (..)
25− , fireEvents
26− , newEventWithTriggerRef
27− , fireEventRef
28− , fireEventRefAndRead
29− ) where
30−
31−import Reflex.Class
32−
33−import Control.Applicative
34−import Control.Monad
35−import Control.Monad.Fix
36−import Control.Monad.Identity
37−import Control.Monad.Ref
38−import Control.Monad.Trans
39−import Control.Monad.Trans.Cont (ContT ())
40−import Control.Monad.Trans.Except (ExceptT ())
41−import Control.Monad.Trans.Reader (ReaderT ())
42−import Control.Monad.Trans.RWS (RWST ())
43−import Control.Monad.Trans.State (StateT ())
44−import qualified Control.Monad.Trans.State.Strict as Strict
45−import Control.Monad.Trans.Writer (WriterT ())
46−import Data.Dependent.Sum (DSum (..))
47−import Data.GADT.Compare
48−import Data.Monoid
49−
50−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
51−import Control.Monad.Primitive (touch)
52−import Data.IORef
53−import Reflex.Spider.Internal (EventM (..), HasSpiderTimeline, RootTrigger, SpiderEventHandle (..),
54− SpiderHost (..), SpiderHostFrame (..), Subscriber (..), newEventWithTriggerIO,
55− newFanEventWithTriggerIO, run, runFrame, scheduleClear, subscribe)
56−import qualified Reflex.Spider.Internal
57−#endif
58−
59−-- Note: this import must come last to silence warnings from AMP
60−import Prelude hiding (foldl, mapM, mapM_, sequence, sequence_)
61−
62−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
63−#include "../Spider/SpiderTimelineHost.include.hs"
64−#endif
65−
66−-- | Framework implementation support class for the reflex implementation
67−-- represented by @t@.
68−class ( Reflex t
69− , MonadReflexCreateTrigger t (HostFrame t)
70− , MonadSample t (HostFrame t)
71− , MonadHold t (HostFrame t)
72− , MonadFix (HostFrame t)
73− , MonadSubscribeEvent t (HostFrame t)
74− ) => ReflexHost t where
75− type EventTrigger t :: * -> *
76− type EventHandle t :: * -> *
77− type HostFrame t :: * -> *
78−
79−-- | Monad in which Events can be 'subscribed'. This forces all underlying
80−-- event sources to be initialized, so that the event will fire whenever it
81−-- ought to. Events must be subscribed before they are read using readEvent
82−class (Reflex t, Monad m) => MonadSubscribeEvent t m | m -> t where
83− -- | Subscribe to an event and set it up if needed.
84− --
85− -- This function will create a new 'EventHandle' from an 'Event'. This handle
86− -- may then be used via 'readEvent' in the read callback of
87− -- 'fireEventsAndRead'.
88− --
89− -- If the event wasn't subscribed to before (either manually or through a
90− -- dependent event or behavior) then this function will cause the event and
91− -- all dependencies of this event to be set up. For example, if the event was
92− -- created by 'newEventWithTrigger', then it's callback will be executed.
93− --
94− -- It's safe to call this function multiple times.
95− subscribeEvent :: Event t a -> m (EventHandle t a)
96−
97−-- | Monad that allows to read events' values.
98−class (ReflexHost t, Applicative m, Monad m) => MonadReadEvent t m | m -> t where
99− -- | Read the value of an 'Event' from an 'EventHandle' (created by calling
100− -- 'subscribeEvent').
101− --
102− -- After event propagation is done, all events can be in two states: either
103− -- they are firing with some value or they are not firing. In the former
104− -- case, this function returns @Just act@, where @act@ in an action to read
105− -- the current value of the event. In the latter case, the function returns
106− -- @Nothing@.
107− --
108− -- This function is normally used in the calllback for 'fireEventsAndRead'.
109− readEvent :: EventHandle t a -> m (Maybe (m a))
110−
111−-- | A monad where new events feed from external sources can be created.
112−class (Applicative m, Monad m) => MonadReflexCreateTrigger t m | m -> t where
113− -- | Creates a root 'Event' (one that is not based on any other event).
114− --
115− -- When a subscriber first subscribes to an event (building another event that
116− -- depends on the subscription) the given callback function is run and passed
117− -- a trigger. The callback function can then set up the event source in IO.
118− -- After this is done, the callback function must return an accompanying
119− -- teardown action.
120− --
121− -- Any time between setup and teardown the trigger can be used to fire the
122− -- event, by passing it to 'fireEventsAndRead'.
123− --
124− -- Note: An event may be set up multiple times. So after the teardown action
125− -- is executed, the event may still be set up again in the future.
126− newEventWithTrigger :: (EventTrigger t a -> IO (IO ())) -> m (Event t a)
127− newFanEventWithTrigger :: GCompare k => (forall a. k a -> EventTrigger t a -> IO (IO ())) -> m (EventSelector t k)
128−
129−-- | 'MonadReflexHost' designates monads that can run reflex frames.
130−class ( ReflexHost t
131− , MonadReflexCreateTrigger t m
132− , MonadSubscribeEvent t m
133− , MonadReadEvent t (ReadPhase m)
134− , MonadSample t (ReadPhase m)
135− , MonadHold t (ReadPhase m)
136− ) => MonadReflexHost t m | m -> t where
137− type ReadPhase m :: * -> *
138− -- | Propagate some events firings and read the values of events afterwards.
139− --
140− -- This function will create a new frame to fire the given events. It will
141− -- then update all dependent events and behaviors. After that is done, the
142− -- given callback is executed which allows to read the final values of events
143− -- and check whether they have fired in this frame or not.
144− --
145− -- All events that are given are fired at the same time.
146− --
147− -- This function is typically used in the main loop of a reflex framework
148− -- implementation. The main loop waits for external events to happen (such as
149− -- keyboard input or a mouse click) and then fires the corresponding events
150− -- using this function. The read callback can be used to read output events
151− -- and perform a corresponding response action to the external event.
152− fireEventsAndRead :: [DSum (EventTrigger t) Identity] -> ReadPhase m a -> m a
153−
154− -- | Run a frame without any events firing.
155− --
156− -- This function should be used when you want to use 'sample' and 'hold' when
157− -- no events are currently firing. Using this function in that case can
158− -- improve performance, since the implementation can assume that no events are
159− -- firing when 'sample' or 'hold' are called.
160− --
161− -- This function is commonly used to set up the basic event network when the
162− -- application starts up.
163− runHostFrame :: HostFrame t a -> m a
164−
165−-- | Like 'fireEventsAndRead', but without reading any events.
166−fireEvents :: MonadReflexHost t m => [DSum (EventTrigger t) Identity] -> m ()
167−fireEvents dm = fireEventsAndRead dm $ return ()
168−{-# INLINE fireEvents #-}
169−
170−-- | Create a new event and store its trigger in an 'IORef' while it's active.
171−--
172−-- An event is only active between the set up (when it's first subscribed to)
173−-- and the teardown phases (when noboby is subscribing the event anymore). This
174−-- function returns an Event and an 'IORef'. As long as the event is active, the
175−-- 'IORef' will contain 'Just' the event trigger to trigger this event. When the
176−-- event is not active, the 'IORef' will contain 'Nothing'. This allows event
177−-- sources to be more efficient, since they don't need to produce events when
178−-- nobody is listening.
179−newEventWithTriggerRef :: (MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO) => m (Event t a, Ref m (Maybe (EventTrigger t a)))
180−newEventWithTriggerRef = do
181− rt <- newRef Nothing
182− e <- newEventWithTrigger $ \t -> do
183− writeRef rt $ Just t
184− return $ writeRef rt Nothing
185− return (e, rt)
186−{-# INLINE newEventWithTriggerRef #-}
187−
188−-- | Fire the given trigger if it is not 'Nothing'.
189−fireEventRef :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
190−fireEventRef mtRef input = do
191− mt <- readRef mtRef
192− case mt of
193− Nothing -> return ()
194− Just trigger -> fireEvents [trigger :=> Identity input]
195−
196−-- | Fire the given trigger if it is not 'Nothing', and read from the given
197−-- 'EventHandle'.
198−fireEventRefAndRead :: (MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO) => Ref m (Maybe (EventTrigger t a)) -> a -> EventHandle t b -> m (Maybe b)
199−fireEventRefAndRead mtRef input e = do
200− mt <- readRef mtRef
201− case mt of
202− Nothing -> return Nothing -- Since we aren't firing the input, the output can't fire
203− Just trigger -> fireEventsAndRead [trigger :=> Identity input] $ do
204− mGetValue <- readEvent e
205− case mGetValue of
206− Nothing -> return Nothing
207− Just getValue -> fmap Just getValue
208−
209−
210−--------------------------------------------------------------------------------
211−-- Instances
212−--------------------------------------------------------------------------------
213−
214−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ReaderT r m) where
215− newEventWithTrigger = lift . newEventWithTrigger
216− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
217−
218−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ReaderT r m) where
219− subscribeEvent = lift . subscribeEvent
220−
221−instance MonadReflexHost t m => MonadReflexHost t (ReaderT r m) where
222− type ReadPhase (ReaderT r m) = ReadPhase m
223− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
224− runHostFrame = lift . runHostFrame
225−
226−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (WriterT w m) where
227− newEventWithTrigger = lift . newEventWithTrigger
228− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
229−
230−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (WriterT w m) where
231− subscribeEvent = lift . subscribeEvent
232−
233−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (WriterT w m) where
234− type ReadPhase (WriterT w m) = ReadPhase m
235− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
236− runHostFrame = lift . runHostFrame
237−
238−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StateT s m) where
239− newEventWithTrigger = lift . newEventWithTrigger
240− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
241−
242−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (StateT r m) where
243− subscribeEvent = lift . subscribeEvent
244−
245−instance MonadReflexHost t m => MonadReflexHost t (StateT s m) where
246− type ReadPhase (StateT s m) = ReadPhase m
247− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
248− runHostFrame = lift . runHostFrame
249−
250−
251−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Strict.StateT s m) where
252− newEventWithTrigger = lift . newEventWithTrigger
253− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
254−
255−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (Strict.StateT r m) where
256− subscribeEvent = lift . subscribeEvent
257−
258−instance MonadReflexHost t m => MonadReflexHost t (Strict.StateT s m) where
259− type ReadPhase (Strict.StateT s m) = ReadPhase m
260− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
261− runHostFrame = lift . runHostFrame
262−
263−
264−
265−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ContT r m) where
266− newEventWithTrigger = lift . newEventWithTrigger
267− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
268−
269−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ContT r m) where
270− subscribeEvent = lift . subscribeEvent
271−
272−instance MonadReflexHost t m => MonadReflexHost t (ContT r m) where
273− type ReadPhase (ContT r m) = ReadPhase m
274− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
275− runHostFrame = lift . runHostFrame
276−
277−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ExceptT e m) where
278− newEventWithTrigger = lift . newEventWithTrigger
279− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
280−
281−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (ExceptT r m) where
282− subscribeEvent = lift . subscribeEvent
283−
284−instance MonadReflexHost t m => MonadReflexHost t (ExceptT e m) where
285− type ReadPhase (ExceptT e m) = ReadPhase m
286− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
287− runHostFrame = lift . runHostFrame
288−
289−instance (MonadReflexCreateTrigger t m, Monoid w) => MonadReflexCreateTrigger t (RWST r w s m) where
290− newEventWithTrigger = lift . newEventWithTrigger
291− newFanEventWithTrigger initializer = lift $ newFanEventWithTrigger initializer
292−
293−instance (MonadSubscribeEvent t m, Monoid w) => MonadSubscribeEvent t (RWST r w s m) where
294− subscribeEvent = lift . subscribeEvent
295−
296−instance (MonadReflexHost t m, Monoid w) => MonadReflexHost t (RWST r w s m) where
297− type ReadPhase (RWST r w s m) = ReadPhase m
298− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
299− runHostFrame = lift . runHostFrame
src/Reflex/Optimizer.hsdeleted−43
@@ -1,43 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE LambdaCase #-}
3−{-# LANGUAGE OverloadedStrings #-}
4−module Reflex.Optimizer (plugin) where
5−
6−#ifdef ghcjs_HOST_OS
7−import Plugins
8−#else
9−import Control.Arrow
10−import CoreMonad
11−import Data.String
12−import GhcPlugins
13−#endif
14−
15−#ifdef ghcjs_HOST_OS
16−
17−plugin :: Plugin
18−plugin = undefined
19−
20−#else
21−
22−plugin :: Plugin
23−plugin = defaultPlugin { installCoreToDos = install }
24−
25−install :: [CommandLineOption] -> [CoreToDo] -> CoreM [CoreToDo]
26−install [] p = return $ makeInlinable : p
27−install options@(_:_) p = do
28− warnMsg $ "Reflex.Optimizer: ignoring " <> fromString (show $ length options) <> " command-line options"
29− install [] p
30−
31−makeInlinable :: CoreToDo
32−makeInlinable = CoreDoPluginPass "MakeInlinable" $ \modGuts -> do
33− let f v = setIdInfo v $ let i = idInfo v in
34− setInlinePragInfo i $ let p = inlinePragInfo i in
35− if isDefaultInlinePragma p
36− then defaultInlinePragma { inl_inline = Inlinable }
37− else p
38− newBinds = flip map (mg_binds modGuts) $ \case
39− NonRec b e -> NonRec (f b) e
40− Rec bes -> Rec $ map (first f) bes
41− return $ modGuts { mg_binds = newBinds }
42−
43−#endif
src/Reflex/Patch.hsdeleted−92
@@ -1,92 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DeriveFunctor #-}
3−{-# LANGUAGE LambdaCase #-}
4−{-# LANGUAGE StandaloneDeriving #-}
5−{-# LANGUAGE TypeFamilies #-}
6−module Reflex.Patch
7− ( Patch (..)
8− , PatchDMap (..)
9− , ComposeMaybe (..)
10− , PatchMap (..)
11− ) where
12−
13−import Control.Monad.Identity
14−import Data.Dependent.Map (DMap, GCompare (..))
15−import qualified Data.Dependent.Map as DMap
16−import Data.Functor.Constant
17−import Data.Map (Map)
18−import qualified Data.Map as Map
19−import Data.Semigroup (Semigroup (..), stimesIdempotentMonoid, (<>))
20−
21−-- | A 'Patch' type represents a kind of change made to a datastructure.
22−class Patch p where
23− type PatchTarget p :: *
24− -- | Apply the patch @p a@ to the value @a@. If no change is needed, return
25− -- 'Nothing'.
26− apply :: p -> PatchTarget p -> Maybe (PatchTarget p)
27−
28−-- | We can't use @Compose Maybe@ instead of 'ComposeMaybe', because that would
29−-- make the 'f' parameter have a nominal type role. We need f to be
30−-- representational so that we can use safe 'coerce'.
31−newtype ComposeMaybe f a = ComposeMaybe { getComposeMaybe :: Maybe (f a) }
32−
33−deriving instance Functor f => Functor (ComposeMaybe f)
34−
35−instance Patch (Identity a) where
36− type PatchTarget (Identity a) = a
37− apply (Identity a) _ = Just a
38−
39−-- | A set of changes to a 'DMap'. Only DMap types are allowed for @a@.
40−newtype PatchDMap k v = PatchDMap (DMap k (ComposeMaybe v))
41−
42−instance GCompare k => Semigroup (PatchDMap k v) where
43− PatchDMap a <> PatchDMap b = PatchDMap $ a `mappend` b --TODO: Add a semigroup instance for DMap
44− -- PatchDMap is idempotent, so stimes n is id for every n
45−#if MIN_VERSION_semigroups(0,17,0)
46− stimes = stimesIdempotentMonoid
47−#else
48− times1p n x = case compare n 0 of
49− LT -> error "stimesIdempotentMonoid: negative multiplier"
50− EQ -> mempty
51− GT -> x
52−#endif
53−
54−instance GCompare k => Monoid (PatchDMap k v) where
55− mempty = PatchDMap mempty
56− mappend = (<>)
57−
58−instance GCompare k => Patch (PatchDMap k v) where
59− type PatchTarget (PatchDMap k v) = DMap k v
60− 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?
61− where insertions = DMap.mapMaybeWithKey (const $ getComposeMaybe) diff
62− deletions = DMap.mapMaybeWithKey (const $ nothingToJust . getComposeMaybe) diff
63− nothingToJust = \case
64− Nothing -> Just $ Constant ()
65− Just _ -> Nothing
66−
67−newtype PatchMap k v = PatchMap (Map k (Maybe v))
68−
69−instance Ord k => Patch (PatchMap k v) where
70− type PatchTarget (PatchMap k v) = Map k v
71− 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?
72− where insertions = Map.mapMaybeWithKey (const id) p
73− deletions = Map.mapMaybeWithKey (const nothingToJust) p
74− nothingToJust = \case
75− Nothing -> Just ()
76− Just _ -> Nothing
77−
78−instance Ord k => Semigroup (PatchMap k v) where
79− PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
80− -- PatchMap is idempotent, so stimes n is id for every n
81−#if MIN_VERSION_semigroups(0,17,0)
82− stimes = stimesIdempotentMonoid
83−#else
84− times1p n x = case compare n 0 of
85− LT -> error "stimesIdempotentMonoid: negative multiplier"
86− EQ -> mempty
87− GT -> x
88−#endif
89−
90−instance Ord k => Monoid (PatchMap k v) where
91− mempty = PatchMap mempty
92− mappend = (<>)
src/Reflex/PerformEvent/Base.hsdeleted−321
@@ -1,321 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7−{-# LANGUAGE MultiParamTypeClasses #-}
8−{-# LANGUAGE PartialTypeSignatures #-}
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−module Reflex.PerformEvent.Base where
21−
22−import Reflex.Class
23−import Reflex.Host.Class
24−import Reflex.PerformEvent.Class
25−import Reflex.PostBuild.Class
26−
27−import Control.Lens
28−import Control.Monad.Exception
29−import Control.Monad.Identity
30−import Control.Monad.Primitive
31−import Control.Monad.Reader
32−import Control.Monad.Ref
33−import Control.Monad.State.Strict
34−import Data.Coerce
35−import Data.Dependent.Map (DMap, GCompare (..), Some)
36−import qualified Data.Dependent.Map as DMap
37−import Data.Dependent.Sum
38−import Data.Functor.Misc
39−import qualified Data.List.NonEmpty as NonEmpty
40−import Data.Maybe
41−import Data.Semigroup
42−import qualified Data.Some as Some
43−import Data.These
44−import Data.Tuple
45−import Data.Unique.Tag
46−
47−newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
48−
49−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?
50−
51−newtype PerformEventT t m a = PerformEventT { unPerformEventT :: RequestT t (HostFrame t) Identity (HostFrame t) a }
52−
53−deriving instance ReflexHost t => Functor (PerformEventT t m)
54−deriving instance ReflexHost t => Applicative (PerformEventT t m)
55−deriving instance ReflexHost t => Monad (PerformEventT t m)
56−deriving instance ReflexHost t => MonadFix (PerformEventT t m)
57−deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
58−deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
59−
60−instance (PrimMonad (HostFrame t), ReflexHost t) => PrimMonad (PerformEventT t m) where
61− type PrimState (PerformEventT t m) = PrimState (HostFrame t)
62− primitive = PerformEventT . lift . primitive
63−
64−instance (ReflexHost t, Ref m ~ Ref IO, PrimMonad (HostFrame t)) => PerformEvent t (PerformEventT t m) where
65− type Performable (PerformEventT t m) = HostFrame t
66− {-# INLINABLE performEvent_ #-}
67− performEvent_ = PerformEventT . requesting_
68− {-# INLINABLE performEvent #-}
69− performEvent = PerformEventT . fmap (fmap runIdentity) . requesting
70−
71−instance (ReflexHost t, PrimMonad (HostFrame t)) => MonadAdjust t (PerformEventT t m) where
72− runWithReplace outerA0 outerA' = PerformEventT $ runWithReplaceRequestTWith f (coerce outerA0) (coerceEvent outerA')
73− where f :: HostFrame t a -> Event t (HostFrame t b) -> RequestT t (HostFrame t) Identity (HostFrame t) (a, Event t b)
74− f a0 a' = do
75− result0 <- lift a0
76− result' <- requesting a'
77− return (result0, fmap runIdentity result')
78− sequenceDMapWithAdjust (outerDm0 :: DMap k (PerformEventT t m)) outerDm' = PerformEventT $ sequenceDMapWithAdjustRequestTWith f (coerce outerDm0) (coerceEvent outerDm')
79− where f :: DMap k' (HostFrame t) -> Event t (PatchDMap k' (HostFrame t)) -> RequestT t (HostFrame t) Identity (HostFrame t) (DMap k' Identity, Event t (PatchDMap k' Identity))
80− f dm0 dm' = do
81− result0 <- lift $ DMap.traverseWithKey (\_ v -> Identity <$> v) dm0
82− result' <- requesting $ ffor dm' $ \(PatchDMap p) -> do
83− PatchDMap <$> DMap.traverseWithKey (\_ (ComposeMaybe mv) -> ComposeMaybe <$> mapM (fmap Identity) mv) p
84− return (result0, fmap runIdentity result')
85−
86−instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
87− {-# INLINABLE newEventWithTrigger #-}
88− newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
89− {-# INLINABLE newFanEventWithTrigger #-}
90− newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
91−
92−{-# INLINABLE hostPerformEventT #-}
93−hostPerformEventT :: forall t m a.
94− ( Monad m
95− , MonadSubscribeEvent t m
96− , MonadReflexHost t m
97− , MonadRef m
98− , Ref m ~ Ref IO
99− )
100− => PerformEventT t m a
101− -> m (a, FireCommand t m)
102−hostPerformEventT a = do
103− (response, responseTrigger) <- newEventWithTriggerRef
104− (result, eventToPerform) <- runHostFrame $ runRequestT (unPerformEventT a) response
105− eventToPerformHandle <- subscribeEvent eventToPerform
106− return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
107− let go :: [DSum (EventTrigger t) Identity] -> m [a']
108− go ts = do
109− (result', mToPerform) <- fireEventsAndRead ts $ do
110− mToPerform <- sequence =<< readEvent eventToPerformHandle
111− result' <- readPhase
112− return (result', mToPerform)
113− case mToPerform of
114− Nothing -> return [result']
115− Just toPerform -> do
116− responses <- runHostFrame $ DMap.traverseWithKey (\_ v -> Identity <$> v) toPerform
117− mrt <- readRef responseTrigger
118− let followupEventTriggers = case mrt of
119− Just rt -> [rt :=> Identity responses]
120− Nothing -> []
121− (result':) <$> go followupEventTriggers
122− go triggers
123−
124−instance ReflexHost t => MonadSample t (PerformEventT t m) where
125− {-# INLINABLE sample #-}
126− sample = PerformEventT . lift . sample
127−
128−instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
129− {-# INLINABLE hold #-}
130− hold v0 v' = PerformEventT $ lift $ hold v0 v'
131− {-# INLINABLE holdDyn #-}
132− holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
133− {-# INLINABLE holdIncremental #-}
134− holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
135−
136−instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
137− type Ref (PerformEventT t m) = Ref (HostFrame t)
138− {-# INLINABLE newRef #-}
139− newRef = PerformEventT . lift . newRef
140− {-# INLINABLE readRef #-}
141− readRef = PerformEventT . lift . readRef
142− {-# INLINABLE writeRef #-}
143− writeRef r = PerformEventT . lift . writeRef r
144−
145−instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
146− {-# INLINABLE atomicModifyRef #-}
147− atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
148−
149−--------------------------------------------------------------------------------
150−-- RequestT
151−--------------------------------------------------------------------------------
152−
153−newtype RequestT t request response m a
154− = RequestT { unRequestT :: StateT [Event t (DMap (Tag (PrimState m)) request)] (ReaderT (EventSelector t (WrapArg response (Tag (PrimState m)))) m) a }
155− deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException)
156−
157−instance MonadTrans (RequestT t request response) where
158− lift = RequestT . lift . lift
159−
160−runRequestT :: forall t m a request response.
161− ( Reflex t
162− , Monad m
163− )
164− => RequestT t request response m a
165− -> Event t (DMap (Tag (PrimState m)) response)
166− -> m (a, Event t (DMap (Tag (PrimState m)) request))
167−runRequestT (RequestT a) responses = do
168− (result, requests) <- runReaderT (runStateT a mempty) $ fan $ mapKeyValuePairsMonotonic (\(t :=> e) -> WrapArg t :=> Identity e) <$> responses
169− return (result, mergeWith DMap.union requests)
170−
171−instance (Reflex t, PrimMonad m) => MonadRequest t (RequestT t request response m) where
172− type Request (RequestT t request response m) = request
173− type Response (RequestT t request response m) = response
174− withRequesting a = do
175− t <- lift newTag
176− s <- RequestT ask
177− (req, result) <- a $ select s $ WrapArg t
178− RequestT $ modify $ (:) $ DMap.singleton t <$> req
179− return result
180−
181−data DMapTransform (a :: *) k k' v v' = forall (b :: *). DMapTransform !(k a -> k' b) !(v a -> v' b)
182−
183−mapPatchKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> PatchDMap k v -> PatchDMap k' v'
184−mapPatchKeysAndValuesMonotonic x (PatchDMap p) = PatchDMap $ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> case x of DMapTransform f g -> f k :=> ComposeMaybe (fmap g mv)) p
185−
186−mapKeysAndValuesMonotonic :: (forall a. DMapTransform a k k' v v') -> DMap k v -> DMap k' v'
187−mapKeysAndValuesMonotonic x = mapKeyValuePairsMonotonic $ \(k :=> v) -> case x of
188− DMapTransform f g -> f k :=> g v
189−
190−instance MonadSample t m => MonadSample t (RequestT t request response m) where
191− sample = lift . sample
192−
193−instance MonadHold t m => MonadHold t (RequestT t request response m) where
194− hold v0 = lift . hold v0
195− holdDyn v0 = lift . holdDyn v0
196− holdIncremental v0 = lift . holdIncremental v0
197−
198−instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (RequestT t request response m) where
199− runWithReplace = runWithReplaceRequestTWith $ \dm0 dm' -> lift $ runWithReplace dm0 dm'
200− sequenceDMapWithAdjust = sequenceDMapWithAdjustRequestTWith $ \dm0 dm' -> lift $ sequenceDMapWithAdjust dm0 dm'
201−
202−runWithReplaceRequestTWith :: forall m t request response a b. (Reflex t, MonadHold t m)
203− => (forall a' b'.
204− m a'
205− -> Event t (m b')
206− -> RequestT t request response m (a', Event t b')
207− )
208− -> RequestT t request response m a
209− -> Event t (RequestT t request response m b)
210− -> RequestT t request response m (a, Event t b)
211−runWithReplaceRequestTWith f a0 a' = do
212− response <- RequestT ask
213− let g :: RequestT t request response m c -> m (c, [Event t (DMap (Tag (PrimState m)) request)])
214− g (RequestT r) = runReaderT (runStateT r mempty) response
215− (result0, result') <- f (g a0) $ g <$> a'
216− request <- holdDyn (fmap (mconcat . NonEmpty.toList) $ mergeList $ snd result0) $ fmap (mconcat . NonEmpty.toList) . mergeList . snd <$> result'
217− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
218− RequestT $ modify $ (:) $ coincidence $ updated request
219− RequestT $ modify $ (:) $ switch $ current request
220− return (fst result0, fst <$> result')
221−
222−sequenceDMapWithAdjustRequestTWith :: (GCompare k, Reflex t, MonadHold t m)
223− => (forall k'. GCompare k'
224− => DMap k' m
225− -> Event t (PatchDMap k' m)
226− -> RequestT t request response m (DMap k' Identity, Event t (PatchDMap k' Identity))
227− )
228− -> DMap k (RequestT t request response m)
229− -> Event t (PatchDMap k (RequestT t request response m))
230− -> RequestT t request response m (DMap k Identity, Event t (PatchDMap k Identity))
231−sequenceDMapWithAdjustRequestTWith f (dm0 :: DMap k (RequestT t request response m)) dm' = do
232− response <- RequestT ask
233− let inputTransform :: forall a. DMapTransform a k (WrapArg ((,) [Event t (DMap (Tag (PrimState m)) request)]) k) (RequestT t request response m) m
234− inputTransform = DMapTransform WrapArg (\(RequestT v) -> swap <$> runReaderT (runStateT v mempty) response)
235− (children0, children') <- f (mapKeysAndValuesMonotonic inputTransform dm0) $ mapPatchKeysAndValuesMonotonic inputTransform <$> dm'
236− let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, v)) -> k :=> Identity v) children0
237− result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
238− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> k :=> ComposeMaybe (fmap (Identity . snd . runIdentity) mv)) p
239− requests0 :: DMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t)
240− requests0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (r, _)) -> Const2 (Some.This k) :=> mergeWith DMap.union r) children0
241− requests' :: Event t (PatchDMap (Const2 (Some k) (DMap (Tag (PrimState m)) request)) (Event t))
242− requests' = ffor children' $ \(PatchDMap p) -> PatchDMap $
243− mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> Const2 (Some.This k) :=> ComposeMaybe (fmap (mergeWith DMap.union . fst . runIdentity) mv)) p
244− childRequestMap <- holdIncremental requests0 requests'
245− -- We add these two separately to take advantage of the free merge being done later. The coincidence case must come first so that it has precedence if both fire simultaneously. (Really, we should probably block the 'switch' whenever 'updated' fires, but switchPromptlyDyn has the same issue.)
246− RequestT $ modify $ (:) $ coincidence $ ffor requests' $ \(PatchDMap p) -> mergeWith DMap.union $ catMaybes $ ffor (DMap.toList p) $ \(Const2 _ :=> ComposeMaybe me) -> me
247− RequestT $ modify $ (:) $ ffor (mergeIncremental childRequestMap) $ \m ->
248− mconcat $ (\(Const2 _ :=> Identity reqs) -> reqs) <$> DMap.toList m
249− return (result0, result')
250−
251−instance PerformEvent t m => PerformEvent t (RequestT t request response m) where
252− type Performable (RequestT t request response m) = Performable m
253− performEvent_ = lift . performEvent_
254− performEvent = lift . performEvent
255−
256−instance PostBuild t m => PostBuild t (RequestT t request response m) where
257− getPostBuild = lift getPostBuild
258−
259−instance TriggerEvent t m => TriggerEvent t (RequestT t request response m) where
260− newTriggerEvent = lift newTriggerEvent
261− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
262− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
263−
264−instance MonadReader r m => MonadReader r (RequestT t request response m) where
265− ask = lift ask
266− local f (RequestT a) = RequestT $ mapStateT (mapReaderT $ local f) a
267− reader = lift . reader
268−
269−{-
270−withRequestT :: forall t m a (req :: * -> *) (req' :: * -> *).
271− (Reflex t, MonadHold t m, MonadFix m)
272− => (forall x. request x -> request' x)
273− -> (forall x. response' x -> response x)
274− -> RequestT t request response m a
275− -> RequestT t request' response m a
276−withRequestT f g m = RequestT $ hoist (hoist (withDynamicWriterT f')) (unRequestT m)
277− where
278− f' :: Event t [(y, Some request)] -> Event t [(y, Some request')]
279− f' = fmap . fmap . fmap $ \(SomeRequest req) -> SomeRequest $ f req
280−-}
281−
282−instance MonadRef m => MonadRef (RequestT t request response m) where
283− type Ref (RequestT t request response m) = Ref m
284− newRef = lift . newRef
285− readRef = lift . readRef
286− writeRef r = lift . writeRef r
287−
288−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (RequestT t request response m) where
289− newEventWithTrigger = lift . newEventWithTrigger
290− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
291−
292−
293−--------------------------------------------------------------------------------
294−-- Helpers
295−--------------------------------------------------------------------------------
296−
297−{-# INLINABLE numberWith #-}
298−numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
299−numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
300−
301−{-# INLINABLE mergeSelfDeletingEvents #-}
302−mergeSelfDeletingEvents :: forall t m k v. (Ord k, Reflex t, MonadFix m, MonadHold t m)
303− => DMap (Const2 k (These v ())) (Event t)
304− -> Event t (PatchDMap (Const2 k (These v ())) (Event t))
305− -> m (Event t [v])
306−mergeSelfDeletingEvents initial eAddNew = do
307− rec events :: Incremental t (PatchDMap (Const2 k (These v ())) (Event t))
308− <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
309− let event = mergeWith DMap.union
310− [ mergeIncremental events
311− , coincidence $ ffor eAddNew $ \(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getComposeMaybe) m
312− ]
313− eDelete :: Event t (PatchDMap (Const2 k (These v ())) (Event t))
314− eDelete = fforMaybe event $
315− fmap (PatchDMap . DMap.fromDistinctAscList) .
316− (\l -> if null l then Nothing else Just l) .
317− fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> ComposeMaybe Nothing) <$ x ^? there) .
318− DMap.toList
319− return $ ffor event $
320− fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
321− DMap.toList
src/Reflex/PerformEvent/Class.hsdeleted−93
@@ -1,93 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE FunctionalDependencies #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE ScopedTypeVariables #-}
7−{-# LANGUAGE TypeFamilies #-}
8−{-# LANGUAGE TypeOperators #-}
9−{-# LANGUAGE UndecidableInstances #-}
10−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
11−module Reflex.PerformEvent.Class where
12−
13−import Reflex.Class
14−
15−import Control.Monad.Identity
16−import Control.Monad.Reader
17−import qualified Control.Monad.State.Lazy as Lazy
18−import Control.Monad.State.Strict
19−
20−class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
21− type Performable m :: * -> *
22− performEvent :: Event t (Performable m a) -> m (Event t a)
23− performEvent_ :: Event t (Performable m ()) -> m ()
24−
25−--TODO: Shouldn't have IO hard-coded
26−class Monad m => TriggerEvent t m | m -> t where
27− newTriggerEvent :: m (Event t a, a -> IO ())
28− newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
29− newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
30−
31−class (Reflex t, Monad m) => MonadRequest t m | m -> t where
32− type Request m :: * -> *
33− type Response m :: * -> *
34− withRequesting :: (Event t (Response m a) -> m (Event t (Request m a), r)) -> m r
35−
36−{-# INLINABLE performEventAsync #-}
37−performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
38−performEventAsync e = do
39− (eOut, triggerEOut) <- newTriggerEvent
40− performEvent_ $ fmap ($ triggerEOut) e
41− return eOut
42−
43−instance PerformEvent t m => PerformEvent t (ReaderT r m) where
44− type Performable (ReaderT r m) = ReaderT r (Performable m)
45− performEvent_ e = do
46− r <- ask
47− lift $ performEvent_ $ flip runReaderT r <$> e
48− performEvent e = do
49− r <- ask
50− lift $ performEvent $ flip runReaderT r <$> e
51−
52−instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
53− newTriggerEvent = lift newTriggerEvent
54− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
55− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
56−
57−instance MonadRequest t m => MonadRequest t (ReaderT r m) where
58− type Request (ReaderT r m) = Request m
59− type Response (ReaderT r m) = Response m
60− withRequesting f = do
61− r <- ask
62− lift $ withRequesting $ (`runReaderT` r) . f
63−
64−instance MonadRequest t m => MonadRequest t (StateT s m) where
65− type Request (StateT s m) = Request m
66− type Response (StateT s m) = Response m
67− withRequesting f = do
68− old <- get
69− (result, new) <- lift $ withRequesting $ \x -> do
70− ((request, result), new) <- runStateT (f x) old
71− return (request, (result, new))
72− put new
73− return result
74−
75−instance MonadRequest t m => MonadRequest t (Lazy.StateT s m) where
76− type Request (Lazy.StateT s m) = Request m
77− type Response (Lazy.StateT s m) = Response m
78− withRequesting f = do
79− old <- Lazy.get
80− (result, new) <- lift $ withRequesting $ \x -> do
81− ((request, result), new) <- Lazy.runStateT (f x) old
82− return (request, (result, new))
83− Lazy.put new
84− return result
85−
86−requesting :: MonadRequest t m => Event t (Request m a) -> m (Event t (Response m a))
87−requesting req = withRequesting $ return . (,) req
88−
89−requesting_ :: MonadRequest t m => Event t (Request m a) -> m ()
90−requesting_ req = withRequesting $ \_ -> return (req, ())
91−
92−requestingIdentity :: (MonadRequest t m, Response m ~ Identity) => Event t (Request m a) -> m (Event t a)
93−requestingIdentity = fmap (fmap runIdentity) . requesting
src/Reflex/PostBuild/Class.hsdeleted−114
@@ -1,114 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE DataKinds #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE FunctionalDependencies #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RecursiveDo #-}
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 where
16−
17−import Control.Monad.Exception
18−import Control.Monad.Primitive
19−import Control.Monad.Reader
20−import Control.Monad.Ref
21−import Control.Monad.Trans.Control
22−import qualified Data.Dependent.Map as DMap
23−import Reflex.Class
24−import Reflex.Host.Class
25−import Reflex.PerformEvent.Class
26−
27−class (Reflex t, Monad m) => PostBuild t m | m -> t where
28− getPostBuild :: m (Event t ())
29−
30−newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
31−
32−instance PrimMonad m => PrimMonad (PostBuildT x m) where
33− type PrimState (PostBuildT x m) = PrimState m
34− primitive = lift . primitive
35−
36−instance MonadTransControl (PostBuildT t) where
37− type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
38− {-# INLINABLE liftWith #-}
39− liftWith = defaultLiftWith PostBuildT unPostBuildT
40− {-# INLINABLE restoreT #-}
41− restoreT = defaultRestoreT PostBuildT
42−
43−instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
44− {-# INLINABLE getPostBuild #-}
45− getPostBuild = PostBuildT ask
46−
47−instance MonadSample t m => MonadSample t (PostBuildT t m) where
48− {-# INLINABLE sample #-}
49− sample = lift . sample
50−
51−instance MonadHold t m => MonadHold t (PostBuildT t m) where
52− {-# INLINABLE hold #-}
53− hold v0 = lift . hold v0
54− {-# INLINABLE holdDyn #-}
55− holdDyn v0 = lift . holdDyn v0
56− {-# INLINABLE holdIncremental #-}
57− holdIncremental v0 = lift . holdIncremental v0
58−
59−instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
60− type Performable (PostBuildT t m) = PostBuildT t (Performable m)
61− {-# INLINABLE performEvent_ #-}
62− performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
63− {-# INLINABLE performEvent #-}
64− performEvent e = liftWith $ \run -> performEvent $ fmap run e
65−
66−instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
67− {-# INLINABLE newEventWithTrigger #-}
68− newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
69− {-# INLINABLE newFanEventWithTrigger #-}
70− newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
71−
72−instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
73− {-# INLINABLE newTriggerEvent #-}
74− newTriggerEvent = lift newTriggerEvent
75− {-# INLINABLE newTriggerEventWithOnComplete #-}
76− newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
77− newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
78−
79−instance MonadRef m => MonadRef (PostBuildT t m) where
80− type Ref (PostBuildT t m) = Ref m
81− {-# INLINABLE newRef #-}
82− newRef = lift . newRef
83− {-# INLINABLE readRef #-}
84− readRef = lift . readRef
85− {-# INLINABLE writeRef #-}
86− writeRef r = lift . writeRef r
87−
88−instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
89− {-# INLINABLE atomicModifyRef #-}
90− atomicModifyRef r = lift . atomicModifyRef r
91−
92−{-# INLINABLE runPostBuildT #-}
93−runPostBuildT :: PostBuildT t m a -> Event t () -> m a
94−runPostBuildT (PostBuildT a) = runReaderT a
95−
96−instance PostBuild t m => PostBuild t (ReaderT r m) where
97− getPostBuild = lift getPostBuild
98−
99−instance (Reflex t, MonadHold t m, MonadFix m, MonadAdjust t m, PerformEvent t m) => MonadAdjust t (PostBuildT t m) where
100− runWithReplace a0 a' = do
101− postBuild <- getPostBuild
102− lift $ do
103− rec result@(_, result') <- runWithReplace (runPostBuildT a0 postBuild) $ fmap (\v -> runPostBuildT v =<< headE voidResult') a'
104− let voidResult' = void result'
105− return result
106− sequenceDMapWithAdjust dm0 dm' = do
107− postBuild <- getPostBuild
108− let loweredDm0 = DMap.map (`runPostBuildT` postBuild) dm0
109− lift $ do
110− rec (result0, result') <- sequenceDMapWithAdjust loweredDm0 loweredDm'
111− let voidResult' = void result'
112− let loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
113− DMap.map (ComposeMaybe . fmap (\v -> runPostBuildT v =<< headE voidResult') . getComposeMaybe) p --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
114− return (result0, result')
src/Reflex/Pure.hsdeleted−174
@@ -1,174 +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 where
22−
23−#if !MIN_VERSION_base(4,8,0)
24−import Control.Applicative
25−#endif
26−
27−import Control.Monad
28−import Data.Dependent.Map (DMap, GCompare)
29−import qualified Data.Dependent.Map as DMap
30−import Data.Functor.Identity
31−import Data.Maybe
32−import Data.MemoTrie
33−import Data.Monoid
34−import Data.Type.Coercion
35−import Reflex.Class
36−
37−data Pure t
38−
39−-- | The Enum instance of t must be dense: for all x :: t, there must not exist
40−-- any y :: t such that pred x < y < x. The HasTrie instance will be used
41−-- exclusively to memoize functions of t, not for any of its other capabilities.
42−instance (Enum t, HasTrie t, Ord t) => Reflex (Pure t) where
43−
44− newtype Behavior (Pure t) a = Behavior { unBehavior :: t -> a }
45− newtype Event (Pure t) a = Event { unEvent :: t -> Maybe a }
46− newtype Dynamic (Pure t) a = Dynamic { unDynamic :: t -> (a, Maybe a) }
47− newtype Incremental (Pure t) p = Incremental { unIncremental :: t -> (PatchTarget p, Maybe p) }
48−
49− type PushM (Pure t) = (->) t
50− type PullM (Pure t) = (->) t
51−
52− never :: Event (Pure t) a
53− never = Event $ \_ -> Nothing
54−
55− constant :: a -> Behavior (Pure t) a
56− constant x = Behavior $ \_ -> x
57−
58− push :: (a -> PushM (Pure t) (Maybe b)) -> Event (Pure t) a -> Event (Pure t) b
59− push f e = Event $ memo $ \t -> unEvent e t >>= \o -> f o t
60−
61− pull :: PullM (Pure t) a -> Behavior (Pure t) a
62− pull = Behavior . memo
63−
64− -- [UNUSED_CONSTRAINT]: The following type signature for merge will produce a
65− -- warning because the GCompare instance is not used; however, removing the
66− -- GCompare instance produces a different warning, due to that constraint
67− -- being present in the original class definition
68−
69− --merge :: GCompare k => DMap k (Event (Pure t)) -> Event (Pure t) (DMap k Identity)
70− merge events = Event $ memo $ \t ->
71− let currentOccurrences = DMap.mapMaybeWithKey (\_ (Event a) -> Identity <$> a t) events
72− in if DMap.null currentOccurrences
73− then Nothing
74− else Just currentOccurrences
75−
76− fan :: GCompare k => Event (Pure t) (DMap k Identity) -> EventSelector (Pure t) k
77− fan e = EventSelector $ \k -> Event $ \t -> unEvent e t >>= fmap runIdentity . DMap.lookup k
78−
79− switch :: Behavior (Pure t) (Event (Pure t) a) -> Event (Pure t) a
80− switch b = Event $ memo $ \t -> unEvent (unBehavior b t) t
81−
82− coincidence :: Event (Pure t) (Event (Pure t) a) -> Event (Pure t) a
83− coincidence e = Event $ memo $ \t -> unEvent e t >>= \o -> unEvent o t
84−
85− current :: Dynamic (Pure t) a -> Behavior (Pure t) a
86− current d = Behavior $ \t -> fst $ unDynamic d t
87−
88− updated :: Dynamic (Pure t) a -> Event (Pure t) a
89− updated d = Event $ \t -> snd $ unDynamic d t
90−
91− unsafeBuildDynamic :: PullM (Pure t) a -> Event (Pure t) a -> Dynamic (Pure t) a
92− unsafeBuildDynamic readV0 v' = Dynamic $ \t -> (readV0 t, unEvent v' t)
93−
94− -- See UNUSED_CONSTRAINT, above.
95−
96− --unsafeBuildIncremental :: Patch p => PullM (Pure t) a -> Event (Pure t) (p
97− --a) -> Incremental (Pure t) p a
98− unsafeBuildIncremental readV0 p = Incremental $ \t -> (readV0 t, unEvent p t)
99−
100− mergeIncremental (Incremental f) = Event $ \t ->
101− let results = DMap.mapMaybeWithKey (\_ (Event e) -> Identity <$> e t) $ fst $ f t
102− in if DMap.null results
103− then Nothing
104− else Just results
105−
106− currentIncremental (Incremental f) = Behavior $ \t -> fst $ f t
107−
108− updatedIncremental (Incremental f) = Event $ \t -> snd $ f t
109−
110− incrementalToDynamic (Incremental f) = Dynamic $ \t ->
111− let (old, mPatch) = f t
112− e = case mPatch of
113− Nothing -> Nothing
114− Just patch -> apply patch old
115− in (old, e)
116− behaviorCoercion Coercion = Coercion
117− eventCoercion Coercion = Coercion
118− dynamicCoercion Coercion = Coercion
119−
120−instance Functor (Dynamic (Pure t)) where
121− fmap f d = Dynamic $ \t -> let (cur, upd) = unDynamic d t
122− in (f cur, fmap f upd)
123−
124−instance Applicative (Dynamic (Pure t)) where
125− pure a = Dynamic $ \_ -> (a, Nothing)
126− (<*>) = ap
127−
128−instance Monad (Dynamic (Pure t)) where
129− return = pure
130− (x :: Dynamic (Pure t) a) >>= (f :: a -> Dynamic (Pure t) b) = Dynamic $ \t ->
131− let (curX :: a, updX :: Maybe a) = unDynamic x t
132− (cur :: b, updOuter :: Maybe b) = unDynamic (f curX) t
133− (updInner :: Maybe b, updBoth :: Maybe b) = case updX of
134− Nothing -> (Nothing, Nothing)
135− Just nextX -> let (c, u) = unDynamic (f nextX) t
136− in (Just c, u)
137− in (cur, getFirst $ mconcat $ map First [updBoth, updOuter, updInner])
138−
139−instance MonadSample (Pure t) ((->) t) where
140−
141− sample :: Behavior (Pure t) a -> (t -> a)
142− sample = unBehavior
143−
144−instance (Enum t, HasTrie t, Ord t) => MonadHold (Pure t) ((->) t) where
145−
146− hold :: a -> Event (Pure t) a -> t -> Behavior (Pure t) a
147− hold initialValue e initialTime = Behavior f
148− where f = memo $ \sampleTime ->
149− -- Really, the sampleTime should never be prior to the initialTime,
150− -- because that would mean the Behavior is being sampled before
151− -- being created.
152− if sampleTime <= initialTime
153− then initialValue
154− else let lastTime = pred sampleTime
155− in fromMaybe (f lastTime) $ unEvent e lastTime
156−
157− holdDyn :: a -> Event (Pure t) a -> t -> Dynamic (Pure t) a
158− holdDyn initialValue e initialTime =
159− let Behavior f = hold initialValue e initialTime
160− in Dynamic $ \t -> (f t, unEvent e t)
161−
162− holdIncremental :: Patch p => PatchTarget p -> Event (Pure t) p -> t -> Incremental (Pure t) p
163− holdIncremental initialValue e initialTime = Incremental $ \t -> (f t, unEvent e t)
164− where f = memo $ \sampleTime ->
165− -- Really, the sampleTime should never be prior to the initialTime,
166− -- because that would mean the Behavior is being sampled before
167− -- being created.
168− if sampleTime <= initialTime
169− then initialValue
170− else let lastTime = pred sampleTime
171− lastValue = f lastTime
172− in case unEvent e lastTime of
173− Nothing -> lastValue
174− Just x -> fromMaybe lastValue $ apply x lastValue
src/Reflex/Spider.hsdeleted−22
@@ -1,22 +0,0 @@
1−{-# LANGUAGE CPP #-}
2−-- | This module exports all of the user-facing functionality of the 'Spider'
3−-- 'Reflex' engine
4−module Reflex.Spider
5− ( Spider
6− , SpiderTimeline
7− , Global
8− , SpiderHost
9− , runSpiderHost
10−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
11− , runSpiderHostForTimeline
12− , newSpiderTimeline
13− , withSpiderTimeline
14−#endif
15− -- * Deprecated
16− , SpiderEnv
17− ) where
18−
19−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
20−import Reflex.Class
21−#endif
22−import Reflex.Spider.Internal
src/Reflex/Spider/Internal.hsdeleted−2046
@@ -1,2046 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE CPP #-}
3−{-# LANGUAGE ConstraintKinds #-}
4−{-# LANGUAGE EmptyDataDecls #-}
5−{-# LANGUAGE ExistentialQuantification #-}
6−{-# LANGUAGE FlexibleContexts #-}
7−{-# LANGUAGE FlexibleInstances #-}
8−{-# LANGUAGE FunctionalDependencies #-}
9−{-# LANGUAGE GADTs #-}
10−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11−{-# LANGUAGE LambdaCase #-}
12−{-# LANGUAGE MagicHash #-}
13−{-# LANGUAGE MultiParamTypeClasses #-}
14−{-# LANGUAGE MultiWayIf #-}
15−{-# LANGUAGE RankNTypes #-}
16−{-# LANGUAGE RoleAnnotations #-}
17−{-# LANGUAGE ScopedTypeVariables #-}
18−{-# LANGUAGE TypeFamilies #-}
19−{-# LANGUAGE TypeOperators #-}
20−{-# LANGUAGE UndecidableInstances #-}
21−#ifdef USE_REFLEX_OPTIMIZER
22−{-# OPTIONS_GHC -fplugin=Reflex.Optimizer #-}
23−#endif
24−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
25−{-# OPTIONS_GHC -fno-warn-orphans #-}
26−#endif
27−-- | This module is the implementation of the 'Spider' 'Reflex' engine. It uses
28−-- a graph traversal algorithm to propagate 'Event's and 'Behavior's.
29−module Reflex.Spider.Internal where
30−
31−import Control.Applicative
32−import Control.Concurrent
33−import Control.Exception
34−import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
35−import Control.Monad.Exception
36−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence)
37−import Control.Monad.Primitive
38−import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence)
39−import Control.Monad.Ref
40−import Data.Align
41−import Data.Coerce
42−import Data.Dependent.Map (DMap, DSum (..))
43−import qualified Data.Dependent.Map as DMap
44−import Data.Foldable hiding (concat, elem, sequence_)
45−import Data.Function
46−import Data.Functor.Constant
47−import Data.Functor.Misc
48−import Data.Functor.Product
49−import Data.GADT.Compare
50−import Data.IntMap.Strict (IntMap)
51−import qualified Data.IntMap.Strict as IntMap
52−import Data.IORef
53−import Data.List hiding (foldl')
54−import Data.Maybe
55−import Data.Monoid ((<>))
56−import Data.Proxy
57−import Data.These
58−import Data.Traversable
59−import Data.Word
60−import GHC.Exts
61−import GHC.IORef (IORef (..))
62−import GHC.Stack
63−import Reflex.FunctorMaybe
64−import System.IO.Unsafe
65−import System.Mem.Weak
66−import Unsafe.Coerce
67−
68−#ifdef DEBUG_CYCLES
69−import Control.Monad.State hiding (forM, forM_, mapM, mapM_, sequence)
70−import Data.List.NonEmpty (NonEmpty (..), nonEmpty)
71−import qualified Data.List.NonEmpty as NonEmpty
72−import Data.Monoid (mempty)
73−import Data.Tree (Forest, Tree (..), drawForest)
74−#endif
75−
76−import Data.WeakBag (WeakBag, WeakBagTicket)
77−import qualified Data.WeakBag as WeakBag
78−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
79−import Data.Reflection
80−import Data.Some (Some)
81−import qualified Data.Some as Some
82−import Data.Type.Coercion
83−import qualified Reflex.Class
84−import qualified Reflex.Class as R
85−import qualified Reflex.Host.Class
86−#endif
87−import Reflex.Patch
88−
89−-- Note: must come last to silence warnings due to AMP on GHC < 7.10
90−import Prelude hiding (any, concat, mapM, mapM_, sequence)
91−
92−debugPropagate :: Bool
93−
94−debugInvalidateHeight :: Bool
95−
96−debugInvalidate :: Bool
97−
98−#ifdef DEBUG
99−
100−#define DEBUG_NODEIDS
101−
102−debugPropagate = True
103−
104−debugInvalidateHeight = False
105−
106−debugInvalidate = True
107−
108−class HasNodeId a where
109− getNodeId :: a -> Int
110−
111−instance HasNodeId (Hold x p a) where
112− getNodeId = holdNodeId
113−
114−instance HasNodeId (PushSubscribed x a b) where
115− getNodeId = pushSubscribedNodeId
116−
117−instance HasNodeId (SwitchSubscribed x a) where
118− getNodeId = switchSubscribedNodeId
119−
120−instance HasNodeId (FanSubscribed x a) where
121− getNodeId = fanSubscribedNodeId
122−
123−instance HasNodeId (CoincidenceSubscribed x a) where
124− getNodeId = coincidenceSubscribedNodeId
125−
126−instance HasNodeId (RootSubscribed x a) where
127− getNodeId = rootSubscribedNodeId
128−
129−instance HasNodeId (Pull x a) where
130− getNodeId = pullNodeId
131−
132−{-# INLINE showNodeId #-}
133−showNodeId :: HasNodeId a => a -> String
134−showNodeId = ("#"<>) . show . getNodeId
135−
136−showSubscriberType :: Subscriber x a -> String
137−showSubscriberType = \case
138− SubscriberPush _ _ -> "SubscriberPush"
139− SubscriberMerge _ _ -> "SubscriberMerge"
140− SubscriberFan _ -> "SubscriberFan"
141− SubscriberMergeChange _ -> "SubscriberMergeChange"
142− SubscriberHold _ -> "SubscriberHold"
143− SubscriberHoldIdentity _ -> "SubscriberHoldIdentity"
144− SubscriberSwitch _ -> "SubscriberSwitch"
145− SubscriberCoincidenceOuter _ -> "SubscriberCoincidenceOuter"
146− SubscriberCoincidenceInner _ -> "SubscriberCoincidenceInner"
147− SubscriberHandle -> "SubscriberHandle"
148−
149−showEventType :: Event x a -> String
150−showEventType = \case
151− EventRoot _ _ -> "EventRoot"
152− EventNever -> "EventNever"
153− EventPush _ -> "EventPush"
154− EventMerge _ -> "EventMerge"
155− EventFan _ _ -> "EventFan"
156− EventSwitch _ -> "EventSwitch"
157− EventCoincidence _ -> "EventCoincidence"
158− EventHold _ -> "EventHold"
159− EventDyn _ -> "EventDyn"
160− EventHoldIdentity _ -> "EventHoldIdentity"
161− EventDynIdentity _ -> "EventDynIdentity"
162−
163−#else
164−
165−debugPropagate = False
166−
167−debugInvalidateHeight = False
168−
169−debugInvalidate = False
170−
171−-- This must be inline, or error messages will cause memory leaks due to retaining the node in question
172−{-# INLINE showNodeId #-}
173−showNodeId :: a -> String
174−showNodeId _ = ""
175−
176−#endif
177−
178−#ifdef DEBUG_NODEIDS
179−{-# NOINLINE nextNodeIdRef #-}
180−nextNodeIdRef :: IORef Int
181−nextNodeIdRef = unsafePerformIO $ newIORef 1
182−
183−newNodeId :: IO Int
184−newNodeId = atomicModifyIORef' nextNodeIdRef $ \n -> (succ n, n)
185−
186−{-# NOINLINE unsafeNodeId #-}
187−unsafeNodeId :: a -> Int
188−unsafeNodeId a = unsafePerformIO $ do
189− touch a
190− newNodeId
191−#endif
192−
193−--------------------------------------------------------------------------------
194−-- Event
195−--------------------------------------------------------------------------------
196−
197−newtype Event x a = Event { unEvent :: Subscriber x a -> EventM x (EventSubscription x, Maybe a) }
198−
199−{-# INLINE subscribeAndRead #-}
200−subscribeAndRead :: Event x a -> Subscriber x a -> EventM x (EventSubscription x, Maybe a)
201−subscribeAndRead = unEvent
202−
203−{-# RULES
204−"cacheEvent/cacheEvent" forall e. cacheEvent (cacheEvent e) = cacheEvent e
205−"cacheEvent/pushCheap" forall f e. pushCheap f (cacheEvent e) = cacheEvent (pushCheap f e)
206−"hold/cacheEvent" forall f e. hold f (cacheEvent e) = hold f e
207− #-}
208−
209−-- | Construct an 'Event' equivalent to that constructed by 'push', but with no
210−-- caching; if the computation function is very cheap, this is (much) more
211−-- efficient than 'push'
212−{-# NOINLINE pushCheap #-}
213−pushCheap :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
214−pushCheap f e = Event $ \sub -> do
215− (subscription, occ) <- subscribeAndRead e $ sub
216− { subscriberPropagate = \a -> do
217− mb <- runComputeM $ f a
218− mapM_ (subscriberPropagate sub) mb
219− }
220− occ' <- join <$> mapM (runComputeM . f) occ
221− return (subscription, occ')
222−
223−data CacheSubscribed x a
224− = CacheSubscribed { _cacheSubscribed_subscribers :: {-# UNPACK #-} !(WeakBag (Subscriber x a))
225− , _cacheSubscribed_weakSelf :: {-# UNPACK #-} !(IORef (Weak (CacheSubscribed x a)))
226− , _cacheSubscribed_parent :: {-# UNPACK #-} !(EventSubscription x)
227− , _cacheSubscribed_cached :: {-# UNPACK #-} !(IORef (Maybe (CacheSubscribed x a)))
228− , _cacheSubscribed_occurrence :: {-# UNPACK #-} !(IORef (Maybe a))
229− }
230−
231−-- | Construct an 'Event' whose value is guaranteed not to be recomputed
232−-- repeatedly
233−--
234−--TODO: Try a caching strategy where we subscribe directly to the parent when
235−--there's only one subscriber, and then build our own WeakBag only when a second
236−--subscriber joins
237−{-# NOINLINE cacheEvent #-}
238−cacheEvent :: HasSpiderTimeline x => Event x a -> Event x a
239−cacheEvent e = Event $ \sub -> {-# SCC "cacheEvent" #-} do
240− let !mSubscribedRef = unsafeNewIORef e Nothing
241− mSubscribed <- liftIO $ readIORef mSubscribedRef
242− case mSubscribed of
243− Just subscribed -> liftIO $ do
244− ticket <- WeakBag.insert sub (_cacheSubscribed_subscribers subscribed) (_cacheSubscribed_weakSelf subscribed) cleanupCacheSubscribed
245− occ <- readIORef $ _cacheSubscribed_occurrence subscribed
246− let es = EventSubscribed
247− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ _cacheSubscribed_parent subscribed
248− , eventSubscribedRetained = toAny subscribed
249− }
250− return (EventSubscription (WeakBag.remove ticket >> touch ticket) es, occ)
251− Nothing -> do
252− weakSelf <- liftIO $ newIORef $ error "cacheEvent: weakSelf not yet intialized"
253− (subscribers, ticket) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCacheSubscribed
254− occRef <- liftIO $ newIORef undefined
255− (parentSub, occ) <- subscribeAndRead e $ Subscriber
256− { subscriberPropagate = \a -> do
257− liftIO $ writeIORef occRef $ Just a
258− scheduleClear occRef
259− propagate a subscribers
260− , subscriberInvalidateHeight = \old -> do
261− WeakBag.traverse subscribers $ invalidateSubscriberHeight old
262− , subscriberRecalculateHeight = \new -> do
263− WeakBag.traverse subscribers $ recalculateSubscriberHeight new
264− }
265− liftIO $ writeIORef occRef occ
266− when (isJust occ) $ scheduleClear occRef
267− let !subscribed = CacheSubscribed
268− { _cacheSubscribed_subscribers = subscribers
269− , _cacheSubscribed_weakSelf = weakSelf
270− , _cacheSubscribed_parent = parentSub
271− , _cacheSubscribed_cached = mSubscribedRef
272− , _cacheSubscribed_occurrence = occRef
273− }
274− liftIO $ writeIORef mSubscribedRef $ Just subscribed
275− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "PushSubscribed"
276− return (EventSubscription (WeakBag.remove ticket >> touch ticket) (EventSubscribed (eventSubscribedHeightRef $ _eventSubscription_subscribed parentSub) (toAny subscribed)), occ)
277−
278−cleanupCacheSubscribed :: CacheSubscribed x a -> IO ()
279−cleanupCacheSubscribed self = do
280− unsubscribe $ _cacheSubscribed_parent self
281− writeIORef (_cacheSubscribed_cached self) Nothing
282−
283−subscribe :: Event x a -> Subscriber x a -> EventM x (EventSubscription x)
284−subscribe e s = fst <$> subscribeAndRead e s
285−
286−{-# INLINE wrap #-}
287−wrap :: MonadIO m => (t -> EventSubscribed x) -> (Subscriber x a -> m (WeakBagTicket, t, Maybe a)) -> Subscriber x a -> m (EventSubscription x, Maybe a)
288−wrap tag getSpecificSubscribed sub = do
289− (sln, subd, occ) <- getSpecificSubscribed sub
290− let es = tag subd
291− return (EventSubscription (WeakBag.remove sln >> touch sln) es, occ)
292−
293−eventRoot :: GCompare k => k a -> Root x k -> Event x a
294−eventRoot !k !r = Event $ wrap eventSubscribedRoot $ liftIO . getRootSubscribed k r
295−
296−eventNever :: Event x a
297−eventNever = Event $ \_ -> return (EventSubscription (return ()) eventSubscribedNever, Nothing)
298−
299−eventFan :: (GCompare k, HasSpiderTimeline x) => k a -> Fan x k -> Event x a
300−eventFan !k !f = Event $ wrap eventSubscribedFan $ getFanSubscribed k f
301−
302−eventSwitch :: HasSpiderTimeline x => Switch x a -> Event x a
303−eventSwitch !s = Event $ wrap eventSubscribedSwitch $ getSwitchSubscribed s
304−
305−eventCoincidence :: HasSpiderTimeline x => Coincidence x a -> Event x a
306−eventCoincidence !c = Event $ wrap eventSubscribedCoincidence $ getCoincidenceSubscribed c
307−
308−eventHold :: HasSpiderTimeline x => Hold x p -> Event x p
309−eventHold !h = Event $ subscribeHoldEvent h
310−
311−eventDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Event x p
312−eventDyn !j = Event $ \sub -> getDynHold j >>= \h -> subscribeHoldEvent h sub
313−
314−{-# INLINE subscribeCoincidenceInner #-}
315−subscribeCoincidenceInner :: CanSubscribe x m => Event x a -> Height -> CoincidenceSubscribed x a -> m (Maybe a, Height, EventSubscribed x)
316−subscribeCoincidenceInner inner outerHeight subscribedUnsafe = do
317− subInner <- liftIO $ newSubscriberCoincidenceInner subscribedUnsafe
318− (subscription@(EventSubscription _ innerSubd), innerOcc) <- subscribeAndRead inner subInner
319− innerHeight <- liftIO $ getEventSubscribedHeight innerSubd
320− let height = max innerHeight outerHeight
321− defer $ SomeResetCoincidence subscription $ if height > outerHeight then Just subscribedUnsafe else Nothing
322− return (innerOcc, height, innerSubd)
323−
324−--------------------------------------------------------------------------------
325−-- Subscriber
326−--------------------------------------------------------------------------------
327−
328−data Subscriber x a = Subscriber
329− { subscriberPropagate :: !(a -> EventM x ())
330− , subscriberInvalidateHeight :: !(Height -> IO ())
331− , subscriberRecalculateHeight :: !(Height -> IO ())
332− }
333−
334−--TODO: Move this comment to WeakBag
335−-- These function are constructor functions that are marked NOINLINE so they are
336−-- opaque to GHC. If we do not do this, then GHC will sometimes fuse the constructor away
337−-- so any weak references that are attached to the constructors will have their
338−-- finalizer run. Using the opaque constructor, does not see the
339−-- constructor application, so it behaves like an IORef and cannot be fused away.
340−--
341−-- The result is also evaluated to WHNF, since forcing a thunk invalidates
342−-- the weak pointer to it in some cases.
343−
344−newSubscriberHold :: (HasSpiderTimeline x, Patch p) => Hold x p -> IO (Subscriber x p)
345−newSubscriberHold h = return $ Subscriber
346− { subscriberPropagate = {-# SCC "traverseHold" #-} propagateSubscriberHold h
347− , subscriberInvalidateHeight = \_ -> return ()
348− , subscriberRecalculateHeight = \_ -> return ()
349− }
350−
351−newSubscriberFan :: forall x k. (HasSpiderTimeline x, GCompare k) => FanSubscribed x k -> IO (Subscriber x (DMap k Identity))
352−newSubscriberFan subscribed = return $ Subscriber
353− { subscriberPropagate = \a -> {-# SCC "traverseFan" #-} do
354− subs <- liftIO $ readIORef $ fanSubscribedSubscribers subscribed
355− tracePropagate (Proxy :: Proxy x) $ "SubscriberFan" <> showNodeId subscribed <> ": " ++ show (DMap.size subs) ++ " keys subscribed, " ++ show (DMap.size a) ++ " keys firing"
356− liftIO $ writeIORef (fanSubscribedOccurrence subscribed) $ Just a
357− scheduleClear $ fanSubscribedOccurrence subscribed
358− let f _ (Pair (Identity v) subsubs) = do
359− propagate v $ _fanSubscribedChildren_list subsubs
360− return $ Constant ()
361− _ <- DMap.traverseWithKey f $ DMap.intersectionWithKey (\_ -> Pair) a subs --TODO: Would be nice to have DMap.traverse_
362− return ()
363− , subscriberInvalidateHeight = \old -> do
364− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
365− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
366− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ invalidateSubscriberHeight old
367− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
368− , subscriberRecalculateHeight = \new -> do
369− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed
370− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
371− forM_ (DMap.toList subscribers) $ \(_ :=> v) -> WeakBag.traverse (_fanSubscribedChildren_list v) $ recalculateSubscriberHeight new
372− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberFan" <> showNodeId subscribed <> " done"
373− }
374−
375−newSubscriberSwitch :: forall x a. HasSpiderTimeline x => SwitchSubscribed x a -> IO (Subscriber x a)
376−newSubscriberSwitch subscribed = return $ Subscriber
377− { subscriberPropagate = \a -> {-# SCC "traverseSwitch" #-} do
378− tracePropagate (Proxy :: Proxy x) $ "SubscriberSwitch" <> showNodeId subscribed
379− liftIO $ writeIORef (switchSubscribedOccurrence subscribed) $ Just a
380− scheduleClear $ switchSubscribedOccurrence subscribed
381− propagate a $ switchSubscribedSubscribers subscribed
382− , subscriberInvalidateHeight = \_ -> do
383− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
384− oldHeight <- readIORef $ switchSubscribedHeight subscribed
385− when (oldHeight /= invalidHeight) $ do
386− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
387− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
388− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
389− , subscriberRecalculateHeight = \new -> do
390− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed
391− updateSwitchHeight new subscribed
392− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberSwitch" <> showNodeId subscribed <> " done"
393− }
394−
395−newSubscriberCoincidenceOuter :: forall x b. HasSpiderTimeline x => CoincidenceSubscribed x b -> IO (Subscriber x (Event x b))
396−newSubscriberCoincidenceOuter subscribed = return $ Subscriber
397− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceOuter" #-} do
398− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceOuter" <> showNodeId subscribed
399− outerHeight <- liftIO $ readIORef $ coincidenceSubscribedHeight subscribed
400− tracePropagate (Proxy :: Proxy x) $ " outerHeight = " <> show outerHeight
401− (occ, innerHeight, innerSubd) <- subscribeCoincidenceInner a outerHeight subscribed
402− tracePropagate (Proxy :: Proxy x) $ " isJust occ = " <> show (isJust occ)
403− tracePropagate (Proxy :: Proxy x) $ " innerHeight = " <> show innerHeight
404− liftIO $ writeIORef (coincidenceSubscribedInnerParent subscribed) $ Just innerSubd
405− scheduleClear $ coincidenceSubscribedInnerParent subscribed
406− case occ of
407− Nothing -> do
408− when (innerHeight > outerHeight) $ liftIO $ do -- If the event fires, it will fire at a later height
409− writeIORef (coincidenceSubscribedHeight subscribed) $! innerHeight
410− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight outerHeight
411− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight innerHeight
412− Just o -> do -- Since it's already firing, no need to adjust height
413− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) occ
414− scheduleClear $ coincidenceSubscribedOccurrence subscribed
415− propagate o $ coincidenceSubscribedSubscribers subscribed
416− , subscriberInvalidateHeight = \_ -> do
417− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
418− invalidateCoincidenceHeight subscribed
419− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
420− , subscriberRecalculateHeight = \_ -> do
421− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed
422− recalculateCoincidenceHeight subscribed
423− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceOuter" <> showNodeId subscribed <> " done"
424− }
425−
426−newSubscriberCoincidenceInner :: forall x a. HasSpiderTimeline x => CoincidenceSubscribed x a -> IO (Subscriber x a)
427−newSubscriberCoincidenceInner subscribed = return $ Subscriber
428− { subscriberPropagate = \a -> {-# SCC "traverseCoincidenceInner" #-} do
429− tracePropagate (Proxy :: Proxy x) $ "SubscriberCoincidenceInner" <> showNodeId subscribed
430− occ <- liftIO $ readIORef $ coincidenceSubscribedOccurrence subscribed
431− case occ of
432− Just _ -> return () -- SubscriberCoincidenceOuter must have already propagated this event
433− Nothing -> do
434− liftIO $ writeIORef (coincidenceSubscribedOccurrence subscribed) $ Just a
435− scheduleClear $ coincidenceSubscribedOccurrence subscribed
436− propagate a $ coincidenceSubscribedSubscribers subscribed
437− , subscriberInvalidateHeight = \_ -> do
438− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
439− invalidateCoincidenceHeight subscribed
440− when debugInvalidateHeight $ putStrLn $ "invalidateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
441− , subscriberRecalculateHeight = \_ -> do
442− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed
443− recalculateCoincidenceHeight subscribed
444− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: SubscriberCoincidenceInner" <> showNodeId subscribed <> " done"
445− }
446−
447−invalidateSubscriberHeight :: Height -> Subscriber x a -> IO ()
448−invalidateSubscriberHeight = flip subscriberInvalidateHeight
449−
450−recalculateSubscriberHeight :: Height -> Subscriber x a -> IO ()
451−recalculateSubscriberHeight = flip subscriberRecalculateHeight
452−
453−-- | Propagate everything at the current height
454−propagate :: a -> WeakBag (Subscriber x a) -> EventM x ()
455−propagate a subscribers = withIncreasedDepth $ do
456− -- 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
457− --TODO: Should we check if nodes already have their values before propagating? Maybe we're re-doing work
458− WeakBag.traverse subscribers $ \s -> subscriberPropagate s a
459−
460−--------------------------------------------------------------------------------
461−-- EventSubscribed
462−--------------------------------------------------------------------------------
463−
464−toAny :: a -> Any
465−toAny = unsafeCoerce
466−
467−data EventSubscribed x = EventSubscribed
468− { eventSubscribedHeightRef :: {-# UNPACK #-} !(IORef Height)
469− , eventSubscribedRetained :: {-# NOUNPACK #-} !Any
470−#ifdef DEBUG_CYCLES
471− , eventSubscribedGetParents :: !(IO [Some (EventSubscribed x)]) -- For debugging loops
472− , eventSubscribedHasOwnHeightRef :: !Bool
473− , eventSubscribedWhoCreated :: !(IO [String])
474−#endif
475− }
476−
477−eventSubscribedRoot :: RootSubscribed x a -> EventSubscribed x
478−eventSubscribedRoot !r = EventSubscribed
479− { eventSubscribedHeightRef = zeroRef
480− , eventSubscribedRetained = toAny r
481−#ifdef DEBUG_CYCLES
482− , eventSubscribedGetParents = return []
483− , eventSubscribedHasOwnHeightRef = False
484− , eventSubscribedWhoCreated = return ["root"]
485−#endif
486− }
487−
488−eventSubscribedNever :: EventSubscribed x
489−eventSubscribedNever = EventSubscribed
490− { eventSubscribedHeightRef = zeroRef
491− , eventSubscribedRetained = toAny ()
492−#ifdef DEBUG_CYCLES
493− , eventSubscribedGetParents = return []
494− , eventSubscribedHasOwnHeightRef = False
495− , eventSubscribedWhoCreated = return ["never"]
496−#endif
497− }
498−
499−eventSubscribedFan :: FanSubscribed x k -> EventSubscribed x
500−eventSubscribedFan !subscribed = EventSubscribed
501− { eventSubscribedHeightRef = eventSubscribedHeightRef $ _eventSubscription_subscribed $ fanSubscribedParent subscribed
502− , eventSubscribedRetained = toAny subscribed
503−#ifdef DEBUG_CYCLES
504− , eventSubscribedGetParents = return [Some.This $ _eventSubscription_subscribed $ fanSubscribedParent subscribed]
505− , eventSubscribedHasOwnHeightRef = False
506− , eventSubscribedWhoCreated = whoCreatedIORef $ fanSubscribedCachedSubscribed subscribed
507−#endif
508− }
509−
510−eventSubscribedSwitch :: SwitchSubscribed x a -> EventSubscribed x
511−eventSubscribedSwitch !subscribed = EventSubscribed
512− { eventSubscribedHeightRef = switchSubscribedHeight subscribed
513− , eventSubscribedRetained = toAny subscribed
514−#ifdef DEBUG_CYCLES
515− , eventSubscribedGetParents = do
516− s <- readIORef $ switchSubscribedCurrentParent subscribed
517− return [Some.This $ _eventSubscription_subscribed s]
518− , eventSubscribedHasOwnHeightRef = True
519− , eventSubscribedWhoCreated = whoCreatedIORef $ switchSubscribedCachedSubscribed subscribed
520−#endif
521− }
522−
523−eventSubscribedCoincidence :: CoincidenceSubscribed x a -> EventSubscribed x
524−eventSubscribedCoincidence !subscribed = EventSubscribed
525− { eventSubscribedHeightRef = coincidenceSubscribedHeight subscribed
526− , eventSubscribedRetained = toAny subscribed
527−#ifdef DEBUG_CYCLES
528− , eventSubscribedGetParents = do
529− innerSubscription <- readIORef $ coincidenceSubscribedInnerParent subscribed
530− let outerParent = Some.This $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
531− innerParents = maybeToList $ fmap Some.This innerSubscription
532− return $ outerParent : innerParents
533− , eventSubscribedHasOwnHeightRef = True
534− , eventSubscribedWhoCreated = whoCreatedIORef $ coincidenceSubscribedCachedSubscribed subscribed
535−#endif
536− }
537−
538−getEventSubscribedHeight :: EventSubscribed x -> IO Height
539−getEventSubscribedHeight es = readIORef $ eventSubscribedHeightRef es
540−
541−#ifdef DEBUG_CYCLES
542−whoCreatedEventSubscribed :: EventSubscribed x -> IO [String]
543−whoCreatedEventSubscribed = eventSubscribedWhoCreated
544−
545−walkInvalidHeightParents :: EventSubscribed x -> IO [Some (EventSubscribed x)]
546−walkInvalidHeightParents s0 = do
547− subscribers <- flip execStateT mempty $ ($ Some.This s0) $ fix $ \loop (Some.This s) -> do
548− h <- liftIO $ readIORef $ eventSubscribedHeightRef s
549− when (h == invalidHeight) $ do
550− when (eventSubscribedHasOwnHeightRef s) $ liftIO $ writeIORef (eventSubscribedHeightRef s) $! invalidHeightBeingTraversed
551− modify (Some.This s :)
552− mapM_ loop =<< liftIO (eventSubscribedGetParents s)
553− forM_ subscribers $ \(Some.This s) -> writeIORef (eventSubscribedHeightRef s) $! invalidHeight
554− return subscribers
555−#endif
556−
557−{-# INLINE subscribeHoldEvent #-}
558−subscribeHoldEvent :: CanSubscribe x m => Hold x p -> Subscriber x p -> m (EventSubscription x, Maybe p)
559−subscribeHoldEvent = subscribeAndRead . holdEvent
560−
561−--------------------------------------------------------------------------------
562−-- EventSubscription
563−--------------------------------------------------------------------------------
564−
565−data EventSubscription x = EventSubscription
566− { _eventSubscription_unsubscribe :: !(IO ())
567− , _eventSubscription_subscribed :: {-# UNPACK #-} !(EventSubscribed x)
568− }
569−
570−unsubscribe :: EventSubscription x -> IO ()
571−unsubscribe (EventSubscription u _) = u
572−
573−--------------------------------------------------------------------------------
574−-- Behavior
575−--------------------------------------------------------------------------------
576−
577−newtype Behavior x a = Behavior { readBehaviorTracked :: BehaviorM x a }
578−
579−behaviorHold :: Hold x p -> Behavior x (PatchTarget p)
580−behaviorHold !h = Behavior $ readHoldTracked h
581−
582−behaviorHoldIdentity :: Hold x (Identity a) -> Behavior x a
583−behaviorHoldIdentity = behaviorHold
584−
585−behaviorConst :: a -> Behavior x a
586−behaviorConst !a = Behavior $ return a
587−
588−behaviorPull :: Pull x a -> Behavior x a
589−behaviorPull !p = Behavior $ do
590− val <- liftIO $ readIORef $ pullValue p
591− case val of
592− Just subscribed -> do
593− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
594− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (pullSubscribedInvalidators subscribed) (wi:))
595− liftIO $ touch $ pullSubscribedOwnInvalidator subscribed
596− return $ pullSubscribedValue subscribed
597− Nothing -> do
598− i <- liftIO $ newInvalidatorPull p
599− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorPull"
600− parentsRef <- liftIO $ newIORef []
601− holdInits <- askBehaviorHoldInits
602− a <- liftIO $ runReaderT (unBehaviorM $ pullCompute p) (Just (wi, parentsRef), holdInits)
603− invsRef <- liftIO . newIORef . maybeToList =<< askInvalidator
604− parents <- liftIO $ readIORef parentsRef
605− let subscribed = PullSubscribed
606− { pullSubscribedValue = a
607− , pullSubscribedInvalidators = invsRef
608− , pullSubscribedOwnInvalidator = i
609− , pullSubscribedParents = parents
610− }
611− liftIO $ writeIORef (pullValue p) $ Just subscribed
612− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedPull subscribed) :))
613− return a
614−
615−behaviorDyn :: Patch p => Dyn x p -> Behavior x (PatchTarget p)
616−behaviorDyn !d = Behavior $ readHoldTracked =<< getDynHold d
617−
618−{-# INLINE readHoldTracked #-}
619−readHoldTracked :: Hold x p -> BehaviorM x (PatchTarget p)
620−readHoldTracked h = do
621− result <- liftIO $ readIORef $ holdValue h
622− askInvalidator >>= mapM_ (\wi -> liftIO $ modifyIORef' (holdInvalidators h) (wi:))
623− askParentsRef >>= mapM_ (\r -> liftIO $ modifyIORef' r (SomeBehaviorSubscribed (BehaviorSubscribedHold h) :))
624− return result
625−
626−{-# INLINABLE readBehaviorUntracked #-}
627−readBehaviorUntracked :: Defer (SomeHoldInit x) m => Behavior x a -> m a
628−readBehaviorUntracked b = do
629− holdInits <- getDeferralQueue
630− liftIO $ runBehaviorM (readBehaviorTracked b) Nothing holdInits --TODO: Specialize readBehaviorTracked to the Nothing and Just cases
631−
632−--------------------------------------------------------------------------------
633−-- Dynamic
634−--------------------------------------------------------------------------------
635−
636−data Dynamic x p = Dynamic
637− { dynamicCurrent :: !(Behavior x (PatchTarget p))
638− , dynamicUpdated :: !(Event x p)
639− }
640−
641−dynamicHold :: HasSpiderTimeline x => Hold x p -> Dynamic x p
642−dynamicHold !h = Dynamic
643− { dynamicCurrent = behaviorHold h
644− , dynamicUpdated = eventHold h
645− }
646−
647−dynamicHoldIdentity :: HasSpiderTimeline x => Hold x (Identity a) -> Dynamic x (Identity a)
648−dynamicHoldIdentity = dynamicHold
649−
650−dynamicConst :: PatchTarget p -> Dynamic x p
651−dynamicConst !a = Dynamic
652− { dynamicCurrent = behaviorConst a
653− , dynamicUpdated = eventNever
654− }
655−
656−dynamicDyn :: (HasSpiderTimeline x, Patch p) => Dyn x p -> Dynamic x p
657−dynamicDyn !d = Dynamic
658− { dynamicCurrent = behaviorDyn d
659− , dynamicUpdated = eventDyn d
660− }
661−
662−dynamicDynIdentity :: HasSpiderTimeline x => Dyn x (Identity a) -> Dynamic x (Identity a)
663−dynamicDynIdentity = dynamicDyn
664−
665−--------------------------------------------------------------------------------
666−-- Combinators
667−--------------------------------------------------------------------------------
668−
669−--TODO: Figure out why certain things are not 'representational', then make them
670−--representational so we can use coerce
671−
672−--type role Hold representational
673−data Hold x p
674− = Hold { holdValue :: !(IORef (PatchTarget p))
675− , holdInvalidators :: !(IORef [Weak (Invalidator x)])
676− , holdEvent :: Event x p -- This must be lazy, or holds cannot be defined before their input Events
677− , holdParent :: !(IORef (Maybe (EventSubscription x))) -- Keeps its parent alive (will be undefined until the hold is initialized) --TODO: Probably shouldn't be an IORef
678−#ifdef DEBUG_NODEIDS
679− , holdNodeId :: Int
680−#endif
681− }
682−
683−data Global
684−
685−{-# NOINLINE globalSpiderTimelineEnv #-}
686−globalSpiderTimelineEnv :: SpiderTimelineEnv Global
687−!globalSpiderTimelineEnv = unsafePerformIO unsafeNewSpiderTimelineEnv
688−
689−-- | Stores all global data relevant to a particular Spider timeline; only one
690−-- value should exist for each type @x@
691−data SpiderTimelineEnv x = SpiderTimelineEnv
692− { _spiderTimeline_lock :: {-# UNPACK #-} !(MVar ())
693− , _spiderTimeline_eventEnv :: {-# UNPACK #-} !(EventEnv x)
694−#ifdef DEBUG
695− , _spiderTimeline_depth :: {-# UNPACK #-} !(IORef Int)
696−#endif
697− }
698−type role SpiderTimelineEnv nominal
699−
700−instance GEq SpiderTimelineEnv where
701− a `geq` b = if _spiderTimeline_lock a == _spiderTimeline_lock b
702− then Just $ unsafeCoerce Refl -- This unsafeCoerce is safe because the same SpiderTimelineEnv can't have two different 'x' arguments
703− else Nothing
704−
705−data EventEnv x
706− = EventEnv { eventEnvAssignments :: !(IORef [SomeAssignment x]) -- Needed for Subscribe
707− , eventEnvHoldInits :: !(IORef [SomeHoldInit x]) -- Needed for Subscribe
708− , eventEnvMergeUpdates :: !(IORef [SomeMergeUpdate x])
709− , eventEnvMergeInits :: !(IORef [SomeMergeInit x]) -- Needed for Subscribe
710− , eventEnvClears :: !(IORef [SomeClear]) -- Needed for Subscribe
711− , eventEnvRootClears :: !(IORef [SomeRootClear])
712− , eventEnvCurrentHeight :: !(IORef Height) -- Needed for Subscribe
713− , eventEnvResetCoincidences :: !(IORef [SomeResetCoincidence x]) -- Needed for Subscribe
714− , eventEnvDelayedMerges :: !(IORef (IntMap [EventM x ()]))
715− }
716−
717−{-# INLINE runEventM #-}
718−runEventM :: EventM x a -> IO a
719−runEventM = unEventM
720−
721−asksEventEnv :: forall x a. HasSpiderTimeline x => (EventEnv x -> a) -> EventM x a
722−asksEventEnv f = return $ f $ _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
723−
724−class MonadIO m => Defer a m where
725− getDeferralQueue :: m (IORef [a])
726−
727−{-# INLINE defer #-}
728−defer :: Defer a m => a -> m ()
729−defer a = do
730− q <- getDeferralQueue
731− liftIO $ modifyIORef' q (a:)
732−
733−instance HasSpiderTimeline x => Defer (SomeAssignment x) (EventM x) where
734− {-# INLINE getDeferralQueue #-}
735− getDeferralQueue = asksEventEnv eventEnvAssignments
736−
737−instance HasSpiderTimeline x => Defer (SomeHoldInit x) (EventM x) where
738− {-# INLINE getDeferralQueue #-}
739− getDeferralQueue = asksEventEnv eventEnvHoldInits
740−
741−instance Defer (SomeHoldInit x) (BehaviorM x) where
742− {-# INLINE getDeferralQueue #-}
743− getDeferralQueue = BehaviorM $ asks snd
744−
745−instance HasSpiderTimeline x => Defer (SomeMergeUpdate x) (EventM x) where
746− {-# INLINE getDeferralQueue #-}
747− getDeferralQueue = asksEventEnv eventEnvMergeUpdates
748−
749−instance HasSpiderTimeline x => Defer (SomeMergeInit x) (EventM x) where
750− {-# INLINE getDeferralQueue #-}
751− getDeferralQueue = asksEventEnv eventEnvMergeInits
752−
753−class HasSpiderTimeline x => HasCurrentHeight x m | m -> x where
754− getCurrentHeight :: m Height
755− scheduleMerge :: Height -> EventM x () -> m ()
756−
757−instance HasSpiderTimeline x => HasCurrentHeight x (EventM x) where
758− {-# INLINE getCurrentHeight #-}
759− getCurrentHeight = do
760− heightRef <- asksEventEnv eventEnvCurrentHeight
761− liftIO $ readIORef heightRef
762− {-# INLINE scheduleMerge #-}
763− scheduleMerge height subscribed = do
764− delayedRef <- asksEventEnv eventEnvDelayedMerges
765− liftIO $ modifyIORef' delayedRef $ IntMap.insertWith (++) (unHeight height) [subscribed]
766−
767−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
768−class x ~ Global => HasSpiderTimeline x where
769−#else
770−class HasSpiderTimeline x where
771−#endif
772− -- | Retrieve the current SpiderTimelineEnv
773− spiderTimeline :: SpiderTimelineEnv x
774−
775−instance HasSpiderTimeline Global where
776− spiderTimeline = globalSpiderTimelineEnv
777−
778−putCurrentHeight :: HasSpiderTimeline x => Height -> EventM x ()
779−putCurrentHeight h = do
780− heightRef <- asksEventEnv eventEnvCurrentHeight
781− liftIO $ writeIORef heightRef $! h
782−
783−instance HasSpiderTimeline x => Defer SomeClear (EventM x) where
784− {-# INLINE getDeferralQueue #-}
785− getDeferralQueue = asksEventEnv eventEnvClears
786−
787−{-# INLINE scheduleClear #-}
788−scheduleClear :: Defer SomeClear m => IORef (Maybe a) -> m ()
789−scheduleClear r = defer $ SomeClear r
790−
791−instance HasSpiderTimeline x => Defer SomeRootClear (EventM x) where
792− {-# INLINE getDeferralQueue #-}
793− getDeferralQueue = asksEventEnv eventEnvRootClears
794−
795−{-# INLINE scheduleRootClear #-}
796−scheduleRootClear :: Defer SomeRootClear m => IORef (DMap k Identity) -> m ()
797−scheduleRootClear r = defer $ SomeRootClear r
798−
799−instance HasSpiderTimeline x => Defer (SomeResetCoincidence x) (EventM x) where
800− {-# INLINE getDeferralQueue #-}
801− getDeferralQueue = asksEventEnv eventEnvResetCoincidences
802−
803−-- Note: hold cannot examine its event until after the phase is over
804−{-# INLINE [1] hold #-}
805−hold :: (Patch p, Defer (SomeHoldInit x) m) => PatchTarget p -> Event x p -> m (Hold x p)
806−hold v0 e = do
807− valRef <- liftIO $ newIORef v0
808− invsRef <- liftIO $ newIORef []
809− parentRef <- liftIO $ newIORef Nothing
810−#ifdef DEBUG_NODEIDS
811− nodeId <- liftIO newNodeId
812−#endif
813− let h = Hold
814− { holdValue = valRef
815− , holdInvalidators = invsRef
816− , holdEvent = e
817− , holdParent = parentRef
818−#ifdef DEBUG_NODEIDS
819− , holdNodeId = nodeId
820−#endif
821− }
822− defer $ SomeHoldInit h
823− return h
824−
825−{-# INLINE getHoldEventSubscription #-}
826−getHoldEventSubscription :: forall m p x. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer SomeClear m, Patch p, HasCurrentHeight x m, Defer (SomeMergeInit x) m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Hold x p -> m (EventSubscription x)
827−getHoldEventSubscription h = do
828− ep <- liftIO $ readIORef $ holdParent h
829− case ep of
830− Just subd -> return subd
831− Nothing -> do
832− let e = holdEvent h
833− subscriptionRef <- liftIO $ newIORef $ error "getHoldEventSubscription: subdRef uninitialized"
834− (subscription@(EventSubscription _ _), occ) <- subscribeAndRead e =<< liftIO (newSubscriberHold h)
835− liftIO $ writeIORef subscriptionRef $! subscription
836− case occ of
837− Nothing -> return ()
838− Just o -> do
839− old <- liftIO $ readIORef $ holdValue h
840− case apply o old of
841− Nothing -> return ()
842− Just new -> do
843− -- Need to evaluate these so that we don't retain the Hold itself
844− v <- liftIO $ evaluate $ holdValue h
845− i <- liftIO $ evaluate $ holdInvalidators h
846− defer $ SomeAssignment v i new
847− liftIO $ writeIORef (holdParent h) $ Just subscription
848− return subscription
849−
850−type BehaviorEnv x = (Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]), IORef [SomeHoldInit x])
851−
852−--type role BehaviorM representational
853−-- BehaviorM can sample behaviors
854−newtype BehaviorM x a = BehaviorM { unBehaviorM :: ReaderT (BehaviorEnv x) IO a } deriving (Functor, Applicative, MonadIO, MonadFix)
855−
856−instance Monad (BehaviorM x) where
857− {-# INLINE (>>=) #-}
858− BehaviorM x >>= f = BehaviorM $ x >>= unBehaviorM . f
859− {-# INLINE (>>) #-}
860− BehaviorM x >> BehaviorM y = BehaviorM $ x >> y
861− {-# INLINE return #-}
862− return x = BehaviorM $ return x
863− {-# INLINE fail #-}
864− fail s = BehaviorM $ fail s
865−
866−data BehaviorSubscribed x a
867− = forall p. BehaviorSubscribedHold (Hold x p)
868− | BehaviorSubscribedPull (PullSubscribed x a)
869−
870−data SomeBehaviorSubscribed x = forall a. SomeBehaviorSubscribed (BehaviorSubscribed x a)
871−
872−--type role PullSubscribed representational
873−data PullSubscribed x a
874− = PullSubscribed { pullSubscribedValue :: !a
875− , pullSubscribedInvalidators :: !(IORef [Weak (Invalidator x)])
876− , pullSubscribedOwnInvalidator :: !(Invalidator x)
877− , pullSubscribedParents :: ![SomeBehaviorSubscribed x] -- Need to keep parent behaviors alive, or they won't let us know when they're invalidated
878− }
879−
880−--type role Pull representational
881−data Pull x a
882− = Pull { pullValue :: !(IORef (Maybe (PullSubscribed x a)))
883− , pullCompute :: !(BehaviorM x a)
884−#ifdef DEBUG_NODEIDS
885− , pullNodeId :: Int
886−#endif
887− }
888−
889−data Invalidator x
890− = forall a. InvalidatorPull (Pull x a)
891− | forall a. InvalidatorSwitch (SwitchSubscribed x a)
892−
893−data RootSubscribed x a = forall k. GCompare k => RootSubscribed
894− { rootSubscribedKey :: !(k a)
895− , rootSubscribedCachedSubscribed :: !(IORef (DMap k (RootSubscribed x))) -- From the original Root
896− , rootSubscribedSubscribers :: !(WeakBag (Subscriber x a))
897− , rootSubscribedOccurrence :: !(IO (Maybe a)) -- Lookup from rootOccurrence
898− , rootSubscribedUninit :: IO ()
899− , 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?
900−#ifdef DEBUG_NODEIDS
901− , rootSubscribedNodeId :: Int
902−#endif
903− }
904−
905−data Root x (k :: * -> *)
906− = Root { rootOccurrence :: !(IORef (DMap k Identity)) -- The currently-firing occurrence of this event
907− , rootSubscribed :: !(IORef (DMap k (RootSubscribed x)))
908− , rootInit :: !(forall a. k a -> RootTrigger x a -> IO (IO ()))
909− }
910−
911−data SomeHoldInit x = forall p. Patch p => SomeHoldInit !(Hold x p)
912−
913−data SomeMergeUpdate x = SomeMergeUpdate
914− { _someMergeUpdate_update :: !(EventM x [EventSubscription x])
915− , _someMergeUpdate_invalidateHeight :: !(IO ())
916− , _someMergeUpdate_recalculateHeight :: !(IO ())
917− }
918−
919−newtype SomeMergeInit x = SomeMergeInit { unSomeMergeInit :: EventM x () }
920−
921−-- EventM can do everything BehaviorM can, plus create holds
922−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
923−
924−newtype ComputeM x a = ComputeM (ReaderT (IORef [SomeHoldInit x]) IO a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
925−
926−instance Defer (SomeHoldInit x) (ComputeM x) where
927− getDeferralQueue = ComputeM ask
928−
929−{-# INLINABLE runComputeM #-}
930−runComputeM :: Defer (SomeHoldInit x) m => ComputeM x a -> m a
931−runComputeM (ComputeM a) = liftIO . runReaderT a =<< getDeferralQueue
932−
933−newtype MergeSubscribedParent x a = MergeSubscribedParent { unMergeSubscribedParent :: EventSubscription x }
934−
935−data HeightBag = HeightBag
936− { _heightBag_size :: {-# UNPACK #-} !Int
937− , _heightBag_contents :: !(IntMap Word) -- Number of excess in each bucket
938− }
939− deriving (Show, Read, Eq, Ord)
940−
941−heightBagEmpty :: HeightBag
942−heightBagEmpty = heightBagVerify $ HeightBag 0 IntMap.empty
943−
944−heightBagSize :: HeightBag -> Int
945−heightBagSize = _heightBag_size
946−
947−heightBagFromList :: [Height] -> HeightBag
948−heightBagFromList heights = heightBagVerify $ foldl' (flip heightBagAdd) heightBagEmpty heights
949−
950−heightBagAdd :: Height -> HeightBag -> HeightBag
951−heightBagAdd (Height h) (HeightBag s c) = heightBagVerify $ HeightBag (succ s) $ IntMap.insertWithKey (\_ _ old -> succ old) h 0 c
952−
953−heightBagRemove :: Height -> HeightBag -> HeightBag
954−heightBagRemove (Height h) b@(HeightBag s c) = heightBagVerify $ case IntMap.lookup h c of
955− Nothing -> error $ "heightBagRemove: Height " <> show h <> " not present in bag " <> show b
956− Just old -> HeightBag (pred s) $ case old of
957− 0 -> IntMap.delete h c
958− _ -> IntMap.insert h (pred old) c
959−
960−heightBagMax :: HeightBag -> Height
961−heightBagMax (HeightBag _ c) = case IntMap.maxViewWithKey c of
962− Just ((h, _), _) -> Height h
963− Nothing -> zeroHeight
964−
965−heightBagVerify :: HeightBag -> HeightBag
966−#ifdef DEBUG
967−heightBagVerify b@(HeightBag s c) = if
968− | s /= IntMap.size c + fromIntegral (sum (IntMap.elems c))
969− -> error $ "heightBagVerify: size doesn't match: " <> show b
970− | unHeight invalidHeight `IntMap.member` c
971− -> error $ "heightBagVerify: contains invalid height: " <> show b
972− | otherwise -> b
973−#else
974−heightBagVerify = id
975−#endif
976−
977−data FanSubscribedChildren (x :: *) k a = FanSubscribedChildren
978− { _fanSubscribedChildren_list :: !(WeakBag (Subscriber x a))
979− , _fanSubscribedChildren_self :: {-# NOUNPACK #-} !(k a, FanSubscribed x k)
980− , _fanSubscribedChildren_weakSelf :: !(IORef (Weak (k a, FanSubscribed x k)))
981− }
982−
983−data FanSubscribed (x :: *) k
984− = FanSubscribed { fanSubscribedCachedSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
985− , fanSubscribedOccurrence :: !(IORef (Maybe (DMap k Identity)))
986− , fanSubscribedSubscribers :: !(IORef (DMap k (FanSubscribedChildren x k))) -- This DMap should never be empty
987− , fanSubscribedParent :: !(EventSubscription x)
988−#ifdef DEBUG_NODEIDS
989− , fanSubscribedNodeId :: Int
990−#endif
991− }
992−
993−data Fan x k
994− = Fan { fanParent :: !(Event x (DMap k Identity))
995− , fanSubscribed :: !(IORef (Maybe (FanSubscribed x k)))
996− }
997−
998−data SwitchSubscribed x a
999− = SwitchSubscribed { switchSubscribedCachedSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1000− , switchSubscribedOccurrence :: !(IORef (Maybe a))
1001− , switchSubscribedHeight :: !(IORef Height)
1002− , switchSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1003− , switchSubscribedOwnInvalidator :: {-# NOUNPACK #-} !(Invalidator x)
1004− , switchSubscribedOwnWeakInvalidator :: !(IORef (Weak (Invalidator x)))
1005− , switchSubscribedBehaviorParents :: !(IORef [SomeBehaviorSubscribed x])
1006− , switchSubscribedParent :: !(Behavior x (Event x a))
1007− , switchSubscribedCurrentParent :: !(IORef (EventSubscription x))
1008− , switchSubscribedWeakSelf :: !(IORef (Weak (SwitchSubscribed x a)))
1009−#ifdef DEBUG_NODEIDS
1010− , switchSubscribedNodeId :: Int
1011−#endif
1012− }
1013−
1014−data Switch x a
1015− = Switch { switchParent :: !(Behavior x (Event x a))
1016− , switchSubscribed :: !(IORef (Maybe (SwitchSubscribed x a)))
1017− }
1018−
1019−{-# ANN CoincidenceSubscribed "HLint: ignore Redundant bracket" #-}
1020−data CoincidenceSubscribed x a
1021− = CoincidenceSubscribed { coincidenceSubscribedCachedSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1022− , coincidenceSubscribedOccurrence :: !(IORef (Maybe a))
1023− , coincidenceSubscribedSubscribers :: !(WeakBag (Subscriber x a))
1024− , coincidenceSubscribedHeight :: !(IORef Height)
1025− , coincidenceSubscribedOuter :: {-# NOUNPACK #-} (Subscriber x (Event x a))
1026− , coincidenceSubscribedOuterParent :: !(EventSubscription x)
1027− , coincidenceSubscribedInnerParent :: !(IORef (Maybe (EventSubscribed x)))
1028− , coincidenceSubscribedWeakSelf :: !(IORef (Weak (CoincidenceSubscribed x a)))
1029−#ifdef DEBUG_NODEIDS
1030− , coincidenceSubscribedNodeId :: Int
1031−#endif
1032− }
1033−
1034−data Coincidence x a
1035− = Coincidence { coincidenceParent :: !(Event x (Event x a))
1036− , coincidenceSubscribed :: !(IORef (Maybe (CoincidenceSubscribed x a)))
1037− }
1038−
1039−{-# NOINLINE newInvalidatorSwitch #-}
1040−newInvalidatorSwitch :: SwitchSubscribed x a -> IO (Invalidator x)
1041−newInvalidatorSwitch subd = return $! InvalidatorSwitch subd
1042−
1043−{-# NOINLINE newInvalidatorPull #-}
1044−newInvalidatorPull :: Pull x a -> IO (Invalidator x)
1045−newInvalidatorPull p = return $! InvalidatorPull p
1046−
1047−instance HasSpiderTimeline x => FunctorMaybe (Event x) where
1048− fmapMaybe f = push $ return . f
1049−
1050−instance HasSpiderTimeline x => Align (Event x) where
1051− nil = eventNever
1052− align ea eb = fmapMaybe dmapToThese $ merge $ dynamicConst $ DMap.fromList [LeftTag :=> ea, RightTag :=> eb]
1053−
1054−newtype Dyn x p = Dyn { unDyn :: IORef (Either (BehaviorM x (PatchTarget p), Event x p) (Hold x p)) }
1055−
1056−newMapDyn :: HasSpiderTimeline x => (a -> b) -> Dynamic x (Identity a) -> Dynamic x (Identity b)
1057−newMapDyn f d = dynamicDynIdentity $ unsafeDyn (fmap f $ readBehaviorTracked $ dynamicCurrent d) (Identity . f . runIdentity <$> dynamicUpdated d)
1058−
1059−--TODO: Avoid the duplication between this and R.zipDynWith
1060−zipDynWith :: HasSpiderTimeline x => (a -> b -> c) -> Dynamic x (Identity a) -> Dynamic x (Identity b) -> Dynamic x (Identity c)
1061−zipDynWith f da db =
1062− let eab = align (dynamicUpdated da) (dynamicUpdated db)
1063− ec = flip push eab $ \o -> do
1064− (a, b) <- case o of
1065− This (Identity a) -> do
1066− b <- readBehaviorUntracked $ dynamicCurrent db
1067− return (a, b)
1068− That (Identity b) -> do
1069− a <- readBehaviorUntracked $ dynamicCurrent da
1070− return (a, b)
1071− These (Identity a) (Identity b) -> return (a, b)
1072− return $ Just $ Identity $ f a b
1073− in dynamicDynIdentity $ unsafeDyn (f <$> readBehaviorUntracked (dynamicCurrent da) <*> readBehaviorUntracked (dynamicCurrent db)) ec
1074−
1075−unsafeDyn :: BehaviorM x (PatchTarget p) -> Event x p -> Dyn x p
1076−unsafeDyn readV0 v' = Dyn $ unsafeNewIORef x $ Left x
1077− where x = (readV0, v')
1078−
1079−-- ResultM can read behaviors and events
1080−type ResultM = EventM
1081−
1082−{-# NOINLINE unsafeNewIORef #-}
1083−unsafeNewIORef :: a -> b -> IORef b
1084−unsafeNewIORef _ b = unsafePerformIO $ newIORef b
1085−
1086−instance HasSpiderTimeline x => Functor (Event x) where
1087− fmap f = push $ return . Just . f
1088−
1089−instance Functor (Behavior x) where
1090− fmap f = pull . fmap f . readBehaviorTracked
1091−
1092−{-# INLINE push #-}
1093−push :: HasSpiderTimeline x => (a -> ComputeM x (Maybe b)) -> Event x a -> Event x b
1094−push f e = cacheEvent (pushCheap f e)
1095−
1096−{-# INLINABLE pull #-}
1097−pull :: BehaviorM x a -> Behavior x a
1098−pull a = behaviorPull $ Pull
1099− { pullCompute = a
1100− , pullValue = unsafeNewIORef a Nothing
1101−#ifdef DEBUG_NODEIDS
1102− , pullNodeId = unsafeNodeId a
1103−#endif
1104− }
1105−
1106−{-# INLINABLE switch #-}
1107−switch :: HasSpiderTimeline x => Behavior x (Event x a) -> Event x a
1108−switch a = eventSwitch $ Switch
1109− { switchParent = a
1110− , switchSubscribed = unsafeNewIORef a Nothing
1111− }
1112−
1113−coincidence :: HasSpiderTimeline x => Event x (Event x a) -> Event x a
1114−coincidence a = eventCoincidence $ Coincidence
1115− { coincidenceParent = a
1116− , coincidenceSubscribed = unsafeNewIORef a Nothing
1117− }
1118−
1119−-- Propagate the given event occurrence; before cleaning up, run the given action, which may read the state of events and behaviors
1120−run :: forall x b. HasSpiderTimeline x => [DSum (RootTrigger x) Identity] -> ResultM x b -> SpiderHost x b
1121−run roots after = do
1122− tracePropagate (Proxy :: Proxy x) $ "Running an event frame with " <> show (length roots) <> " events"
1123− t <- SpiderHost ask
1124− result <- SpiderHost $ lift $ withMVar (_spiderTimeline_lock t) $ \_ -> flip runReaderT t $ unSpiderHost $ runFrame $ do
1125− rootsToPropagate <- forM roots $ \r@(RootTrigger (_, occRef, k) :=> a) -> do
1126− occBefore <- liftIO $ do
1127− occBefore <- readIORef occRef
1128− writeIORef occRef $! DMap.insert k a occBefore
1129− return occBefore
1130− if DMap.null occBefore
1131− then do scheduleRootClear occRef
1132− return $ Just r
1133− else return Nothing
1134− forM_ (catMaybes rootsToPropagate) $ \(RootTrigger (subscribersRef, _, _) :=> Identity a) -> do
1135− propagate a subscribersRef
1136− delayedRef <- asksEventEnv eventEnvDelayedMerges
1137− let go = do
1138− delayed <- liftIO $ readIORef delayedRef
1139− case IntMap.minViewWithKey delayed of
1140− Nothing -> return ()
1141− Just ((currentHeight, cur), future) -> do
1142− tracePropagate (Proxy :: Proxy x) $ "Running height " ++ show currentHeight
1143− putCurrentHeight $ Height currentHeight
1144− liftIO $ writeIORef delayedRef $! future
1145− sequence_ cur
1146− go
1147− go
1148− putCurrentHeight maxBound
1149− after
1150− tracePropagate (Proxy :: Proxy x) "Done running an event frame"
1151− return result
1152−
1153−scheduleMerge' :: HasSpiderTimeline x => Height -> IORef Height -> EventM x () -> EventM x ()
1154−scheduleMerge' initialHeight heightRef a = scheduleMerge initialHeight $ do
1155− height <- liftIO $ readIORef heightRef
1156− currentHeight <- getCurrentHeight
1157− case height `compare` currentHeight of
1158− LT -> error "Somehow a merge's height has been decreased after it was scheduled"
1159− GT -> scheduleMerge' height heightRef a -- The height has been increased (by a coincidence event; TODO: is this the only way?)
1160− EQ -> a
1161−
1162−data SomeClear = forall a. SomeClear (IORef (Maybe a))
1163−
1164−data SomeRootClear = forall k. SomeRootClear (IORef (DMap k Identity))
1165−
1166−data SomeAssignment x = forall a. SomeAssignment {-# UNPACK #-} !(IORef a) {-# UNPACK #-} !(IORef [Weak (Invalidator x)]) a
1167−
1168−debugFinalize :: Bool
1169−debugFinalize = False
1170−
1171−mkWeakPtrWithDebug :: a -> String -> IO (Weak a)
1172−mkWeakPtrWithDebug x debugNote = do
1173− x' <- evaluate x
1174− mkWeakPtr x' $
1175− if debugFinalize
1176− then Just $ putStrLn $ "finalizing: " ++ debugNote
1177− else Nothing
1178−
1179−type WeakList a = [Weak a]
1180−
1181−{-# INLINE withIncreasedDepth #-}
1182−#ifdef DEBUG
1183−withIncreasedDepth :: CanTrace x m => m a -> m a
1184−withIncreasedDepth a = do
1185− spiderTimeline <- askSpiderTimelineEnv
1186− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) succ
1187− result <- a
1188− liftIO $ modifyIORef' (_spiderTimeline_depth spiderTimeline) pred
1189− return result
1190−#else
1191−withIncreasedDepth :: m a -> m a
1192−withIncreasedDepth = id
1193−#endif
1194−
1195−type CanTrace x m = (HasSpiderTimeline x, MonadIO m)
1196−
1197−{-# INLINE tracePropagate #-}
1198−tracePropagate :: (CanTrace x m) => proxy x -> String -> m ()
1199−tracePropagate p = traceWhen p debugPropagate
1200−
1201−{-# INLINE traceInvalidate #-}
1202−traceInvalidate :: String -> IO ()
1203−traceInvalidate = when debugInvalidate . liftIO . putStrLn
1204−
1205−{-# INLINE traceWhen #-}
1206−traceWhen :: (CanTrace x m) => proxy x -> Bool -> String -> m ()
1207−traceWhen p b message = traceMWhen p b $ return message
1208−
1209−{-# INLINE traceMWhen #-}
1210−traceMWhen :: (CanTrace x m) => proxy x -> Bool -> m String -> m ()
1211−traceMWhen _ b getMessage = when b $ do
1212− message <- getMessage
1213−#ifdef DEBUG
1214− spiderTimeline <- askSpiderTimelineEnv
1215− d <- liftIO $ readIORef $ _spiderTimeline_depth spiderTimeline
1216−#else
1217− let d = 0
1218−#endif
1219− liftIO $ putStrLn $ replicate d ' ' <> message
1220−
1221−whoCreatedIORef :: IORef a -> IO [String]
1222−whoCreatedIORef (IORef a) = whoCreated $! a
1223−
1224−#ifdef DEBUG_CYCLES
1225−groupByHead :: Eq a => [NonEmpty a] -> [(a, NonEmpty [a])]
1226−groupByHead = \case
1227− [] -> []
1228− (x :| xs) : t -> case groupByHead t of
1229− [] -> [(x, xs :| [])]
1230− l@((y, yss) : t')
1231− | x == y -> (x, xs `NonEmpty.cons` yss) : t'
1232− | otherwise -> (x, xs :| []) : l
1233−
1234−listsToForest :: Eq a => [[a]] -> Forest a
1235−listsToForest l = fmap (\(a, l') -> Node a $ listsToForest $ toList l') $ groupByHead $ catMaybes $ fmap nonEmpty l
1236−#endif
1237−
1238−{-# INLINE propagateSubscriberHold #-}
1239−propagateSubscriberHold :: forall x p. (HasSpiderTimeline x, Patch p) => Hold x p -> p -> EventM x ()
1240−propagateSubscriberHold h a = do
1241− {-# SCC "trace" #-} traceMWhen (Proxy :: Proxy x) debugPropagate $ liftIO $ do
1242− invalidators <- liftIO $ readIORef $ holdInvalidators h
1243− return $ "SubscriberHold" <> showNodeId h <> ": " ++ show (length invalidators)
1244− v <- {-# SCC "read" #-} liftIO $ readIORef $ holdValue h
1245− case {-# SCC "apply" #-} apply a v of
1246− Nothing -> return ()
1247− Just v' -> do
1248− {-# SCC "trace2" #-} withIncreasedDepth $ tracePropagate (Proxy :: Proxy x) $ "propagateSubscriberHold: assigning Hold" <> showNodeId h
1249− vRef <- {-# SCC "vRef" #-} liftIO $ evaluate $ holdValue h
1250− iRef <- {-# SCC "iRef" #-} liftIO $ evaluate $ holdInvalidators h
1251− defer $ {-# SCC "assignment" #-} SomeAssignment vRef iRef v'
1252−
1253−data SomeResetCoincidence x = forall a. SomeResetCoincidence !(EventSubscription x) !(Maybe (CoincidenceSubscribed x a)) -- The CoincidenceSubscriber will be present only if heights need to be reset
1254−
1255−runBehaviorM :: BehaviorM x a -> Maybe (Weak (Invalidator x), IORef [SomeBehaviorSubscribed x]) -> IORef [SomeHoldInit x] -> IO a
1256−runBehaviorM a mwi holdInits = runReaderT (unBehaviorM a) (mwi, holdInits)
1257−
1258−askInvalidator :: BehaviorM x (Maybe (Weak (Invalidator x)))
1259−askInvalidator = do
1260− (!m, _) <- BehaviorM ask
1261− case m of
1262− Nothing -> return Nothing
1263− Just (!wi, _) -> return $ Just wi
1264−
1265−askParentsRef :: BehaviorM x (Maybe (IORef [SomeBehaviorSubscribed x]))
1266−askParentsRef = do
1267− (!m, _) <- BehaviorM ask
1268− case m of
1269− Nothing -> return Nothing
1270− Just (_, !p) -> return $ Just p
1271−
1272−askBehaviorHoldInits :: BehaviorM x (IORef [SomeHoldInit x])
1273−askBehaviorHoldInits = do
1274− (_, !his) <- BehaviorM ask
1275− return his
1276−
1277−{-# INLINE getDynHold #-}
1278−getDynHold :: (Defer (SomeHoldInit x) m, Patch p) => Dyn x p -> m (Hold x p)
1279−getDynHold d = do
1280− mh <- liftIO $ readIORef $ unDyn d
1281− case mh of
1282− Right h -> return h
1283− Left (readV0, v') -> do
1284− holdInits <- getDeferralQueue
1285− v0 <- liftIO $ runBehaviorM readV0 Nothing holdInits
1286− h <- hold v0 v'
1287− liftIO $ writeIORef (unDyn d) $ Right h
1288− return h
1289−
1290−-- Always refers to 0
1291−{-# NOINLINE zeroRef #-}
1292−zeroRef :: IORef Height
1293−zeroRef = unsafePerformIO $ newIORef zeroHeight
1294−
1295−type CanSubscribe x m =
1296− ( HasSpiderTimeline x
1297− , HasCurrentHeight x m
1298− , Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m -- The things we need to lazily initialize state
1299− , Defer SomeClear m -- Wiping out event firings
1300− , Defer (SomeAssignment x) m -- Updating state
1301− , Defer (SomeResetCoincidence x) m
1302− , m ~ EventM x
1303− )
1304−
1305−getRootSubscribed :: GCompare k => k a -> Root x k -> Subscriber x a -> IO (WeakBagTicket, RootSubscribed x a, Maybe a)
1306−getRootSubscribed k r sub = do
1307− mSubscribed <- readIORef $ rootSubscribed r
1308− let getOcc = fmap (coerce . DMap.lookup k) $ readIORef $ rootOccurrence r
1309− case DMap.lookup k mSubscribed of
1310− Just subscribed -> {-# SCC "hitRoot" #-} do
1311− sln <- subscribeRootSubscribed subscribed sub
1312− occ <- getOcc
1313− return (sln, subscribed, occ)
1314− Nothing -> {-# SCC "missRoot" #-} do
1315− weakSelf <- newIORef $ error "getRootSubscribed: weakSelfRef not initialized"
1316− let !cached = rootSubscribed r
1317− uninitRef <- newIORef $ error "getRootsubscribed: uninitRef not initialized"
1318− (subs, sln) <- WeakBag.singleton sub weakSelf cleanupRootSubscribed
1319− when debugPropagate $ putStrLn $ "getRootSubscribed: calling rootInit"
1320− uninit <- rootInit r k $ RootTrigger (subs, rootOccurrence r, k)
1321− writeIORef uninitRef $! uninit
1322− let subscribed = RootSubscribed
1323− { rootSubscribedKey = k
1324− , rootSubscribedCachedSubscribed = cached
1325− , rootSubscribedOccurrence = getOcc
1326− , rootSubscribedSubscribers = subs
1327− , rootSubscribedUninit = uninit
1328− , rootSubscribedWeakSelf = weakSelf
1329−#ifdef DEBUG_NODEIDS
1330− , rootSubscribedNodeId = unsafeNodeId (k, r, subs)
1331−#endif
1332− }
1333− writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "RootSubscribed"
1334− 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
1335− occ <- getOcc
1336− return (sln, subscribed, occ)
1337−
1338−{-# ANN cleanupRootSubscribed "HLint: ignore Redundant bracket" #-}
1339−cleanupRootSubscribed :: RootSubscribed x a -> IO ()
1340−cleanupRootSubscribed self@(RootSubscribed { rootSubscribedKey = k, rootSubscribedCachedSubscribed = cached }) = do
1341− rootSubscribedUninit self
1342− modifyIORef' cached $ DMap.delete k
1343−
1344−{-# INLINE subscribeRootSubscribed #-}
1345−subscribeRootSubscribed :: RootSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1346−subscribeRootSubscribed subscribed sub = WeakBag.insert sub (rootSubscribedSubscribers subscribed) (rootSubscribedWeakSelf subscribed) cleanupRootSubscribed
1347−
1348−{-# INLINABLE getFanSubscribed #-}
1349−getFanSubscribed :: (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x, GCompare k) => k a -> Fan x k -> Subscriber x a -> m (WeakBagTicket, FanSubscribed x k, Maybe a)
1350−getFanSubscribed k f sub = do
1351− mSubscribed <- liftIO $ readIORef $ fanSubscribed f
1352− case mSubscribed of
1353− Just subscribed -> {-# SCC "hitFan" #-} liftIO $ do
1354− sln <- subscribeFanSubscribed k subscribed sub
1355− occ <- readIORef $ fanSubscribedOccurrence subscribed
1356− return (sln, subscribed, coerce $ DMap.lookup k =<< occ)
1357− Nothing -> {-# SCC "missFan" #-} do
1358− subscribedRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribedRef not yet initialized"
1359− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1360− s <- liftIO $ newSubscriberFan subscribedUnsafe
1361− (subscription, parentOcc) <- subscribeAndRead (fanParent f) s
1362− weakSelf <- liftIO $ newIORef $ error "getFanSubscribed: weakSelf not yet initialized"
1363− (subsForK, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupFanSubscribed
1364− subscribersRef <- liftIO $ newIORef $ error "getFanSubscribed: subscribersRef not yet initialized"
1365− occRef <- liftIO $ newIORef parentOcc
1366− when (isJust parentOcc) $ scheduleClear occRef
1367− let subscribed = FanSubscribed
1368− { fanSubscribedCachedSubscribed = fanSubscribed f
1369− , fanSubscribedOccurrence = occRef
1370− , fanSubscribedParent = subscription
1371− , fanSubscribedSubscribers = subscribersRef
1372−#ifdef DEBUG_NODEIDS
1373− , fanSubscribedNodeId = unsafeNodeId f
1374−#endif
1375− }
1376− let !self = (k, subscribed)
1377− liftIO $ writeIORef subscribersRef $! DMap.singleton k $ FanSubscribedChildren subsForK self weakSelf
1378− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug self "FanSubscribed"
1379− liftIO $ writeIORef subscribedRef $! subscribed
1380− liftIO $ writeIORef (fanSubscribed f) $ Just subscribed
1381− return (slnForSub, subscribed, coerce $ DMap.lookup k =<< parentOcc)
1382−
1383−cleanupFanSubscribed :: GCompare k => (k a, FanSubscribed x k) -> IO ()
1384−cleanupFanSubscribed (k, subscribed) = do
1385− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1386− let reducedSubscribers = DMap.delete k subscribers
1387− if DMap.null reducedSubscribers
1388− then do
1389− unsubscribe $ fanSubscribedParent subscribed
1390− -- Not necessary in this case, because this whole FanSubscribed is dead: writeIORef (fanSubscribedSubscribers subscribed) reducedSubscribers
1391− writeIORef (fanSubscribedCachedSubscribed subscribed) Nothing
1392− else writeIORef (fanSubscribedSubscribers subscribed) $! reducedSubscribers
1393−
1394−{-# INLINE subscribeFanSubscribed #-}
1395−subscribeFanSubscribed :: GCompare k => k a -> FanSubscribed x k -> Subscriber x a -> IO WeakBagTicket
1396−subscribeFanSubscribed k subscribed sub = do
1397− subscribers <- readIORef $ fanSubscribedSubscribers subscribed
1398− case DMap.lookup k subscribers of
1399− Nothing -> {-# SCC "missSubscribeFanSubscribed" #-} do
1400− let !self = (k, subscribed)
1401− weakSelf <- newIORef =<< mkWeakPtrWithDebug self "FanSubscribed"
1402− (list, sln) <- WeakBag.singleton sub weakSelf cleanupFanSubscribed
1403− 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
1404− return sln
1405− Just (FanSubscribedChildren list _ weakSelf) -> {-# SCC "hitSubscribeFanSubscribed" #-} WeakBag.insert sub list weakSelf cleanupFanSubscribed
1406−
1407−{-# INLINABLE getSwitchSubscribed #-}
1408−getSwitchSubscribed :: (Defer (SomeHoldInit x) m, Defer (SomeAssignment x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, Defer (SomeResetCoincidence x) m, HasCurrentHeight x m, HasSpiderTimeline x, m ~ EventM x) => Switch x a -> Subscriber x a -> m (WeakBagTicket, SwitchSubscribed x a, Maybe a)
1409−getSwitchSubscribed s sub = do
1410− mSubscribed <- liftIO $ readIORef $ switchSubscribed s
1411− case mSubscribed of
1412− Just subscribed -> {-# SCC "hitSwitch" #-} liftIO $ do
1413− sln <- subscribeSwitchSubscribed subscribed sub
1414− occ <- readIORef $ switchSubscribedOccurrence subscribed
1415− return (sln, subscribed, occ)
1416− Nothing -> {-# SCC "missSwitch" #-} do
1417− subscribedRef <- liftIO $ newIORef $ error "getSwitchSubscribed: subscribed has not yet been created"
1418− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1419− i <- liftIO $ newInvalidatorSwitch subscribedUnsafe
1420− mySub <- liftIO $ newSubscriberSwitch subscribedUnsafe
1421− wi <- liftIO $ mkWeakPtrWithDebug i "InvalidatorSwitch"
1422− wiRef <- liftIO $ newIORef wi
1423− parentsRef <- liftIO $ newIORef [] --TODO: This should be unnecessary, because it will always be filled with just the single parent behavior
1424− holdInits <- getDeferralQueue
1425− e <- liftIO $ runBehaviorM (readBehaviorTracked (switchParent s)) (Just (wi, parentsRef)) holdInits
1426− (subscription@(EventSubscription _ subd), parentOcc) <- subscribeAndRead e mySub
1427− heightRef <- liftIO $ newIORef =<< getEventSubscribedHeight subd
1428− subscriptionRef <- liftIO $ newIORef subscription
1429− occRef <- liftIO $ newIORef parentOcc
1430− when (isJust parentOcc) $ scheduleClear occRef
1431− weakSelf <- liftIO $ newIORef $ error "getSwitchSubscribed: weakSelf not yet initialized"
1432− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupSwitchSubscribed
1433− let !subscribed = SwitchSubscribed
1434− { switchSubscribedCachedSubscribed = switchSubscribed s
1435− , switchSubscribedOccurrence = occRef
1436− , switchSubscribedHeight = heightRef
1437− , switchSubscribedSubscribers = subs
1438− , switchSubscribedOwnInvalidator = i
1439− , switchSubscribedOwnWeakInvalidator = wiRef
1440− , switchSubscribedBehaviorParents = parentsRef
1441− , switchSubscribedParent = switchParent s
1442− , switchSubscribedCurrentParent = subscriptionRef
1443− , switchSubscribedWeakSelf = weakSelf
1444−#ifdef DEBUG_NODEIDS
1445− , switchSubscribedNodeId = unsafeNodeId s
1446−#endif
1447− }
1448− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "switchSubscribedWeakSelf"
1449− liftIO $ writeIORef subscribedRef $! subscribed
1450− liftIO $ writeIORef (switchSubscribed s) $ Just subscribed
1451− return (slnForSub, subscribed, parentOcc)
1452−
1453−cleanupSwitchSubscribed :: SwitchSubscribed x a -> IO ()
1454−cleanupSwitchSubscribed subscribed = do
1455− unsubscribe =<< readIORef (switchSubscribedCurrentParent subscribed)
1456− finalize =<< readIORef (switchSubscribedOwnWeakInvalidator subscribed) -- We don't need to get invalidated if we're dead
1457− writeIORef (switchSubscribedCachedSubscribed subscribed) Nothing
1458−
1459−{-# INLINE subscribeSwitchSubscribed #-}
1460−subscribeSwitchSubscribed :: SwitchSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1461−subscribeSwitchSubscribed subscribed sub = WeakBag.insert sub (switchSubscribedSubscribers subscribed) (switchSubscribedWeakSelf subscribed) cleanupSwitchSubscribed
1462−
1463−{-# INLINABLE getCoincidenceSubscribed #-}
1464−getCoincidenceSubscribed :: forall x a m. (Defer (SomeAssignment x) m, Defer (SomeHoldInit x) m, Defer (SomeMergeInit x) m, Defer SomeClear m, HasCurrentHeight x m, Defer (SomeResetCoincidence x) m, HasSpiderTimeline x, m ~ EventM x) => Coincidence x a -> Subscriber x a -> m (WeakBagTicket, CoincidenceSubscribed x a, Maybe a)
1465−getCoincidenceSubscribed c sub = do
1466− mSubscribed <- liftIO $ readIORef $ coincidenceSubscribed c
1467− case mSubscribed of
1468− Just subscribed -> {-# SCC "hitCoincidence" #-} liftIO $ do
1469− sln <- subscribeCoincidenceSubscribed subscribed sub
1470− occ <- readIORef $ coincidenceSubscribedOccurrence subscribed
1471− return (sln, subscribed, occ)
1472− Nothing -> {-# SCC "missCoincidence" #-} do
1473− subscribedRef <- liftIO $ newIORef $ error "getCoincidenceSubscribed: subscribed has not yet been created"
1474− subscribedUnsafe <- liftIO $ unsafeInterleaveIO $ readIORef subscribedRef
1475− subOuter <- liftIO $ newSubscriberCoincidenceOuter subscribedUnsafe
1476− (outerSubscription@(EventSubscription _ outerSubd), outerOcc) <- subscribeAndRead (coincidenceParent c) subOuter
1477− outerHeight <- liftIO $ getEventSubscribedHeight outerSubd
1478− (occ, height, mInnerSubd) <- case outerOcc of
1479− Nothing -> return (Nothing, outerHeight, Nothing)
1480− Just o -> do
1481− (occ, height, innerSubd) <- subscribeCoincidenceInner o outerHeight subscribedUnsafe
1482− return (occ, height, Just innerSubd)
1483− occRef <- liftIO $ newIORef occ
1484− when (isJust occ) $ scheduleClear occRef
1485− heightRef <- liftIO $ newIORef height
1486− innerSubdRef <- liftIO $ newIORef mInnerSubd
1487− scheduleClear innerSubdRef
1488− weakSelf <- liftIO $ newIORef $ error "getCoincidenceSubscribed: weakSelf not yet implemented"
1489− (subs, slnForSub) <- liftIO $ WeakBag.singleton sub weakSelf cleanupCoincidenceSubscribed
1490− let subscribed = CoincidenceSubscribed
1491− { coincidenceSubscribedCachedSubscribed = coincidenceSubscribed c
1492− , coincidenceSubscribedOccurrence = occRef
1493− , coincidenceSubscribedHeight = heightRef
1494− , coincidenceSubscribedSubscribers = subs
1495− , coincidenceSubscribedOuter = subOuter
1496− , coincidenceSubscribedOuterParent = outerSubscription
1497− , coincidenceSubscribedInnerParent = innerSubdRef
1498− , coincidenceSubscribedWeakSelf = weakSelf
1499−#ifdef DEBUG_NODEIDS
1500− , coincidenceSubscribedNodeId = unsafeNodeId c
1501−#endif
1502− }
1503− liftIO $ writeIORef weakSelf =<< evaluate =<< mkWeakPtrWithDebug subscribed "CoincidenceSubscribed"
1504− liftIO $ writeIORef subscribedRef $! subscribed
1505− liftIO $ writeIORef (coincidenceSubscribed c) $ Just subscribed
1506− return (slnForSub, subscribed, occ)
1507−
1508−cleanupCoincidenceSubscribed :: CoincidenceSubscribed x a -> IO ()
1509−cleanupCoincidenceSubscribed subscribed = do
1510− unsubscribe $ coincidenceSubscribedOuterParent subscribed
1511− writeIORef (coincidenceSubscribedCachedSubscribed subscribed) Nothing
1512−
1513−{-# INLINE subscribeCoincidenceSubscribed #-}
1514−subscribeCoincidenceSubscribed :: CoincidenceSubscribed x a -> Subscriber x a -> IO WeakBagTicket
1515−subscribeCoincidenceSubscribed subscribed sub = WeakBag.insert sub (coincidenceSubscribedSubscribers subscribed) (coincidenceSubscribedWeakSelf subscribed) cleanupCoincidenceSubscribed
1516−
1517−{-# INLINE merge #-}
1518−merge :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1519−merge d = cacheEvent (mergeCheap d)
1520−
1521−{-# INLINE [1] mergeCheap #-}
1522−mergeCheap :: forall k x. (HasSpiderTimeline x, GCompare k) => Dynamic x (PatchDMap k (Event x)) -> Event x (DMap k Identity)
1523−mergeCheap d = Event $ \sub -> do
1524− initialParents <- readBehaviorUntracked $ dynamicCurrent d
1525− accumRef <- liftIO $ newIORef $ error "merge: accumRef not yet initialized"
1526− heightRef <- liftIO $ newIORef $ error "merge: heightRef not yet initialized"
1527− heightBagRef <- liftIO $ newIORef $ error "merge: heightBagRef not yet initialized"
1528− parentsRef <- liftIO $ newIORef $ error "merge: parentsRef not yet initialized"
1529− let scheduleSelf height = scheduleMerge' height heightRef $ do
1530− m <- liftIO $ readIORef accumRef
1531− liftIO $ writeIORef accumRef $! DMap.empty
1532− --TODO: Assert that m is not empty
1533− subscriberPropagate sub m
1534− invalidateMyHeight = do
1535− oldHeight <- readIORef heightRef
1536− 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
1537− writeIORef heightRef $! invalidHeight
1538− subscriberInvalidateHeight sub oldHeight
1539− revalidateMyHeight = do
1540− currentHeight <- readIORef heightRef
1541− when (currentHeight == invalidHeight) $ do -- revalidateMergeHeight may be called multiple times; perhaps the's a way to finesse it to avoid this check
1542− heights <- readIORef heightBagRef
1543− parents <- readIORef parentsRef
1544− -- When the number of heights in the bag reaches the number of parents, we should have a valid height
1545− case heightBagSize heights `compare` DMap.size parents of
1546− LT -> return ()
1547− EQ -> do
1548− let height = succHeight $ heightBagMax heights
1549− when debugInvalidateHeight $ putStrLn $ "recalculateSubscriberHeight: height: " <> show height
1550− writeIORef heightRef $! height
1551− subscriberRecalculateHeight sub height
1552− GT -> error $ "revalidateMergeHeight: more heights than parents for Merge"
1553− subscriber :: forall a. k a -> Subscriber x a
1554− subscriber k = Subscriber
1555− { subscriberPropagate = \a -> do
1556− oldM <- liftIO $ readIORef accumRef
1557− let newM = DMap.insertWith (error $ "Same key fired multiple times for Merge") k (Identity a) oldM
1558− tracePropagate (Proxy :: Proxy x) $ " DMap.size oldM = " <> show (DMap.size oldM) <> "; DMap.size newM = " <> show (DMap.size newM)
1559− liftIO $ writeIORef accumRef $! newM
1560− when (DMap.null oldM) $ do -- Only schedule the firing once
1561− height <- liftIO $ readIORef heightRef
1562− --TODO: assertions about height
1563− currentHeight <- getCurrentHeight
1564− when (height <= currentHeight) $ do
1565− if height /= invalidHeight
1566− then do
1567− myStack <- liftIO $ whoCreatedIORef undefined --TODO
1568− error $ "Height (" ++ show height ++ ") is not greater than current height (" ++ show currentHeight ++ ")\n" ++ unlines (reverse myStack)
1569− else liftIO $ do
1570−#ifdef DEBUG_CYCLES
1571− nodesInvolvedInCycle <- walkInvalidHeightParents $ eventSubscribedMerge subscribed
1572− stacks <- forM nodesInvolvedInCycle $ \(Some.This es) -> whoCreatedEventSubscribed es
1573− let cycleInfo = ":\n" <> drawForest (listsToForest stacks)
1574−#else
1575− let cycleInfo = ""
1576−#endif
1577− error $ "Causality loop found" <> cycleInfo
1578− scheduleSelf height
1579− , subscriberInvalidateHeight = \old -> do --TODO: When removing a parent doesn't actually change the height, maybe we can avoid invalidating
1580− modifyIORef' heightBagRef $ heightBagRemove old
1581− invalidateMyHeight
1582− , subscriberRecalculateHeight = \new -> do
1583− modifyIORef' heightBagRef $ heightBagAdd new
1584− revalidateMyHeight
1585− }
1586− subscribers :: [(Maybe (DSum k Identity), Height, DSum k (MergeSubscribedParent x))] <- forM (DMap.toList initialParents) $ \(k :=> e) -> do
1587− let s = subscriber k
1588− (subscription@(EventSubscription _ parentSubd), parentOcc) <- subscribeAndRead e s
1589− height <- liftIO $ getEventSubscribedHeight parentSubd
1590− return $ (fmap (\x -> k :=> Identity x) parentOcc, height, k :=> MergeSubscribedParent subscription)
1591− let dm = DMap.fromDistinctAscList $ mapMaybe (\(x, _, _) -> x) subscribers
1592− heights = fmap (\(_, h, _) -> h) subscribers --TODO: Assert that there's no invalidHeight in here
1593− myHeightBag = heightBagFromList $ filter (/= invalidHeight) heights
1594− myHeight = if invalidHeight `elem` heights
1595− then invalidHeight
1596− else succHeight $ heightBagMax myHeightBag
1597− currentHeight <- getCurrentHeight
1598− let (occ, accum) = if currentHeight >= myHeight -- If we should have fired by now
1599− then (if DMap.null dm then Nothing else Just dm, DMap.empty)
1600− else (Nothing, dm)
1601− unless (DMap.null accum) $ do
1602− scheduleSelf myHeight
1603− occRef <- liftIO $ newIORef occ
1604− when (isJust occ) $ scheduleClear occRef
1605− liftIO $ writeIORef accumRef $! accum
1606− liftIO $ writeIORef heightRef $! myHeight
1607− liftIO $ writeIORef heightBagRef $! myHeightBag
1608− changeSubdRef <- liftIO $ newIORef $ error "getMergeSubscribed: changeSubdRef not yet initialized"
1609− liftIO $ writeIORef parentsRef $! DMap.fromDistinctAscList $ map (\(_, _, x) -> x) subscribers
1610− let updateMe (PatchDMap p) = do
1611− oldParents <- liftIO $ readIORef parentsRef
1612− let f (subscriptionsToKill, ps) (k :=> ComposeMaybe me) = do
1613− (mOldSubd, newPs) <- case me of
1614− Nothing -> return $ DMap.updateLookupWithKey (\_ _ -> Nothing) k ps
1615− Just e -> do
1616− let s = subscriber k
1617− subscription@(EventSubscription _ subd) <- subscribe e s
1618− newParentHeight <- liftIO $ getEventSubscribedHeight subd
1619− let newParent = MergeSubscribedParent subscription
1620− liftIO $ modifyIORef' heightBagRef $ heightBagAdd newParentHeight
1621− return $ DMap.insertLookupWithKey' (\_ new _ -> new) k newParent ps
1622− forM_ mOldSubd $ \oldSubd -> do
1623− oldHeight <- liftIO $ getEventSubscribedHeight $ _eventSubscription_subscribed $ unMergeSubscribedParent oldSubd
1624− liftIO $ modifyIORef heightBagRef $ heightBagRemove oldHeight
1625− return (maybeToList (unMergeSubscribedParent <$> mOldSubd) ++ subscriptionsToKill, newPs)
1626− (subscriptionsToKill, newParents) <- foldM f ([], oldParents) $ DMap.toList p --TODO: I think this runEventM is OK, since no events are firing at this time, but it might not be
1627− liftIO $ writeIORef parentsRef $! newParents
1628− return subscriptionsToKill
1629− defer $ SomeMergeInit $ do
1630− let s = Subscriber
1631− { subscriberPropagate = \a -> {-# SCC "traverseMergeChange" #-} do
1632− tracePropagate (Proxy :: Proxy x) $ "SubscriberMerge/Change"
1633− defer $ SomeMergeUpdate (updateMe a) invalidateMyHeight revalidateMyHeight
1634− , subscriberInvalidateHeight = \_ -> return ()
1635− , subscriberRecalculateHeight = \_ -> return ()
1636− }
1637− (EventSubscription _ changeSubd, change) <- subscribeAndRead (dynamicUpdated d) s
1638− forM_ change $ \c -> defer $ SomeMergeUpdate (updateMe c) invalidateMyHeight revalidateMyHeight
1639− liftIO $ writeIORef changeSubdRef (s, changeSubd)
1640− let unsubscribeAll = do
1641− parents <- readIORef parentsRef
1642− forM_ (DMap.toList parents) $ \(_ :=> MergeSubscribedParent s) -> unsubscribe s
1643− return ( EventSubscription unsubscribeAll $ EventSubscribed heightRef $ toAny (parentsRef, changeSubdRef)
1644− , occ
1645− )
1646−
1647−newtype EventSelector x k = EventSelector { select :: forall a. k a -> Event x a }
1648−
1649−fan :: (HasSpiderTimeline x, GCompare k) => Event x (DMap k Identity) -> EventSelector x k
1650−fan e =
1651− let f = Fan
1652− { fanParent = e
1653− , fanSubscribed = unsafeNewIORef e Nothing
1654− }
1655− in EventSelector $ \k -> eventFan k f
1656−
1657−runHoldInits :: HasSpiderTimeline x => IORef [SomeHoldInit x] -> IORef [SomeMergeInit x] -> EventM x ()
1658−runHoldInits holdInitRef mergeInitRef = do
1659− holdInits <- liftIO $ readIORef holdInitRef
1660− mergeInits <- liftIO $ readIORef mergeInitRef
1661− unless (null holdInits && null mergeInits) $ do
1662− liftIO $ writeIORef holdInitRef []
1663− liftIO $ writeIORef mergeInitRef []
1664− mapM_ initHold holdInits
1665− mapM_ unSomeMergeInit mergeInits
1666− runHoldInits holdInitRef mergeInitRef
1667−
1668−initHold :: HasSpiderTimeline x => SomeHoldInit x -> EventM x ()
1669−initHold (SomeHoldInit h) = void $ getHoldEventSubscription h
1670−
1671−newEventEnv :: IO (EventEnv x)
1672−newEventEnv = do
1673− toAssignRef <- newIORef [] -- This should only actually get used when events are firing
1674− holdInitRef <- newIORef []
1675− mergeUpdateRef <- newIORef []
1676− mergeInitRef <- newIORef []
1677− heightRef <- newIORef zeroHeight
1678− toClearRef <- newIORef []
1679− toClearRootRef <- newIORef []
1680− coincidenceInfosRef <- newIORef []
1681− delayedRef <- newIORef IntMap.empty
1682− return $ EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef
1683−
1684−clearEventEnv :: EventEnv x -> IO ()
1685−clearEventEnv (EventEnv toAssignRef holdInitRef mergeUpdateRef mergeInitRef toClearRef toClearRootRef heightRef coincidenceInfosRef delayedRef) = do
1686− writeIORef toAssignRef []
1687− writeIORef holdInitRef []
1688− writeIORef mergeUpdateRef []
1689− writeIORef mergeInitRef []
1690− writeIORef heightRef zeroHeight
1691− writeIORef toClearRef []
1692− writeIORef toClearRootRef []
1693− writeIORef coincidenceInfosRef []
1694− writeIORef delayedRef IntMap.empty
1695−
1696−-- | Run an event action outside of a frame
1697−runFrame :: forall x a. HasSpiderTimeline x => EventM x a -> SpiderHost x a --TODO: This function also needs to hold the mutex
1698−runFrame a = SpiderHost $ ask >>= \_ -> lift $ do
1699− let env = _spiderTimeline_eventEnv (spiderTimeline :: SpiderTimelineEnv x)
1700− let go = do
1701− result <- a
1702− runHoldInits (eventEnvHoldInits env) (eventEnvMergeInits env) -- This must happen before doing the assignments, in case subscribing a Hold causes existing Holds to be read by the newly-propagated events
1703− return result
1704− result <- runEventM go
1705− toClear <- readIORef $ eventEnvClears env
1706− forM_ toClear $ \(SomeClear ref) -> {-# SCC "clear" #-} writeIORef ref Nothing
1707− toClearRoot <- readIORef $ eventEnvRootClears env
1708− forM_ toClearRoot $ \(SomeRootClear ref) -> {-# SCC "rootClear" #-} writeIORef ref $! DMap.empty
1709− toAssign <- readIORef $ eventEnvAssignments env
1710− toReconnectRef <- newIORef []
1711− coincidenceInfos <- readIORef $ eventEnvResetCoincidences env
1712− forM_ toAssign $ \(SomeAssignment vRef iRef v) -> {-# SCC "assignment" #-} do
1713− writeIORef vRef v
1714− when debugInvalidate $ putStrLn $ "Invalidating Hold"
1715− writeIORef iRef =<< evaluate =<< invalidate toReconnectRef =<< readIORef iRef
1716− mergeUpdates <- readIORef $ eventEnvMergeUpdates env
1717− writeIORef (eventEnvMergeUpdates env) []
1718− when debugPropagate $ putStrLn "Updating merges"
1719− mergeSubscriptionsToKill <- runEventM $ concat <$> mapM _someMergeUpdate_update mergeUpdates
1720− when debugPropagate $ putStrLn "Updating merges done"
1721− toReconnect <- readIORef toReconnectRef
1722− clearEventEnv env
1723− switchSubscriptionsToKill <- forM toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1724− oldSubscription <- readIORef $ switchSubscribedCurrentParent subscribed
1725− wi <- readIORef $ switchSubscribedOwnWeakInvalidator subscribed
1726− when debugInvalidate $ putStrLn $ "Finalizing invalidator for Switch" <> showNodeId subscribed
1727− finalize wi
1728− i <- evaluate $ switchSubscribedOwnInvalidator subscribed
1729− wi' <- mkWeakPtrWithDebug i "wi'"
1730− writeIORef (switchSubscribedOwnWeakInvalidator subscribed) $! wi'
1731− writeIORef (switchSubscribedBehaviorParents subscribed) []
1732− writeIORef (eventEnvHoldInits env) [] --TODO: Should we reuse this?
1733− e <- runBehaviorM (readBehaviorTracked (switchSubscribedParent subscribed)) (Just (wi', switchSubscribedBehaviorParents subscribed)) $ eventEnvHoldInits env
1734− runEventM $ runHoldInits (eventEnvHoldInits env) (eventEnvMergeInits env) --TODO: Is this actually OK? It seems like it should be, since we know that no events are firing at this point, but it still seems inelegant
1735− --TODO: Make sure we touch the pieces of the SwitchSubscribed at the appropriate times
1736− sub <- newSubscriberSwitch subscribed
1737− subscription <- runReaderT (unSpiderHost (runFrame ({-# SCC "subscribeSwitch" #-} subscribe e sub))) spiderTimeline --TODO: Assert that the event isn't firing --TODO: This should not loop because none of the events should be firing, but still, it is inefficient
1738− {-
1739− stackTrace <- liftIO $ fmap renderStack $ ccsToStrings =<< (getCCSOf $! switchSubscribedParent subscribed)
1740− liftIO $ putStrLn $ (++stackTrace) $ "subd' subscribed to " ++ case e of
1741− EventRoot _ -> "EventRoot"
1742− EventNever -> "EventNever"
1743− _ -> "something else"
1744− -}
1745− writeIORef (switchSubscribedCurrentParent subscribed) $! subscription
1746− return oldSubscription
1747− liftIO $ mapM_ unsubscribe mergeSubscriptionsToKill
1748− liftIO $ mapM_ unsubscribe switchSubscriptionsToKill
1749− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> {-# SCC "switchSubscribed" #-} do
1750− EventSubscription _ subd' <- readIORef $ switchSubscribedCurrentParent subscribed
1751− parentHeight <- getEventSubscribedHeight subd'
1752− myHeight <- readIORef $ switchSubscribedHeight subscribed
1753− when (parentHeight /= myHeight) $ do
1754− writeIORef (switchSubscribedHeight subscribed) $! invalidHeight
1755− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ invalidateSubscriberHeight myHeight
1756− 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
1757− forM_ coincidenceInfos $ \(SomeResetCoincidence subscription mcs) -> do
1758− unsubscribe subscription
1759− mapM_ invalidateCoincidenceHeight mcs
1760− forM_ coincidenceInfos $ \(SomeResetCoincidence _ mcs) -> mapM_ recalculateCoincidenceHeight mcs
1761− mapM_ _someMergeUpdate_recalculateHeight mergeUpdates
1762− forM_ toReconnect $ \(SomeSwitchSubscribed subscribed) -> do
1763− height <- calculateSwitchHeight subscribed
1764− updateSwitchHeight height subscribed
1765− return result
1766−
1767−newtype Height = Height { unHeight :: Int } deriving (Show, Read, Eq, Ord, Bounded)
1768−
1769−{-# INLINE zeroHeight #-}
1770−zeroHeight :: Height
1771−zeroHeight = Height 0
1772−
1773−{-# INLINE invalidHeight #-}
1774−invalidHeight :: Height
1775−invalidHeight = Height (-1000)
1776−
1777−#ifdef DEBUG_CYCLES
1778−-- | An invalid height that is currently being traversed, e.g. by walkInvalidHeightParents
1779−{-# INLINE invalidHeightBeingTraversed #-}
1780−invalidHeightBeingTraversed :: Height
1781−invalidHeightBeingTraversed = Height (-1001)
1782−#endif
1783−
1784−{-# INLINE succHeight #-}
1785−succHeight :: Height -> Height
1786−succHeight h@(Height a) =
1787− if h == invalidHeight
1788− then invalidHeight
1789− else Height $ succ a
1790−
1791−invalidateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1792−invalidateCoincidenceHeight subscribed = do
1793− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1794− when (oldHeight /= invalidHeight) $ do
1795− writeIORef (coincidenceSubscribedHeight subscribed) $! invalidHeight
1796− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ invalidateSubscriberHeight oldHeight
1797−
1798−updateSwitchHeight :: Height -> SwitchSubscribed x a -> IO ()
1799−updateSwitchHeight new subscribed = do
1800− oldHeight <- readIORef $ switchSubscribedHeight subscribed
1801− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1802− when (new /= invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1803− writeIORef (switchSubscribedHeight subscribed) $! new
1804− WeakBag.traverse (switchSubscribedSubscribers subscribed) $ recalculateSubscriberHeight new
1805−
1806−recalculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO ()
1807−recalculateCoincidenceHeight subscribed = do
1808− oldHeight <- readIORef $ coincidenceSubscribedHeight subscribed
1809− when (oldHeight == invalidHeight) $ do --TODO: This 'when' should probably be an assertion
1810− height <- calculateCoincidenceHeight subscribed
1811− when (height /= invalidHeight) $ do
1812− writeIORef (coincidenceSubscribedHeight subscribed) $! height
1813− WeakBag.traverse (coincidenceSubscribedSubscribers subscribed) $ recalculateSubscriberHeight height
1814−
1815−calculateSwitchHeight :: SwitchSubscribed x a -> IO Height
1816−calculateSwitchHeight subscribed = getEventSubscribedHeight . _eventSubscription_subscribed =<< readIORef (switchSubscribedCurrentParent subscribed)
1817−
1818−calculateCoincidenceHeight :: CoincidenceSubscribed x a -> IO Height
1819−calculateCoincidenceHeight subscribed = do
1820− outerHeight <- getEventSubscribedHeight $ _eventSubscription_subscribed $ coincidenceSubscribedOuterParent subscribed
1821− innerHeight <- maybe (return zeroHeight) getEventSubscribedHeight =<< readIORef (coincidenceSubscribedInnerParent subscribed)
1822− return $ if outerHeight == invalidHeight || innerHeight == invalidHeight then invalidHeight else max outerHeight innerHeight
1823−
1824−data SomeSwitchSubscribed x = forall a. SomeSwitchSubscribed {-# NOUNPACK #-} (SwitchSubscribed x a)
1825−
1826−invalidate :: IORef [SomeSwitchSubscribed x] -> WeakList (Invalidator x) -> IO (WeakList (Invalidator x))
1827−invalidate toReconnectRef wis = do
1828− forM_ wis $ \wi -> do
1829− mi <- deRefWeak wi
1830− case mi of
1831− Nothing -> do
1832− traceInvalidate "invalidate Dead"
1833− return () --TODO: Should we clean this up here?
1834− Just i -> do
1835− finalize wi -- Once something's invalidated, it doesn't need to hang around; this will change when some things are strict
1836− case i of
1837− InvalidatorPull p -> do
1838− traceInvalidate $ "invalidate: Pull" <> showNodeId p
1839− mVal <- readIORef $ pullValue p
1840− forM_ mVal $ \val -> do
1841− writeIORef (pullValue p) Nothing
1842− writeIORef (pullSubscribedInvalidators val) =<< evaluate =<< invalidate toReconnectRef =<< readIORef (pullSubscribedInvalidators val)
1843− InvalidatorSwitch subscribed -> do
1844− traceInvalidate $ "invalidate: Switch" <> showNodeId subscribed
1845− modifyIORef' toReconnectRef (SomeSwitchSubscribed subscribed :)
1846− 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
1847−
1848−--------------------------------------------------------------------------------
1849−-- Reflex integration
1850−--------------------------------------------------------------------------------
1851−
1852−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1853−-- When we're specializing, we include this from Reflex.Class instead, because
1854−-- it needs access to these types; when we're not, we want them here, so our
1855−-- instance isn't an orphan instance.
1856−#include "SpiderTimeline.include.hs"
1857−#include "SpiderTimelineHost.include.hs"
1858−#endif
1859−
1860−unsafeNewSpiderTimelineEnv :: forall x. IO (SpiderTimelineEnv x)
1861−unsafeNewSpiderTimelineEnv = do
1862− lock <- newMVar ()
1863− env <- newEventEnv
1864−#ifdef DEBUG
1865− depthRef <- newIORef 0
1866−#endif
1867− return $ SpiderTimelineEnv
1868− { _spiderTimeline_lock = lock
1869− , _spiderTimeline_eventEnv = env
1870−#ifdef DEBUG
1871− , _spiderTimeline_depth = depthRef
1872−#endif
1873− }
1874−
1875−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1876−-- | Create a new SpiderTimelineEnv
1877−newSpiderTimeline :: IO (Some SpiderTimelineEnv)
1878−newSpiderTimeline = withSpiderTimeline (pure . Some.This)
1879−
1880−data LocalSpiderTimeline x s
1881−
1882−instance Reifies s (SpiderTimelineEnv x) =>
1883− HasSpiderTimeline (LocalSpiderTimeline x s) where
1884− spiderTimeline = localSpiderTimeline (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
1885−
1886−localSpiderTimeline
1887− :: Proxy s
1888− -> SpiderTimelineEnv x
1889− -> SpiderTimelineEnv (LocalSpiderTimeline x s)
1890−localSpiderTimeline _ = unsafeCoerce
1891−
1892−-- | Pass a new timeline to the given function.
1893−withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
1894−withSpiderTimeline k = do
1895− env <- unsafeNewSpiderTimelineEnv
1896− reify env $ \s -> k $ localSpiderTimeline s env
1897−#endif
1898−
1899−newtype SpiderPullM x a = SpiderPullM (BehaviorM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1900−
1901−newtype SpiderPushM x a = SpiderPushM (ComputeM x a) deriving (Functor, Applicative, Monad, MonadIO, MonadFix)
1902−
1903−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1904−instance HasSpiderTimeline x => R.Reflex (SpiderTimeline x) where
1905− {-# SPECIALIZE instance R.Reflex (SpiderTimeline Global) #-}
1906− newtype Behavior (SpiderTimeline x) a = SpiderBehavior { unSpiderBehavior :: Behavior x a }
1907− newtype Event (SpiderTimeline x) a = SpiderEvent { unSpiderEvent :: Event x a }
1908− newtype Dynamic (SpiderTimeline x) a = SpiderDynamic { unSpiderDynamic :: Dynamic x (Identity a) } -- deriving (Functor, Applicative, Monad)
1909− newtype Incremental (SpiderTimeline x) p = SpiderIncremental { unSpiderIncremental :: Dynamic x p }
1910− type PullM (SpiderTimeline x) = SpiderPullM x
1911− type PushM (SpiderTimeline x) = SpiderPushM x
1912− {-# INLINABLE never #-}
1913− never = SpiderEvent eventNever
1914− {-# INLINABLE constant #-}
1915− constant = SpiderBehavior . behaviorConst
1916− {-# INLINE push #-}
1917− push f = SpiderEvent . push (coerce f) . unSpiderEvent
1918− {-# INLINABLE pull #-}
1919− pull = SpiderBehavior . pull . coerce
1920− {-# INLINABLE merge #-}
1921− merge = SpiderEvent . merge . dynamicConst . (coerce :: DMap k (R.Event (SpiderTimeline x)) -> DMap k (Event x))
1922− {-# INLINABLE fan #-}
1923− fan e = R.EventSelector $ SpiderEvent . select (fan (unSpiderEvent e))
1924− {-# INLINABLE switch #-}
1925− switch = SpiderEvent . switch . (coerce :: Behavior x (R.Event (SpiderTimeline x) a) -> Behavior x (Event x a)) . unSpiderBehavior
1926− {-# INLINABLE coincidence #-}
1927− coincidence = SpiderEvent . coincidence . (coerce :: Event x (R.Event (SpiderTimeline x) a) -> Event x (Event x a)) . unSpiderEvent
1928− {-# INLINABLE current #-}
1929− current = SpiderBehavior . dynamicCurrent . unSpiderDynamic
1930− {-# INLINABLE updated #-}
1931− updated = SpiderEvent . fmap runIdentity . dynamicUpdated . unSpiderDynamic
1932− {-# INLINABLE unsafeBuildDynamic #-}
1933− unsafeBuildDynamic readV0 v' = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (coerce readV0) $ coerce $ unSpiderEvent v'
1934− {-# INLINABLE unsafeBuildIncremental #-}
1935− unsafeBuildIncremental readV0 dv = SpiderIncremental $ dynamicDyn $ unsafeDyn (coerce readV0) $ unSpiderEvent dv
1936− {-# INLINABLE mergeIncremental #-}
1937− mergeIncremental = SpiderEvent . merge . (unsafeCoerce :: Dynamic x (PatchDMap k (R.Event (SpiderTimeline x))) -> Dynamic x (PatchDMap k (Event x))) . unSpiderIncremental
1938− {-# INLINABLE currentIncremental #-}
1939− currentIncremental = SpiderBehavior . dynamicCurrent . unSpiderIncremental
1940− {-# INLINABLE updatedIncremental #-}
1941− updatedIncremental = SpiderEvent . dynamicUpdated . unSpiderIncremental
1942− {-# INLINABLE incrementalToDynamic #-}
1943− incrementalToDynamic (SpiderIncremental i) = SpiderDynamic $ dynamicDynIdentity $ unsafeDyn (readBehaviorUntracked $ dynamicCurrent i) $ flip push (dynamicUpdated i) $ \p -> do
1944− c <- readBehaviorUntracked $ dynamicCurrent i
1945− return $ Identity <$> apply p c --TODO: Avoid the redundant 'apply'
1946− eventCoercion Coercion = Coercion
1947− behaviorCoercion Coercion = Coercion
1948− dynamicCoercion = unsafeCoerce --TODO: How can we avoid this unsafeCoerce? This is safe only because we know how Identity works as a Patch instance
1949−#endif
1950−
1951−data RootTrigger x a = forall k. GCompare k => RootTrigger (WeakBag (Subscriber x a), IORef (DMap k Identity), k a)
1952−
1953−data SpiderEventHandle x a = SpiderEventHandle
1954− { spiderEventHandleSubscription :: EventSubscription x
1955− , spiderEventHandleValue :: IORef (Maybe a)
1956− }
1957−
1958−instance MonadRef (EventM x) where
1959− type Ref (EventM x) = Ref IO
1960− {-# INLINABLE newRef #-}
1961− {-# INLINABLE readRef #-}
1962− {-# INLINABLE writeRef #-}
1963− newRef = liftIO . newRef
1964− readRef = liftIO . readRef
1965− writeRef r a = liftIO $ writeRef r a
1966−
1967−instance MonadAtomicRef (EventM x) where
1968− {-# INLINABLE atomicModifyRef #-}
1969− atomicModifyRef r f = liftIO $ atomicModifyRef r f
1970−
1971−-- | The monad for actions that manipulate a Spider timeline identified by @x@
1972−newtype SpiderHost x a = SpiderHost { unSpiderHost :: ReaderT (SpiderTimelineEnv x) IO a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1973−
1974−instance Monad (SpiderHost x) where
1975− {-# INLINABLE (>>=) #-}
1976− SpiderHost x >>= f = SpiderHost $ x >>= unSpiderHost . f
1977− {-# INLINABLE (>>) #-}
1978− SpiderHost x >> SpiderHost y = SpiderHost $ x >> y
1979− {-# INLINABLE return #-}
1980− return x = SpiderHost $ return x
1981− {-# INLINABLE fail #-}
1982− fail s = SpiderHost $ fail s
1983−
1984−-- | Run an action affecting the global Spider timeline; this will be guarded by
1985−-- a mutex for that timeline
1986−runSpiderHost :: SpiderHost Global a -> IO a
1987−runSpiderHost (SpiderHost a) = runReaderT a globalSpiderTimelineEnv
1988−
1989−#ifndef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
1990−-- | Run an action affecting a given Spider timeline; this will be guarded by a
1991−-- mutex for that timeline
1992−runSpiderHostForTimeline :: SpiderHost x a -> SpiderTimelineEnv x -> IO a
1993−runSpiderHostForTimeline (SpiderHost a) = runReaderT a
1994−#endif
1995−
1996−newtype SpiderHostFrame x a = SpiderHostFrame { runSpiderHostFrame :: EventM x a } deriving (Functor, Applicative, MonadFix, MonadIO, MonadException, MonadAsyncException)
1997−
1998−instance Monad (SpiderHostFrame x) where
1999− {-# INLINABLE (>>=) #-}
2000− SpiderHostFrame x >>= f = SpiderHostFrame $ x >>= runSpiderHostFrame . f
2001− {-# INLINABLE (>>) #-}
2002− SpiderHostFrame x >> SpiderHostFrame y = SpiderHostFrame $ x >> y
2003− {-# INLINABLE return #-}
2004− return x = SpiderHostFrame $ return x
2005− {-# INLINABLE fail #-}
2006− fail s = SpiderHostFrame $ fail s
2007−
2008−newEventWithTriggerIO :: (RootTrigger x a -> IO (IO ())) -> IO (Event x a)
2009−newEventWithTriggerIO f = do
2010− es <- newFanEventWithTriggerIO $ \Refl -> f
2011− return $ select es Refl
2012−
2013−newFanEventWithTriggerIO :: GCompare k => (forall a. k a -> RootTrigger x a -> IO (IO ())) -> IO (EventSelector x k)
2014−newFanEventWithTriggerIO f = do
2015− occRef <- newIORef DMap.empty
2016− subscribedRef <- newIORef DMap.empty
2017− let !r = Root
2018− { rootOccurrence = occRef
2019− , rootSubscribed = subscribedRef
2020− , rootInit = f
2021− }
2022− return $ EventSelector $ \k -> eventRoot k r
2023−
2024−newtype ReadPhase x a = ReadPhase (ResultM x a) deriving (Functor, Applicative, Monad, MonadFix)
2025−
2026−instance MonadRef (SpiderHost x) where
2027− type Ref (SpiderHost x) = Ref IO
2028− newRef = SpiderHost . newRef
2029− readRef = SpiderHost . readRef
2030− writeRef r = SpiderHost . writeRef r
2031−
2032−instance MonadAtomicRef (SpiderHost x) where
2033− atomicModifyRef r = SpiderHost . atomicModifyRef r
2034−
2035−instance MonadRef (SpiderHostFrame x) where
2036− type Ref (SpiderHostFrame x) = Ref IO
2037− newRef = SpiderHostFrame . newRef
2038− readRef = SpiderHostFrame . readRef
2039− writeRef r = SpiderHostFrame . writeRef r
2040−
2041−instance MonadAtomicRef (SpiderHostFrame x) where
2042− atomicModifyRef r = SpiderHostFrame . atomicModifyRef r
2043−
2044−instance PrimMonad (SpiderHostFrame x) where
2045− type PrimState (SpiderHostFrame x) = PrimState IO
2046− primitive = SpiderHostFrame . EventM . primitive
src/Reflex/Spider/SpiderTimeline.include.hsdeleted−121
@@ -1,121 +0,0 @@
1−-- | Designates the default, global Spider timeline
2−data SpiderTimeline x
3−type role SpiderTimeline nominal
4−
5−-- | The default, global Spider environment
6−type Spider = SpiderTimeline Global
7−
8−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (EventM x) where
9− {-# INLINABLE sample #-}
10− sample (SpiderBehavior b) = readBehaviorUntracked b
11−
12−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (EventM x) where
13− {-# INLINABLE hold #-}
14− hold = holdSpiderEventM
15− {-# INLINABLE holdDyn #-}
16− holdDyn = holdDynSpiderEventM
17− {-# INLINABLE holdIncremental #-}
18− holdIncremental = holdIncrementalSpiderEventM
19−
20−instance (
21−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
22− x ~ Global
23−#endif
24− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPullM x) where
25− {-# INLINABLE sample #-}
26− sample = coerce . readBehaviorTracked . unSpiderBehavior
27−
28−instance (
29−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
30− x ~ Global
31−#endif
32− ) => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderPushM x) where
33− {-# INLINABLE sample #-}
34− sample (SpiderBehavior b) = SpiderPushM $ readBehaviorUntracked b
35−
36−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderPushM x) where
37− {-# INLINABLE hold #-}
38− hold v0 e = Reflex.Class.current <$> Reflex.Class.holdDyn v0 e
39− {-# INLINABLE holdDyn #-}
40− holdDyn v0 (SpiderEvent e) = SpiderPushM $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce e
41− {-# INLINABLE holdIncremental #-}
42− holdIncremental v0 (SpiderEvent e) = SpiderPushM $ SpiderIncremental . dynamicHold <$> Reflex.Spider.Internal.hold v0 e
43−
44−instance HasSpiderTimeline x => Monad (Reflex.Class.Dynamic (SpiderTimeline x)) where
45− {-# INLINE return #-}
46− return = pure
47− {-# INLINE (>>=) #-}
48− x >>= f = SpiderDynamic $ dynamicDynIdentity $ newJoinDyn $ newMapDyn (unSpiderDynamic . f) $ unSpiderDynamic x
49− {-# INLINE (>>) #-}
50− _ >> y = y
51− {-# INLINE fail #-}
52− fail _ = error "Dynamic does not support 'fail'"
53−
54−newJoinDyn :: HasSpiderTimeline x => Reflex.Spider.Internal.Dynamic x (Identity (Reflex.Spider.Internal.Dynamic x (Identity a))) -> Reflex.Spider.Internal.Dyn x (Identity a)
55−newJoinDyn d =
56− let readV0 = readBehaviorTracked . dynamicCurrent =<< readBehaviorTracked (dynamicCurrent d)
57− eOuter = Reflex.Spider.Internal.push (fmap (Just . Identity) . readBehaviorUntracked . dynamicCurrent . runIdentity) $ dynamicUpdated d
58− eInner = Reflex.Spider.Internal.switch $ dynamicUpdated <$> dynamicCurrent d
59− eBoth = Reflex.Spider.Internal.coincidence $ dynamicUpdated . runIdentity <$> dynamicUpdated d
60− v' = unSpiderEvent $ Reflex.Class.leftmost $ map SpiderEvent [eBoth, eOuter, eInner]
61− in unsafeDyn readV0 v'
62−
63−instance HasSpiderTimeline x => Functor (Reflex.Class.Dynamic (SpiderTimeline x)) where
64− fmap f = SpiderDynamic . newMapDyn f . unSpiderDynamic
65−
66−instance HasSpiderTimeline x => Applicative (Reflex.Class.Dynamic (SpiderTimeline x)) where
67− pure = SpiderDynamic . dynamicConst
68− SpiderDynamic a <*> SpiderDynamic b = SpiderDynamic $ Reflex.Spider.Internal.zipDynWith ($) a b
69− _ *> b = b
70− a <* _ = a
71−
72−holdSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Behavior (SpiderTimeline x) a)
73−holdSpiderEventM v0 e = fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
74−
75−holdDynSpiderEventM :: HasSpiderTimeline x => a -> Reflex.Class.Event (SpiderTimeline x) a -> EventM x (Reflex.Class.Dynamic (SpiderTimeline x) a)
76−holdDynSpiderEventM v0 e = fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
77−
78−holdIncrementalSpiderEventM :: (HasSpiderTimeline x, Patch p) => PatchTarget p -> Reflex.Class.Event (SpiderTimeline x) p -> EventM x (Reflex.Class.Incremental (SpiderTimeline x) p)
79−holdIncrementalSpiderEventM v0 e = fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
80−
81−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHost x) where
82− {-# INLINABLE hold #-}
83− hold v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.hold v0 e
84− {-# INLINABLE holdDyn #-}
85− holdDyn v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdDyn v0 e
86− {-# INLINABLE holdIncremental #-}
87− holdIncremental v0 e = runFrame . runSpiderHostFrame $ Reflex.Class.holdIncremental v0 e
88−
89−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHostFrame x) where
90− sample = SpiderHostFrame . readBehaviorUntracked . unSpiderBehavior --TODO: This can cause problems with laziness, so we should get rid of it if we can
91−
92−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (SpiderHostFrame x) where
93− {-# INLINABLE hold #-}
94− hold v0 e = SpiderHostFrame $ fmap (SpiderBehavior . behaviorHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
95− {-# INLINABLE holdDyn #-}
96− holdDyn v0 e = SpiderHostFrame $ fmap (SpiderDynamic . dynamicHoldIdentity) $ Reflex.Spider.Internal.hold v0 $ coerce $ unSpiderEvent e
97− {-# INLINABLE holdIncremental #-}
98− holdIncremental v0 e = SpiderHostFrame $ fmap (SpiderIncremental . dynamicHold) $ Reflex.Spider.Internal.hold v0 $ unSpiderEvent e
99−
100−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (SpiderHost x) where
101− {-# INLINABLE sample #-}
102− sample = runFrame . readBehaviorUntracked . unSpiderBehavior
103−
104−instance HasSpiderTimeline x => Reflex.Class.MonadSample (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
105− {-# INLINABLE sample #-}
106− sample = Reflex.Spider.Internal.ReadPhase . Reflex.Class.sample
107−
108−instance HasSpiderTimeline x => Reflex.Class.MonadHold (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
109− {-# INLINABLE hold #-}
110− hold v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.hold v0 e
111− {-# INLINABLE holdDyn #-}
112− holdDyn v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdDyn v0 e
113− {-# INLINABLE holdIncremental #-}
114− holdIncremental v0 e = Reflex.Spider.Internal.ReadPhase $ Reflex.Class.holdIncremental v0 e
115−
116−--------------------------------------------------------------------------------
117−-- Deprecated items
118−--------------------------------------------------------------------------------
119−
120−{-# DEPRECATED SpiderEnv "Use 'SpiderTimelineEnv' instead" #-}
121−type SpiderEnv = SpiderTimeline
src/Reflex/Spider/SpiderTimelineHost.include.hsdeleted−57
@@ -1,57 +0,0 @@
1−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHostFrame x) where
2− {-# INLINABLE subscribeEvent #-}
3− subscribeEvent e = SpiderHostFrame $ do
4− --TODO: Unsubscribe eventually (manually and/or with weak ref)
5− val <- liftIO $ newIORef Nothing
6− subscription <- subscribe (unSpiderEvent e) $ Subscriber
7− { subscriberPropagate = \a -> do
8− liftIO $ writeIORef val $ Just a
9− scheduleClear val
10− , subscriberInvalidateHeight = \_ -> return ()
11− , subscriberRecalculateHeight = \_ -> return ()
12− }
13− return $ SpiderEventHandle
14− { spiderEventHandleSubscription = subscription
15− , spiderEventHandleValue = val
16− }
17−
18−instance HasSpiderTimeline x => Reflex.Host.Class.ReflexHost (SpiderTimeline x) where
19− type EventTrigger (SpiderTimeline x) = RootTrigger x
20− type EventHandle (SpiderTimeline x) = SpiderEventHandle x
21− type HostFrame (SpiderTimeline x) = SpiderHostFrame x
22−
23−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReadEvent (SpiderTimeline x) (Reflex.Spider.Internal.ReadPhase x) where
24− {-# NOINLINE readEvent #-}
25− readEvent h = Reflex.Spider.Internal.ReadPhase $ fmap (fmap return) $ liftIO $ do
26− result <- readIORef $ spiderEventHandleValue h
27− touch h
28− return result
29−
30−instance (
31−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
32− x ~ Global
33−#endif
34− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHost x) where
35− newEventWithTrigger = SpiderHost . lift . fmap SpiderEvent . newEventWithTriggerIO
36− newFanEventWithTrigger f = SpiderHost $ lift $ do
37− es <- newFanEventWithTriggerIO f
38− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
39−
40−instance (
41−#ifdef SPECIALIZE_TO_SPIDERTIMELINE_GLOBAL
42− x ~ Global
43−#endif
44− ) => Reflex.Host.Class.MonadReflexCreateTrigger (SpiderTimeline x) (SpiderHostFrame x) where
45− newEventWithTrigger = SpiderHostFrame . EventM . liftIO . fmap SpiderEvent . newEventWithTriggerIO
46− newFanEventWithTrigger f = SpiderHostFrame $ EventM $ liftIO $ do
47− es <- newFanEventWithTriggerIO f
48− return $ Reflex.Class.EventSelector $ SpiderEvent . Reflex.Spider.Internal.select es
49−
50−instance HasSpiderTimeline x => Reflex.Host.Class.MonadSubscribeEvent (SpiderTimeline x) (SpiderHost x) where
51− {-# INLINABLE subscribeEvent #-}
52− subscribeEvent = runFrame . runSpiderHostFrame . Reflex.Host.Class.subscribeEvent
53−
54−instance HasSpiderTimeline x => Reflex.Host.Class.MonadReflexHost (SpiderTimeline x) (SpiderHost x) where
55− type ReadPhase (SpiderHost x) = Reflex.Spider.Internal.ReadPhase x
56− fireEventsAndRead es (Reflex.Spider.Internal.ReadPhase a) = run es a
57− runHostFrame = runFrame . runSpiderHostFrame
stylizenew file+4
@@ -0,0 +1,4 @@
1+#!/usr/bin/env bash
2+set -euo pipefail
3+
4+for x in **/*.hs ; do stylish-haskell -i "$x" ; done
test/Reflex/Bench/Focused.hsdeleted−369
@@ -1,369 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE RecursiveDo #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE TupleSections #-}
10−{-# LANGUAGE TypeSynonymInstances #-}
11−
12−module Reflex.Bench.Focused where
13−
14−import Reflex
15−import Reflex.TestPlan
16−
17−import Control.Applicative
18−import Control.Monad
19−import Control.Monad.Fix
20−import Control.Monad.Identity
21−import Data.Traversable (for, traverse)
22−
23−import qualified Data.Dependent.Map as DMap
24−import Data.Map (Map)
25−import qualified Data.Map as Map
26−import Data.Maybe
27−
28−import Data.Functor.Misc
29−
30−import Data.List
31−import Data.List.Split
32−import Data.Tuple
33−
34−import Data.Monoid
35−import Data.Word
36−
37−import Control.DeepSeq
38−
39−import Prelude hiding (concat, sum)
40−
41−
42−mergeTree :: Num a => (Monoid (f [a]), Functor f) => Int -> [f a] -> f a
43−mergeTree n es | length es <= n = sum' es
44− | otherwise = mergeTree n subTrees
45− where
46− merges = chunksOf n es
47− subTrees = map sum' merges
48− sum' = fmap sum . mconcat . fmap (fmap (:[]) )
49−
50−mergeListDyn :: Reflex t => [Dynamic t a] -> Dynamic t [a]
51−mergeListDyn = mconcat . fmap (fmap pure)
52−
53−mergeTreeDyn :: (Num a, Reflex t) => Int -> [Dynamic t a] -> Dynamic t a
54−mergeTreeDyn n dyns | length dyns <= n = sumDyns dyns
55− | otherwise = mergeTreeDyn n $ fmap sumDyns merges
56− where
57− merges = chunksOf n dyns
58− sumDyns = fmap sum . mergeListDyn
59−
60−
61−
62−-- N events all firing for N frames
63−continuous :: TestPlan t m => Word -> Word -> m [Event t Word]
64−continuous n frames = for [1..n] $ \i -> plan $ (, i) <$> [1..frames]
65−
66−-- Fire N events once every P frames for total N frames (interspersed evenly)
67−occasional :: TestPlan t m => Word -> Word -> Word -> m [Event t Word]
68−occasional n period frames = traverse plan $ zipWith occs [1..] phases
69− where
70− phases = take (fromIntegral n) $ concat (repeat [1..period])
71− occs i phase = (, i) <$> [1, phase+1..frames]
72−
73−
74−
75−-- Event which never fires (but isn't 'never')
76−never' :: TestPlan t m => m (Event t Word)
77−never' = plan []
78−
79−
80−-- Event which fires once on first frame
81−event :: TestPlan t m => m (Event t Word)
82−event = plan [(1, 0)]
83−
84−-- Event which fires constantly over N frames
85−events :: TestPlan t m => Word -> m (Event t Word)
86−events n = plan $ (\i -> (i, i)) <$> [1..n]
87−
88−
89−-- N events all originating from one event
90−fmapFan :: Reflex t => Word -> Event t Word -> [Event t Word]
91−fmapFan n e = (\i -> (+i) <$> e) <$> [1..n]
92−
93−
94−-- Make a fan using the value in the event converting it to a singleton Map
95−-- use a fan of size n (key is the value mod n)
96−fanMerge :: Reflex t => Word -> Event t Word -> Event t Word
97−fanMerge n e = leftmost $ s <$> [0..n - 1] where
98− s k = select f (Const2 k)
99− f = fan (toMap <$> e)
100− toMap k = DMap.singleton (Const2 $ k `mod` n) (Identity k)
101−
102−-- Dumb version of the above using simple fmapMaybe to filter
103−fmapFanMerge :: Reflex t => Word -> Event t Word -> Event t Word
104−fmapFanMerge n e = leftmost $ s <$> [0..n - 1] where
105− s k = flip fmapMaybe e $ \j -> if k == j `mod` n
106− then Just k
107− else Nothing
108−
109−
110−
111−-- Create an Event with a Map which is dense to size N
112−denseMap :: TestPlan t m => Word -> m (Event t (Map Word Word))
113−denseMap n = plan [(1, m)] where
114− m = Map.fromList $ zip [1..n] [1..n]
115−
116−iter :: (a -> a) -> Word -> a -> a
117−iter _ 0 a = a
118−iter f n a = iter f (n - 1) (f a)
119−
120−iterM :: Monad m => (a -> m a) -> Word -> a -> m a
121−iterM _ 0 a = return a
122−iterM f n a = iterM f (n - 1) =<< f a
123−
124−fmapChain :: (Functor f) => Word -> f Word -> f Word
125−fmapChain = iter (fmap (+1))
126−
127−mapDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
128−mapDynChain = iterM (return . fmap (+1))
129−
130−joinDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
131−joinDynChain = iterM (\d -> return $ join $ fmap (const d) d)
132−
133−combineDynChain :: (Reflex t, MonadHold t m) => Word -> Dynamic t Word -> m (Dynamic t Word)
134−combineDynChain = iterM (\d -> return $ zipDynWith (+) d d)
135−
136−pingPong :: (Reflex t, MonadHold t m, MonadFix m) => Event t a -> Event t a -> m (Event t a)
137−pingPong e1 e2 = do
138− rec
139− d <- foldDyn (const swap) (e1, e2) e
140− let e = switch (fst <$> current d)
141− return e
142−
143−switchFactors :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
144−switchFactors 0 e = return e
145−switchFactors i e = do
146− b <- hold ((+1) <$> e) (e <$ ffilter ((== 0) . (i `mod`)) e)
147− switchFactors (i - 1) (switch b)
148−
149−
150−switchChain :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
151−switchChain = iterM (\e -> pingPong e ((+1) <$> e))
152−
153−
154−switchChain2 :: (Reflex t, MonadHold t m, MonadFix m) => Word -> Event t Word -> m (Event t Word)
155−switchChain2 = iterM (\e -> pingPong e ((+1) <$> leftmost [e, e]))
156−
157−switchPromptlyChain :: (Reflex t, MonadHold t m) => Word -> Event t Word -> m (Event t Word)
158−switchPromptlyChain = iterM $ \e ->
159− switchPromptlyDyn <$> holdDyn e (e <$ e)
160−
161−
162−coinChain :: Reflex t => Word -> Event t Word -> Event t Word
163−coinChain = iter $ \e -> coincidence (e <$ e)
164−
165−fanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
166−fanMergeChain width = iter $ fanMerge width
167−
168−fmapFanMergeChain :: Reflex t => Word -> Word -> Event t Word -> Event t Word
169−fmapFanMergeChain width = iter $ fmapFanMerge width
170−
171−-- | Data type for a collection (a map) which is updated by providing differences
172−data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a)))
173−
174−-- This will hopefully become a primitive (faster!)
175−switchMergeEvents :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
176−switchMergeEvents mapChanges = switch . fmap mergeMap <$> holdMap mapChanges
177−
178−-- switchMergeMaybe :: (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Event t a) -> m (Event t (Map k a))
179−-- switchMergeMaybe (UpdatedMap initial changes) = switchMergeMap initial (fmap (fromMaybe never) <$> changes)
180−
181−switchMergeBehaviors :: forall a t m k. (MonadFix m, MonadHold t m, Reflex t, Ord k) => UpdatedMap t k (Behavior t a) -> m (Behavior t (Map k a))
182−switchMergeBehaviors mapChanges = pull . joinMap <$> holdMap mapChanges
183− where joinMap m = traverse sample =<< sample m
184−
185−-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value
186−holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a))
187−holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (Map.foldWithKey modify)) initial changes
188−
189− where
190− modify k Nothing items = Map.delete k items
191− modify k (Just item) items = Map.insert k item items
192−
193−
194−-- | Hold an UpdatedMap as a behavior by applying differences to the initial value
195−holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a))
196−holdMap = (current <$>) . holdMapDyn
197−
198−
199−increasingMerge :: TestPlan t m => [a] -> m (UpdatedMap t Int a)
200−increasingMerge xs = do
201− e <- planList (zipWith Map.singleton [1..] (Just <$> xs) ++ [reset])
202− return (UpdatedMap Map.empty e)
203− where
204− reset = Map.fromList $ zip [1..] (const Nothing <$> xs)
205−
206−decreasingMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
207−decreasingMerge xs = do
208− e <- planList (zipWith Map.singleton [1..] (const Nothing <$> xs) ++ [reset])
209− return (UpdatedMap initial e)
210− where
211− initial = Map.fromList $ zip [1..] xs
212− reset = Just <$> initial
213−
214−modifyMerge :: (TestPlan t m) => [a] -> m (UpdatedMap t Int a)
215−modifyMerge xs = do
216− e <- planList $ zipWith Map.singleton [1..] (Just <$> xs)
217− return (UpdatedMap initial e)
218− where
219− initial = Map.fromList $ zip [1..] xs
220−
221−
222−countMany :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Behavior t Int]
223−countMany = traverse (fmap current . count)
224−
225−countManyDyn :: (Reflex t, MonadHold t m, MonadFix m) => [Event t a] -> m [Dynamic t Int]
226−countManyDyn = traverse count
227−
228−
229−-- Given a function which creates a sub-network (from a single event)
230−-- Create a plan which uses a switch to substitute it
231−switches :: TestPlan t m => Word -> (forall n. (MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> m (Event t a)
232−switches numFrames f = do
233− es <- events numFrames
234− switch <$> hold never (makeEvents es)
235−
236− where
237− makeEvents es = pushAlways (\n -> f (fmap (+n) es)) es
238−
239−
240−
241−-- Two sets of benchmarks, one which we're interested in testing subscribe time (as well as time to fire frames)
242−-- the other, which we're only interested in time for running frames
243−subscribing :: Word -> Word -> [(String, TestCase)]
244−subscribing n frames =
245− [ testSub "fmapFanMerge" $ return . mergeList . fmapFan n
246− , testSub "fanMerge" $ return . fanMerge n
247− , testSub "fmapFan/mergeTree" $ return . mergeTree 8 . fmapFan n
248− , testSub "fmapChain" $ return . fmapChain n
249− , testSub "switchChain" $ switchChain n
250− , testSub "switchPromptlyChain" $ switchPromptlyChain n
251− , testSub "switchFactors" $ switchFactors n
252− , testSub "coincidenceChain" $ return . coinChain n
253− ]
254−
255− where
256− testSub :: (Eq a, Show a, NFData a) => String -> (forall t n. (Reflex t, MonadHold t n, MonadFix n) => Event t Word -> n (Event t a)) -> (String, TestCase)
257− testSub name test = testE name (switches frames test)
258−
259−-- Set of benchmark to test specifically merging dynamic collections
260−-- a pattern which occurs frequently in reflex-dom collections
261−merging :: Word -> [(String, TestCase)]
262−merging n =
263− [ testE "static events" $ mergeList <$> sparse
264− , testE "increasing events" $ switchMergeEvents =<< increasingMerge =<< sparse
265− , testE "decreasing events" $ switchMergeEvents =<< decreasingMerge =<< sparse
266− , testE "modify events" $ switchMergeEvents =<< modifyMerge =<< sparse
267−
268− , testB "static behaviors" $ pull . traverse sample <$> counters
269− , testB "increasing behaviors" $ switchMergeBehaviors =<< increasingMerge =<< counters
270− , testB "decreasing behaviors" $ switchMergeBehaviors =<< decreasingMerge =<< counters
271− , testB "modify behaviors" $ switchMergeBehaviors =<< modifyMerge =<< counters
272−
273−
274− -- , testE "increasing events/switchMerge" $ switchMergeMaybe =<< increasingMerge =<< sparse
275− -- , testE "decreasing events/switchMerge" $ switchMergeMaybe =<< decreasingMerge =<< sparse
276− -- , testE "modify events/switchMerge" $ switchMergeMaybe =<< modifyMerge =<< sparse
277− ]
278−
279− where
280− sparse :: TestPlan t m => m [Event t Word]
281− sparse = fmap (fmap (+1)) <$> occasional n 8 n
282−
283− counters :: TestPlan t m => m [Behavior t Int]
284− counters = countMany =<< sparse
285−
286−
287−
288−fans :: Word -> [(String, TestCase)]
289−fans n =
290− [ testE "fanMapChain" $ fanMergeChain n 10 <$> e
291− , testE "fmapFanMapChain" $ fmapFanMergeChain n 10 <$> e
292−
293− , testE "fanMerge " $ fanMerge n <$> events n
294−
295− , testE "fmapFanMerge " $ fmapFanMerge n <$> events n
296− , testE "triggerMerge" $ leftmost <$> for [1..n] (\i -> plan [(i, i)])
297−
298− ]
299− where
300−
301− e :: TestPlan t m => m (Event t Word)
302− e = events 100
303−
304−
305−
306−
307−dynamics :: Word -> [(String, TestCase)]
308−dynamics n =
309− [ testE "mapDynChain" $ fmap updated $ mapDynChain n =<< d
310− , testE "joinDynChain" $ fmap updated $ joinDynChain n =<< d
311− , testE "combineDynChain" $ fmap updated $ combineDynChain n =<< d
312− , testE "dense mergeTree" $ fmap (updated . mergeTreeDyn 8) dense
313− , testE "sparse mergeTree" $ fmap (updated . mergeTreeDyn 8) sparse
314− , testE "mergeDyn" $ fmap (updated . fmap sum . mergeListDyn) sparse
315− ] where
316−
317− d :: TestPlan t m => m (Dynamic t Word)
318− d = count =<< events 10
319−
320− sparse, dense :: TestPlan t m => m [Dynamic t Int]
321− sparse = countManyDyn =<< occasional n 8 16
322− dense = countManyDyn =<< continuous n 2
323−
324−
325−firing :: Word -> [(String, TestCase)]
326−firing n =
327− [ testE "fmapChain" $ fmapChain n <$> e
328− , testE "switchChain2" $ switchChain2 n =<< e
329− , testE "switchPromptlyChain" $ switchPromptlyChain n =<< e
330− , testE "switchFactors" $ switchFactors n =<< e
331− , testE "coincidenceChain" $ coinChain n <$> e
332−
333− , testE "dense mergeTree" $ mergeTree 8 <$> dense
334− , testE "sparse mergeTree" $ mergeTree 8 <$> sparse
335−
336− -- Triggers test failure in Spider
337− --, testE "runFrame" $ events n
338−
339− , testB "sum counters" $ do
340− counts <- counters
341− return $ pull $ sum <$> traverse sample counts
342−
343− , testB "pullChain" $ fmapChain n . current <$> (count =<< events 4)
344− , testB "pullChain2" $ do
345− es <- events 4
346− e' <- plan [(0, ())]
347−
348− b <- hold (constant 0) $
349− pushAlways (const $ fmapChain n <$> hold 0 es) e'
350−
351− return (join b)
352− , testB "counters/pullTree" $ mergeTree 8 <$> counters
353−
354− ]
355− where
356− e :: TestPlan t m => m (Event t Word)
357− e = events 10
358−
359− dense, sparse :: TestPlan t m => m [Event t Word]
360− dense = continuous n 2
361− sparse = occasional n 8 16
362−
363− counters :: TestPlan t m => m [Behavior t Int]
364− counters = countMany =<< sparse
365−
366−
367−
368−
369−
test/Reflex/Plan/Pure.hsdeleted−70
@@ -1,70 +0,0 @@
1−{-# LANGUAGE FlexibleContexts #-}
2−{-# LANGUAGE FlexibleInstances #-}
3−{-# LANGUAGE GADTs #-}
4−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−
9−module Reflex.Plan.Pure where
10−
11−import Reflex
12−import Reflex.Pure
13−import Reflex.TestPlan
14−
15−import Control.Applicative
16−import Control.Monad.Fix
17−import Control.Monad.State
18−import Data.IntMap (IntMap)
19−import qualified Data.IntMap as IntMap
20−import Data.IntSet (IntSet)
21−import qualified Data.IntSet as IntSet
22−
23−import Data.Bifunctor
24−import Data.Maybe
25−import Data.Monoid
26−import Prelude
27−
28−
29−mapToPureEvent :: IntMap a -> Event (Pure Int) a
30−mapToPureEvent m = Event $ flip IntMap.lookup m
31−
32−type TimeM = (->) Int
33−newtype PurePlan a = PurePlan { unPlan :: StateT IntSet TimeM a } deriving (Functor, Applicative, Monad, MonadFix)
34−
35−liftPlan :: TimeM a -> PurePlan a
36−liftPlan = PurePlan . lift
37−
38−instance MonadHold (Pure Int) PurePlan where
39− hold initial = liftPlan . hold initial
40− holdDyn initial = liftPlan . holdDyn initial
41− holdIncremental initial = liftPlan . holdIncremental initial
42−
43−instance MonadSample (Pure Int) PurePlan where
44− sample = liftPlan . sample
45−
46−
47−instance TestPlan (Pure Int) PurePlan where
48− plan occs = do
49− PurePlan . modify $ IntSet.union (IntMap.keysSet m)
50− return $ mapToPureEvent m
51− where m = IntMap.fromList (first fromIntegral <$> occs)
52−
53−runPure :: PurePlan a -> (a, IntSet)
54−runPure (PurePlan p) = runStateT p mempty $ 0
55−
56−relavantTimes :: IntSet -> IntSet
57−relavantTimes occs = IntSet.fromList [0..l + 1]
58− where l = fromMaybe 0 (fst <$> IntSet.maxView occs)
59−
60−testBehavior :: (Behavior (Pure Int) a, IntSet) -> IntMap a
61−testBehavior (b, occs) = IntMap.fromSet (sample b) (relavantTimes occs)
62−
63−testEvent :: (Event (Pure Int) a, IntSet) -> IntMap (Maybe a)
64−testEvent (Event readEvent, occs) = IntMap.fromSet readEvent (relavantTimes occs)
65−
66−
67−
68−
69−
70−
test/Reflex/Plan/Reflex.hsdeleted−135
@@ -1,135 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE ScopedTypeVariables #-}
9−{-# LANGUAGE StandaloneDeriving #-}
10−{-# LANGUAGE UndecidableInstances #-}
11−module Reflex.Plan.Reflex
12− ( TestPlan(..)
13− , runPlan
14− , Plan (..)
15− , Schedule
16− , Firing (..)
17− , MonadIORef
18−
19−
20− , readSchedule
21− , readSchedule_
22− , testSchedule
23− , readEvent'
24− , makeDense
25−
26− , runTestE
27− , runTestB
28−
29− ) where
30−
31−import Reflex.Class
32−import Reflex.Host.Class
33−import Reflex.TestPlan
34−
35−import Control.Applicative
36−import Control.Monad
37−import Control.Monad.Identity
38−import Control.Monad.State.Strict
39−
40−import Control.Monad.Ref
41−import Data.Dependent.Sum (DSum (..))
42−import qualified Data.IntMap.Strict as IntMap
43−import Data.Maybe
44−import Data.Monoid
45−import Data.Traversable (sequenceA, traverse)
46−
47−import Control.DeepSeq
48−import Control.Exception
49−import Data.IntMap.Strict (IntMap)
50−import Data.IORef
51−import System.Mem
52−
53−-- Note: this import must come last to silence warnings from AMP
54−import Prelude
55−
56−type MonadIORef m = (MonadIO m, MonadRef m, Ref m ~ Ref IO)
57−
58−data Firing t where
59− Firing :: IORef (Maybe (EventTrigger t a)) -> a -> Firing t
60−
61−
62−type Schedule t = IntMap [Firing t]
63−
64−-- Implementation of a TestPlan in terms of ReflexHost
65−newtype Plan t a = Plan (StateT (Schedule t) (HostFrame t) a)
66−
67−deriving instance ReflexHost t => Functor (Plan t)
68−deriving instance ReflexHost t => Applicative (Plan t)
69−deriving instance ReflexHost t => Monad (Plan t)
70−
71−deriving instance ReflexHost t => MonadSample t (Plan t)
72−deriving instance ReflexHost t => MonadHold t (Plan t)
73−deriving instance ReflexHost t => MonadFix (Plan t)
74−
75−
76−instance (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => TestPlan t (Plan t) where
77− plan occurances = Plan $ do
78− (e, ref) <- newEventWithTriggerRef
79− modify (IntMap.unionWith mappend (firings ref))
80− return e
81−
82− where
83− firings ref = IntMap.fromList (makeFiring ref <$> occurances)
84− makeFiring ref (t, a) = (fromIntegral t, [Firing ref a])
85−
86−
87−firingTrigger :: (MonadReflexHost t m, MonadIORef m) => Firing t -> m (Maybe (DSum (EventTrigger t) Identity))
88−firingTrigger (Firing ref a) = fmap (:=> Identity a) <$> readRef ref
89−
90−runPlan :: (MonadReflexHost t m) => Plan t a -> m (a, Schedule t)
91−runPlan (Plan p) = runHostFrame $ runStateT p mempty
92−
93−
94−makeDense :: Schedule t -> Schedule t
95−makeDense s = fromMaybe (emptyRange 0) $ do
96− (end, _) <- fst <$> IntMap.maxViewWithKey s
97− return $ IntMap.union s (emptyRange end)
98− where
99− emptyRange end = IntMap.fromList (zip [0..end + 1] (repeat []))
100−
101−
102−-- For the purposes of testing, we add in a zero frame and extend one frame (to observe changes to behaviors
103−-- after the last event)
104−-- performGC is called at each frame to test for GC issues
105−testSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
106−testSchedule schedule readResult = IntMap.traverseWithKey (\t occs -> liftIO performGC *> triggerFrame readResult t occs) (makeDense schedule)
107−
108−readSchedule :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m (IntMap a)
109−readSchedule schedule readResult = IntMap.traverseWithKey (triggerFrame readResult) schedule
110−
111−readSchedule_ :: (MonadReflexHost t m, MonadIORef m, NFData a) => Schedule t -> ReadPhase m a -> m ()
112−readSchedule_ schedule readResult = mapM_ (uncurry $ triggerFrame readResult) $ IntMap.toList schedule
113−
114−triggerFrame :: (MonadReflexHost t m, MonadIORef m, NFData a) => ReadPhase m a -> Int -> [Firing t] -> m a
115−triggerFrame readResult _ occs = do
116− triggers <- catMaybes <$> traverse firingTrigger occs
117− liftIO . evaluate . force =<< fireEventsAndRead triggers readResult
118−
119−readEvent' :: MonadReadEvent t m => EventHandle t a -> m (Maybe a)
120−readEvent' = readEvent >=> sequenceA
121−
122−
123−-- Convenience functions for running tests producing Events/Behaviors
124−runTestB :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Behavior t a) -> m (IntMap a)
125−runTestB p = do
126− (b, s) <- runPlan p
127− testSchedule s $ sample b
128−
129−runTestE :: (MonadReflexHost t m, MonadIORef m, NFData a) => Plan t (Event t a) -> m (IntMap (Maybe a))
130−runTestE p = do
131− (e, s) <- runPlan p
132− h <- subscribeEvent e
133− testSchedule s (readEvent' h)
134−
135−
test/Reflex/Test.hsdeleted−66
@@ -1,66 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Reflex.Test
9− ( testAgreement
10− , compareResult
11− , runTests
12−
13− , module Reflex.TestPlan
14−
15− ) where
16−
17−import Reflex.Spider
18−
19−import Reflex.TestPlan
20−
21−import Reflex.Plan.Pure
22−import Reflex.Plan.Reflex
23−
24−import Control.Monad
25−import Data.Monoid
26−
27−import Data.IntMap (IntMap)
28−
29−--import Data.Foldable
30−import System.Exit
31−
32−import Prelude
33−
34−
35−testAgreement :: TestCase -> IO Bool
36−testAgreement (TestE p) = do
37− spider <- runSpiderHost $ runTestE p
38− let results = [("spider", spider)]
39−
40− compareResult results (testEvent $ runPure p)
41−
42−testAgreement (TestB p) = do
43− spider <- runSpiderHost $ runTestB p
44− let results = [("spider", spider)]
45−
46− compareResult results (testBehavior $ runPure p)
47−
48−
49−compareResult :: (Show a, Eq a) => [(String, IntMap a)] -> IntMap a -> IO Bool
50−compareResult results expected = fmap and $ forM results $ \(name, r) -> do
51−
52− when (r /= expected) $ do
53− putStrLn ("Got: " ++ show (name, r))
54− putStrLn ("Expected: " ++ show expected)
55− return (r == expected)
56−
57−
58−runTests :: [(String, TestCase)] -> IO ()
59−runTests testCases = do
60− results <- forM testCases $ \(name, test) -> do
61− putStrLn $ "Test: " <> name
62− testAgreement test
63− exitWith $ if and results
64− then ExitSuccess
65− else ExitFailure 1
66−
test/Reflex/Test/CrossImpl.hsdeleted−244
@@ -1,244 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE ExistentialQuantification #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE FunctionalDependencies #-}
6−{-# LANGUAGE GADTs #-}
7−{-# LANGUAGE LambdaCase #-}
8−{-# LANGUAGE MultiParamTypeClasses #-}
9−{-# LANGUAGE NoMonomorphismRestriction #-}
10−{-# LANGUAGE RankNTypes #-}
11−{-# LANGUAGE ScopedTypeVariables #-}
12−{-# LANGUAGE TypeFamilies #-}
13−{-# LANGUAGE TypeOperators #-}
14−{-# LANGUAGE UndecidableInstances #-}
15−module Reflex.Test.CrossImpl (test) where
16−
17−import Prelude hiding (and, foldl, mapM, mapM_, sequence, sequence_)
18−
19−import Control.Monad.Ref
20−import Reflex.Class
21−import Reflex.Dynamic
22−import Reflex.Host.Class
23−import qualified Reflex.Pure as P
24−import qualified Reflex.Spider.Internal as S
25−
26−import Control.Arrow (second, (&&&))
27−import Control.Monad.Identity hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
28−import Control.Monad.State.Strict hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
29−import Control.Monad.Writer hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
30−import Data.Dependent.Map (DSum (..))
31−import Data.Foldable
32−import Data.Map.Strict (Map)
33−import qualified Data.Map.Strict as Map
34−import qualified Data.Set as Set
35−import Data.Traversable
36−import System.Exit
37−import System.Mem
38−
39−import System.IO.Unsafe
40−
41−mapToPureBehavior :: Map Int a -> Behavior PureReflexDomain a
42−mapToPureBehavior m = P.Behavior $ \t -> case Map.lookupLE t m of
43− Nothing -> error $ "mapToPureBehavior: no value for time " <> show t
44− Just (_, v) -> v
45−
46−mapToPureEvent :: Map Int a -> Event PureReflexDomain a
47−mapToPureEvent m = P.Event $ flip Map.lookup m
48−
49−relevantTestingTimes :: (Map Int a, Map Int b) -> [Int]
50−relevantTestingTimes (b, e) = case Set.minView &&& Set.maxView $ Map.keysSet b `Set.union` Map.keysSet e of
51− (Just (t0, _), Just (t1, _)) -> [t0..t1+1] -- We need to go to b+1 to see the result of the final event
52− _ -> [] -- Doesn't actually make much sense
53−
54−type PureReflexDomain = P.Pure Int
55−type TimeM = (->) Int
56−
57−testPure :: (t ~ PureReflexDomain, m ~ TimeM) => ((Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> (Map Int c, Map Int d)
58−testPure builder (b, e) =
59− let (P.Behavior b', P.Event e') = ($ 0) $ builder (mapToPureBehavior b, mapToPureEvent e)
60− relevantTimes = relevantTestingTimes (b, e)
61− e'' = Map.mapMaybe id $ Map.fromList $ map (id &&& e') relevantTimes
62− b'' = Map.fromList $ map (id &&& b') relevantTimes
63− in (b'', e'')
64−
65−class MapMSignals a a' t t' | a -> t, a' -> t', a t' -> a', a' t -> a where
66− mapMSignals :: Monad m => (forall b. Behavior t b -> m (Behavior t' b)) -> (forall b. Event t b -> m (Event t' b)) -> a -> m a'
67−
68−instance MapMSignals (Behavior t a) (Behavior t' a) t t' where
69− mapMSignals fb _ = fb
70−
71−instance MapMSignals (Event t a) (Event t' a) t t' where
72− mapMSignals _ fe = fe
73−
74−instance (MapMSignals a a' t t', MapMSignals b b' t t') => MapMSignals (a, b) (a', b') t t' where
75− mapMSignals fb fe (a, b) = liftM2 (,) (mapMSignals fb fe a) (mapMSignals fb fe b)
76−
77−testSpider :: (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> (Map Int c, Map Int d)
78−testSpider builder (bMap, eMap) = unsafePerformIO $ S.runSpiderHost $ do
79− (re, reTrigger) <- newEventWithTriggerRef
80− (rb, rbTrigger) <- newEventWithTriggerRef
81− b <- runHostFrame $ hold (error "testSpider: No value for input behavior yet") rb
82− (b', e') <- runHostFrame $ builder (b, re)
83− e'Handle <- subscribeEvent e' --TODO: This should be unnecessary
84− let times = relevantTestingTimes (bMap, eMap)
85− liftIO performGC
86− outputs <- forM times $ \t -> do
87− forM_ (Map.lookup t bMap) $ \val -> mapM_ (\ rbt -> fireEvents [rbt :=> Identity val]) =<< readRef rbTrigger
88− bOutput <- sample b'
89− eOutput <- fmap join $ forM (Map.lookup t eMap) $ \val -> do
90− mret <- readRef reTrigger
91− let firing = case mret of
92− Just ret -> [ret :=> Identity val]
93− Nothing -> []
94− fireEventsAndRead firing $ sequence =<< readEvent e'Handle
95− liftIO performGC
96− return (t, (bOutput, eOutput))
97− return (Map.fromList $ map (second fst) outputs, Map.mapMaybe id $ Map.fromList $ map (second snd) outputs)
98−
99−tracePerf :: Show a => a -> b -> b
100−tracePerf = flip const
101−
102−testAgreement :: (Eq c, Eq d, Show c, Show d) => (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d)) -> (Map Int a, Map Int b) -> IO Bool
103−testAgreement builder inputs = do
104− let identityResult = testPure builder inputs
105− tracePerf "---------" $ return ()
106− let spiderResult = testSpider builder inputs
107− tracePerf "---------" $ return ()
108− let resultsAgree = identityResult == spiderResult
109− if resultsAgree
110− then do putStrLn "Success:"
111− print identityResult
112− else do putStrLn "Failure:"
113− putStrLn $ "Pure result: " <> show identityResult
114− putStrLn $ "Spider result: " <> show spiderResult
115− return resultsAgree
116−
117−type TestCaseConstraint t m = (Reflex t, MonadSample t m, MonadHold t m, MonadFix m, MonadFix (PushM t))
118−
119−data TestCase = forall a b c d. (Eq c, Eq d, Show c, Show d) => TestCase (Map Int a, Map Int b) (forall m t. TestCaseConstraint t m => (Behavior t a, Event t b) -> m (Behavior t c, Event t d))
120−
121−testCases :: [(String, TestCase)]
122−testCases =
123− [ (,) "hold" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(_, e) -> do
124− b' <- hold "123" e
125− return (b', e)
126− , (,) "count" $ TestCase (Map.singleton 0 (), Map.fromList [(1, ()), (2, ()), (3, ())]) $ \(_, e) -> do
127− e' <- updated <$> count e
128− b' <- hold (0 :: Int) e'
129− return (b', e')
130− , (,) "onceE-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
131− e' <- onceE $ leftmost [e, e]
132− return (b, e')
133− , (,) "switch-1" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
134− let e' = fmap (const e) e
135− b' <- hold never e'
136− let e'' = switch b'
137− return (b, e'')
138− , (,) "switch-2" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
139− let e' = flip pushAlways e $ const $ do
140− let eab = splitRecombineEvent e
141− switch <$> hold eab never
142− e'' = coincidence e'
143− return (b, e'')
144− , (,) "switch-3" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
145− let e' = flip pushAlways e $ const $ do
146− let eab = splitRecombineEvent e
147− switch <$> hold eab (fmap (const e) e)
148− e'' = coincidence e'
149− return (b, e'')
150− , (,) "switch-4" $ TestCase (Map.singleton 0 "asdf", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
151− let e' = leftmost [e, e]
152− e'' <- switch <$> hold e' (fmap (const e) e)
153− return (b, e'')
154− , (,) "switchPromptly-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
155− let e' = fmap (const e) e
156− e'' <- switchPromptly never e'
157− return (b, e'')
158− , (,) "switchPromptly-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
159− let e' = fmap (const e) e
160− e'' <- switchPromptly never $ leftmost [e', e']
161− return (b, e'')
162− , (,) "switchPromptly-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
163− let e' = leftmost [e, e]
164− e'' <- switchPromptly never (fmap (const e) e')
165− return (b, e'')
166− , (,) "switchPromptly-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
167− let e' = leftmost [e, e]
168− e'' <- switchPromptly never (fmap (const e') e)
169− return (b, e'')
170− , (,) "switch-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
171− let e' = leftmost [e, e]
172− e'' <- switch <$> hold never (fmap (const e') e)
173− return (b, e'')
174− , (,) "switchPromptly-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
175− let e' = flip push e $ \_ -> do
176− Just <$> onceE e
177− e'' <- switchPromptly never e'
178− return (b, e'')
179− , (,) "switchPromptly-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
180− let e' = flip pushAlways e $ \_ -> do
181− switchPromptly e never
182− e'' <- switchPromptly never e'
183− return (b, e'')
184− , (,) "coincidence-1" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
185− let e' = flip pushAlways e $ \_ -> return e
186− e'' = coincidence e'
187− return (b, e'')
188− , (,) "coincidence-2" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
189− let e' = flip pushAlways e $ \_ -> return $ leftmost [e, e]
190− e'' = coincidence e'
191− return (b, e'')
192− , (,) "coincidence-3" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
193− let e' = flip pushAlways e $ \_ -> return $ coincidence $ fmap (const e) e
194− e'' = coincidence e'
195− return (b, e'')
196− , (,) "coincidence-4" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
197− let e' = flip pushAlways e $ \_ -> onceE e
198− e'' = coincidence e'
199− return (b, e'')
200− , (,) "coincidence-5" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
201− let eChild = flip pushAlways e $ const $ do
202− let eNewValues = leftmost [e, e]
203− return $ coincidence $ fmap (const eNewValues) eNewValues
204− e' = coincidence eChild
205− return (b, e')
206− , (,) "coincidence-6" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer")]) $ \(b, e) -> do
207− let eChild = flip pushAlways e $ const $ do
208− let e' = coincidence $ fmap (const e) e
209− return $ leftmost [e', e']
210− e'' = coincidence eChild
211− return (b, e'')
212− , (,) "coincidence-7" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj"), (3, "asdf")]) $ \(b, e) -> do
213− let e' = leftmost [e, e]
214− eCoincidences = coincidence $ fmap (const e') e
215− return (b, eCoincidences)
216− , (,) "holdWhileFiring" $ TestCase (Map.singleton 0 "zxc", Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
217− eo <- onceE e
218− bb <- hold b $ pushAlways (const $ hold "asdf" eo) eo
219− let b' = pull $ sample =<< sample bb
220− return (b', e)
221− , (,) "joinDyn" $ TestCase (Map.singleton 0 (0 :: Int), Map.fromList [(1, "qwer"), (2, "lkj")]) $ \(b, e) -> do
222− bb <- hold "b" e
223− bd <- hold never . fmap (const e) =<< onceE e
224− eOuter <- pushAlways sample . fmap (const bb) <$> onceE e
225− let eInner = switch bd
226− e' = leftmost [eOuter, eInner]
227− return (b, e')
228− ]
229−
230−splitRecombineEvent :: Reflex t => Event t a -> Event t String
231−splitRecombineEvent e =
232− let ea = "a" <$ e
233− eb = "b" <$ e
234− in leftmost [ea, eb]
235−
236−
237−test :: IO ()
238−test = do
239− results <- forM testCases $ \(name, TestCase inputs builder) -> do
240− putStrLn $ "Test: " <> name
241− testAgreement builder inputs
242− exitWith $ if and results
243− then ExitSuccess
244− else ExitFailure 1
test/Reflex/Test/Micro.hsdeleted−285
@@ -1,285 +0,0 @@
1−{-# LANGUAGE BangPatterns #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE GADTs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE RecursiveDo #-}
9−
10−module Reflex.Test.Micro (testCases) where
11−
12−import Reflex
13−import Reflex.TestPlan
14−
15−import Control.Applicative
16−import Control.Monad
17−import Control.Monad.Fix
18−import Data.Char
19−import Data.Foldable
20−import Data.Functor.Misc
21−import qualified Data.Map as Map
22−import Data.Monoid
23−
24−import Prelude
25−
26−{-# ANN testCases "HLint: ignore Functor law" #-}
27−testCases :: [(String, TestCase)]
28−testCases =
29− [ testB "hold" $ hold "0" =<< events1
30−
31− , testB "count" $ do
32− b <- current <$> (count =<< events2)
33− return $ (+ (0::Int)) <$> b
34−
35− , testB "pull-1" $ do
36− b <- hold "0" =<< events1
37− return (pull $ sample $ pull $ sample b)
38−
39− , testB "pull-2" $ do
40− b1 <- behavior1
41− return (pull $ liftA2 (<>) (sample b1) (sample b1))
42−
43− , testB "pull-3" $ do
44− b1 <- behavior1
45− b2 <- behavior2
46− return (pull $ liftA2 (<>) (sample b1) (sample b2))
47−
48− , testB "pull-4" $ do
49− es <- planList ["a", "b", "c"]
50− e <- plan [(0, ())]
51− b <- hold (constant "") $
52− pushAlways (const $ hold "z" es) e
53− return (join b)
54−
55− , testE "id" $ do
56− events2
57−
58− , testE "fmap-id" $ do
59− e <- events2
60− return $ fmap id e
61−
62− , testE "tag-1" $ do
63− b1 <- behavior1
64− e <- events2
65− return (tag b1 e)
66−
67− , testE "tag-2" $ do
68− b1 <- behavior1
69− e <- events2
70− return (tag (map toUpper <$> b1) e)
71−
72− , testE "attach-1" $ do
73− b1 <- behavior1
74− e <- events2
75− return (attachWith (++) (map toUpper <$> b1) e)
76−
77− , testE "leftmost" $ liftA2 leftmost2 events1 events2
78−
79− , testE "appendEvents-1" $ liftA2 mappend events1 events2
80−
81− , testE "appendEvents-2" $ liftA2 mappend events3 events2
82−
83− , testE "merge-1" $ do
84− e <- events1
85− return $ leftmost ["x" <$ e, "y" <$ e]
86−
87− , testE "merge-2" $ do
88− e <- events1
89− let m = mergeMap $ Map.fromList [(1::Int, "y" <$ e), (2, "z" <$ e)]
90− let ee = flip pushAlways e $ const $ return m
91− return $ coincidence ee
92−
93− , testE "headE-1" $ do
94− e <- events1
95− headE $ leftmost [e, e]
96−
97− , testE "headE-2" $ do
98− e <- events1
99− b <- hold never (e <$ e)
100− headE $ switch b
101−
102− , testE "switch-1" $ do
103− e <- events1
104− b <- hold never (e <$ e)
105− return $ switch b
106−
107− , testE "switch-2" $ do
108− e <- events1
109− return $ coincidence $ flip pushAlways e $ const $ do
110− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) (e <$ e)
111−
112− , testE "switch-3" $ do
113− e <- events1
114− return $ coincidence $ flip pushAlways e $ const $ do
115− switch <$> hold (leftmost ["x" <$ e, "y" <$ e, "z" <$ e]) never
116−
117− , testE "switch-4" $ do
118− e <- events1
119− switch <$> hold (deep e) (e <$ e)
120−
121− , testE "switch-5" $ do
122− e <- events1
123− return $ coincidence $ flip pushAlways e $ const $
124− return $ leftmost ["x" <$ e, "y" <$ e, "z" <$ e]
125−
126− , testE "switch-6" $ do
127− e <- events1
128− return $ coincidence $ flip pushAlways e $ const $ do
129− switch <$> hold ("x" <$ e) (e <$ e)
130−
131− , testE "switchPromptly-1" $ do
132− e <- events1
133− let e' = e <$ e
134− switchPromptly never $ e <$ e'
135−
136− , testE "switchPromptly-2" $ do
137− e <- events1
138− switchPromptly never $ deep (e <$ e)
139−
140− , testE "switchPromptly-3" $ do
141− e <- events1
142− switchPromptly never $ (e <$ deep e)
143−
144− , testE "switchPromptly-4" $ do
145− e <- events1
146− switchPromptly never $ (deep e <$ e)
147−
148− , testE "switch-5" $ do
149− e <- events1
150− switch <$> hold never (deep e <$ e)
151−
152− , testE "switchPromptly-5" $ do
153− e <- events1
154− switchPromptly never $ flip push e $
155− const (Just <$> headE e)
156−
157− , testE "switchPromptly-6" $ do
158− e <- events1
159− switchPromptly never $ flip pushAlways e $
160− const (switchPromptly e never)
161−
162− , testE "coincidence-1" $ do
163− e <- events1
164− return $ coincidence $ flip pushAlways e $
165− const $ return e
166−
167− , testE "coincidence-2" $ do
168− e <- events1
169− return $ coincidence $ flip pushAlways e $
170− const $ return (deep e)
171−
172− , testE "coincidence-3" $ do
173− e <- events1
174− return $ coincidence $ flip pushAlways e $
175− const $ return (coincidence (e <$ e))
176−
177− , testE "coincidence-4" $ do
178− e <- events1
179− return $ coincidence $ flip pushAlways e $
180− const (headE e)
181−
182− , testE "coincidence-5" $ do
183− e <- events1
184− return $ coincidence $ flip pushAlways e $ const $ do
185− let e' = deep e
186− return (coincidence (e' <$ e'))
187−
188− , testE "coincidence-6" $ do
189− e <- events1
190− return $ coincidence $ flip pushAlways e $ const $ do
191− let e' = coincidence (e <$ e)
192− return $ deep e'
193−
194− , testE "coincidence-7" $ do
195− e <- events1
196− return $ coincidence (deep e <$ e)
197−
198− , testB "holdWhileFiring" $ do
199− e <- events1
200− eo <- headE e
201− bb <- hold (constant "x") $ pushAlways (const $ hold "a" eo) eo
202− return $ pull $ sample =<< sample bb
203−
204− , testE "joinDyn" $ do
205− e <- events1
206− bb <- hold "b" e
207− bd <- hold never . fmap (const e) =<< headE e
208−
209− eOuter <- pushAlways sample . fmap (const bb) <$> headE e
210− let eInner = switch bd
211− return $ leftmost [eOuter, eInner]
212−
213− , testB "foldDyn" $ do
214− d <- foldDyn (++) "0" =<< events1
215− return (current d)
216−
217− , testB "mapDyn" $ do
218− d <- foldDyn (++) "0" =<< events1
219− return $ current $ fmap (map toUpper) d
220−
221− , testB "combineDyn" $ do
222− d1 <- foldDyn (++) "0" =<< events1
223− d2 <- fmap (fmap (map toUpper)) $ foldDyn (++) "0" =<< events2
224−
225− return $ current $ zipDynWith (<>) d1 d2
226− , testE "fan-1" $ do
227− e <- fmap toMap <$> events1
228− let es = select (fanMap e) . Const2 <$> values
229−
230− return (mergeList es)
231−
232− , testE "fan-2" $ do
233− e <- fmap toMap <$> events3
234− let es = select (fanMap e) . Const2 <$> values
235−
236− return (mergeList es)
237−
238− , testE "fan-3" $ do
239− f <- fanMap . fmap toMap <$> events3
240− return $ select f (Const2 'c')
241−
242− , testE "fan-4" $ do
243− e <- fmap toMap <$> events1
244− return $ toUpper <$> select (fanMap e) (Const2 'a')
245−
246− , testE "fan-5" $ do
247− e <- fmap toMap <$> events2
248− return $ toUpper <$> select (fanMap e) (Const2 'c')
249−
250− , testE "fan-6" $ do
251− f <- fanMap . fmap toMap <$> events1
252− return $ toList <$> mergeList [ select f (Const2 'b'), select f (Const2 'b'), select f (Const2 'e'), select f (Const2 'e') ]
253−
254− , testE "difference" $ do
255− e1 <- events1
256− e2 <- events2
257− return $ e1 `difference ` e2
258−
259− , testE "lazy-hold" $ do
260− let lazyHold :: forall t m. (Reflex t, MonadHold t m, MonadFix m) => m (Event t ())
261− lazyHold = do
262− rec !b <- hold never e
263− let e = never <$ switch b
264− return $ void e
265− lazyHold
266−
267− ] where
268−
269− events1, events2, events3 :: TestPlan t m => m (Event t String)
270− events1 = plan [(1, "a"), (2, "b"), (5, "c"), (7, "d"), (8, "e")]
271− events2 = plan [(1, "e"), (3, "d"), (4, "c"), (6, "b"), (7, "a")]
272−
273− events3 = liftA2 mappend events1 events2
274−
275− values = "abcde"
276− toMap str = Map.fromList $ map (\c -> (c, c)) str
277−
278− behavior1, behavior2 :: forall t m. TestPlan t m => m (Behavior t String)
279− behavior1 = hold "1" =<< events1
280− behavior2 = hold "2" =<< events2
281−
282− deep e = leftmost [e, e]
283− leftmost2 e1 e2 = leftmost [e1, e2]
284−
285−
test/Reflex/TestPlan.hsdeleted−52
@@ -1,52 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE ScopedTypeVariables #-}
8−{-# LANGUAGE UndecidableInstances #-}
9−module Reflex.TestPlan
10− ( TestPlan(..)
11−
12− , TestCase (..)
13− , testE, testB
14− , TestE, TestB
15−
16− , planList
17−
18− ) where
19−
20−import Control.DeepSeq
21−import Control.Monad.Fix
22−import Data.Word
23−import Reflex.Class
24−
25−
26−import Prelude
27−
28−class (Reflex t, MonadHold t m, MonadFix m) => TestPlan t m where
29− -- | Speicify a plan of an input Event firing
30− -- Occurances must be in the future (i.e. Time > 0)
31− -- Initial specification is
32−
33− plan :: [(Word, a)] -> m (Event t a)
34−
35−
36−planList :: TestPlan t m => [a] -> m (Event t a)
37−planList xs = plan $ zip [1..] xs
38−
39−type TestE a = forall t m. TestPlan t m => m (Event t a)
40−type TestB a = forall t m. TestPlan t m => m (Behavior t a)
41−
42−data TestCase where
43− TestE :: (Show a, Eq a, NFData a) => TestE a -> TestCase
44− TestB :: (Show a, Eq a, NFData a) => TestB a -> TestCase
45−
46−-- Helpers to declare test cases
47−testE :: (Eq a, Show a, NFData a) => String -> TestE a -> (String, TestCase)
48−testE name test = (name, TestE test)
49−
50−testB :: (Eq a, Show a, NFData a) => String -> TestB a -> (String, TestCase)
51−testB name test = (name, TestB test)
52−
test/hlint.hs+34−11
@@ -1,16 +1,39 @@
1+module Main where
2+
3+import Control.Monad
1−import Language.Haskell.HLint3 (hlint)
4+import Language.Haskell.HLint (hlint)
5+import System.Directory
2import System.Exit (exitFailure, exitSuccess)
6import System.Exit (exitFailure, exitSuccess)
7+import System.FilePath
8+import System.FilePath.Find
3
9
4main :: IO ()
10main :: IO ()
5main = do
11main = do
6− ideas <- hlint
12+ pwd <- getCurrentDirectory
7− [ "."
13+ let runHlint f = hlint $ f:
8− , "--ignore=Redundant do"
14+ [ "--ignore=Redundant do"
9− , "--ignore=Use camelCase"
15+ , "--ignore=Use camelCase"
10− , "--ignore=Redundant $"
16+ , "--ignore=Redundant $"
17+ , "--ignore=Use &&"
11− , "--ignore=Use &&&"
18+ , "--ignore=Use &&&"
12− , "--ignore=Use const"
19+ , "--ignore=Use const"
13− , "--ignore=Use >=>"
20+ , "--ignore=Use >=>"
14− , "--ignore=Use ."
21+ , "--ignore=Use ."
22+ , "--ignore=Use unless"
23+ , "--ignore=Reduce duplication"
24+ , "--cpp-define=USE_TEMPLATE_HASKELL"
25+ , "--ignore=Use tuple-section"
26+ , "--ignore=Unused LANGUAGE pragma" -- hlint3 falsely believes that TypeOperators is not needed
27+ ]
28+ recurseInto = and <$> sequence
29+ [ fileType ==? Directory
30+ , fileName /=? ".git"
31+ ]
32+ matchFile = and <$> sequence
33+ [ extension ==? ".hs"
15− ]
34+ ]
35+ files <- find recurseInto matchFile (pwd </> "src") --TODO: Someday fix all hints in tests, etc.
36+ ideas <- fmap concat $ forM files $ \f -> do
37+ putStr $ "linting file " ++ drop (length pwd + 1) f ++ "... "
38+ runHlint f
16 if null ideas then exitSuccess else exitFailure
39 if null ideas then exitSuccess else exitFailure
test/semantics.hsdeleted−49
@@ -1,49 +0,0 @@
1−{-# LANGUAGE ConstraintKinds #-}
2−{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE GADTs #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−
8−module Main (main) where
9−
10−import Reflex.Test
11−
12−import Data.Bifunctor
13−import Data.Functor
14−import Data.List
15−import qualified Reflex.Bench.Focused as Focused
16−import qualified Reflex.Test.Micro as Micro
17−
18−import System.Environment
19−import System.Exit
20−
21−import Prelude
22−
23−matchPrefixes :: [String] -> (String -> Bool)
24−matchPrefixes [] = const True
25−matchPrefixes args = \name -> any (`isPrefixOf` name) args
26−
27−
28−main :: IO ()
29−main = do
30− args <- getArgs
31−
32− case args of
33− ["--list"] -> mapM_ putStrLn (fst <$> allTests) >> exitWith (ExitFailure 1)
34− _ -> case filter (matchPrefixes args . fst) allTests of
35− [] -> putStrLn "filter did not match any tests" >> exitWith (ExitFailure 1)
36− tests -> runTests tests
37−
38− where
39− allTests = concat
40− [ makeGroup "micro" Micro.testCases
41− , makeGroup "subscribing (100,40)" (Focused.subscribing 100 40)
42− , makeGroup "firing 1000" (Focused.firing 1000)
43− , makeGroup "merge 100" (Focused.merging 100)
44− , makeGroup "fan 50" (Focused.fans 50)
45− ]
46−
47− makeGroup name tests = first (\test -> intercalate "/" [name, test]) <$> tests
48−
49−
test/tests.hsnew file+43
@@ -0,0 +1,43 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+module Main where
3+
4+import Test.HUnit (runTestTT, (~:), assertEqual, errors, failures, test)
5+import Data.Patch ( Patch(apply) )
6+import Data.Patch.MapWithMove ( patchThatChangesMap )
7+import Data.Map as Map ( Map, fromList, singleton )
8+import Hedgehog (checkParallel, discover, Property, property, forAll, PropertyT, (===))
9+import Hedgehog.Gen as Gen ( int )
10+import Hedgehog.Range as Range ( linear )
11+import Control.Monad (replicateM)
12+import System.Exit (exitFailure, exitSuccess)
13+import Data.Sequence as Seq ( foldMapWithIndex, replicateM )
14+
15+main :: IO ()
16+main = do
17+ counts <- runTestTT $ test [
18+ "Simple Move" ~: (do
19+ let mapBefore = Map.fromList [(0,1)]
20+ mapAfter = Map.fromList [(0,0),(1,1)]
21+ patch = patchThatChangesMap mapBefore mapAfter
22+ afterPatch = apply patch mapBefore
23+ assertEqual "Patch creates the same Map" (Just mapAfter) afterPatch),
24+ "Property Checks" ~: propertyChecks
25+ ]
26+ if errors counts + failures counts == 0 then exitSuccess else exitFailure
27+
28+propertyChecks :: IO Bool
29+propertyChecks = checkParallel $$(discover)
30+
31+prop_patchThatChangesMap :: Property
32+prop_patchThatChangesMap = property $ do
33+ mapBefore <- makeRandomIntMap
34+ mapAfter <- makeRandomIntMap
35+ let patch = patchThatChangesMap mapBefore mapAfter
36+ Just mapAfter === apply patch mapBefore
37+
38+makeRandomIntMap :: Monad m => PropertyT m (Map Int Int)
39+makeRandomIntMap = do
40+ let genNum = Gen.int (Range.linear 0 100)
41+ length <- forAll genNum
42+ listOfNumbers <- forAll $ Seq.replicateM length genNum
43+ pure $ Seq.foldMapWithIndex Map.singleton listOfNumbers